Closed
Description
I have some code that continuously updates an MPL plot, and used fig.canvas.start_event_loop(0.0001)
to listen for keyboard input. As of MPL 3.4, this code is broken in two ways:
- Commit c53570d converts the timeout to int milliseconds. This rounds down 0.0001 to 0 and fails to display the window at all. I might suggest
math.ceil(1000*timeout)
instead ofint(1000*timeout)
? - After changing 0.0001 to 0.001 in my code, Commit 630e806 causes MPL to throw an error
invalid command name "140668474408832stop_event_loop" while executing "140668474408832stop_event_loop" ("after" script)
when closing the window before pausing the loop. Expected behavior would be for it to simply terminate the loop. It's a harmless error in the minimal code below (you can still launch new runs), but my full code updates using anasyncio
event loop, which this behavior fails to exit, so this error prevents launching new runs in that case (asyncio.run() cannot be called from a running event loop
). Not sure how to fix this one, but I'll note that usingfig.canvas.flush_events()
in my code instead ofstart_event_loop
produces the desired behavior.
Not sure if #2 is worth fixing given that flush_events()
restores the desired behavior, but @richardsheridan suggested I post an issue in my comment on that commit. Minimal example below. Launch an MPL window and then close it before pausing to trigger the error:
from random import randint
import matplotlib, matplotlib.pyplot as plt
from tkinter import *
class Visual:
running = False
#Construct a launch button
def __init__(self):
self.parent = Tk()
self.runButton = Button(self.parent, text='Run', command=self.launchVisual, padx=10, pady=10)
self.runButton.pack(fill="x", side=TOP)
def start(self):
self.runButton['text'] = 'Pause'
self.runButton['command'] = self.stop
self.running = True
while self.running:
self.data.append(randint(0,100))
self.line.set_ydata(self.data)
self.line.set_xdata(range(len(self.data)))
self.axes.relim()
self.axes.autoscale_view(tight=False)
if self.fig.stale: self.fig.canvas.draw_idle()
#This rounds down to 0 and doesn't draw the window at all
#self.fig.canvas.start_event_loop(0.0001)
#This throws an error and breaks asyncio if you close the window before pausing
self.fig.canvas.start_event_loop(0.001)
#This works
#self.fig.canvas.flush_events()
def stop(self, *args):
self.running = False
self.runButton['text'] = 'Run'
self.runButton['command'] = self.start
def terminate(self, evt=False):
self.running = False
self.runButton['text'] = 'New Model'
self.runButton['command'] = self.launchVisual
def launchVisual(self):
matplotlib.use('TkAgg')
self.fig, self.axes = plt.subplots()
self.fig.canvas.mpl_connect('close_event', self.terminate)
#Keyboard input
def pause(event):
if event.key==' ' and event.canvas is self.fig.canvas:
if self.running: self.stop()
else: self.start()
self.fig.canvas.mpl_connect('key_press_event', pause)
self.data = []
self.line, = self.axes.plot(self.data, color='#330099')
self.fig.canvas.draw_idle()
plt.show(block=False)
self.start()
viz = Visual()
viz.parent.mainloop()