diff --git a/lib/matplotlib/backends/_backend_tk.py b/lib/matplotlib/backends/_backend_tk.py index f8516584187c..443e2b1e5e63 100644 --- a/lib/matplotlib/backends/_backend_tk.py +++ b/lib/matplotlib/backends/_backend_tk.py @@ -209,7 +209,6 @@ def __init__(self, figure, master=None, resize_callback=None): # to the window and filter. def filter_destroy(event): if event.widget is self._tkcanvas: - self._master.update_idletasks() self.close_event() root.bind("", filter_destroy, "+") @@ -233,7 +232,6 @@ def resize(self, event): self._tkcanvas.create_image( int(width / 2), int(height / 2), image=self._tkphoto) self.resize_event() - self.draw() def draw_idle(self): # docstring inherited @@ -383,6 +381,16 @@ def flush_events(self): # docstring inherited self._master.update() + def start_event_loop(self, timeout=0): + # docstring inherited + if timeout > 0: + self._master.after(int(1000*timeout), self.stop_event_loop) + self._master.mainloop() + + def stop_event_loop(self): + # docstring inherited + self._master.quit() + class FigureManagerTk(FigureManagerBase): """ @@ -527,10 +535,6 @@ def __init__(self, canvas, window, *, pack_toolbar=True): if pack_toolbar: self.pack(side=tk.BOTTOM, fill=tk.X) - def destroy(self, *args): - del self.message - tk.Frame.destroy(self, *args) - def set_message(self, s): self.message.set(s) @@ -554,8 +558,6 @@ def set_cursor(self, cursor): window.configure(cursor=cursord[cursor]) except tkinter.TclError: pass - else: - window.update_idletasks() def _Button(self, text, image_file, toggle, command): image = (tk.PhotoImage(master=self, file=image_file) diff --git a/lib/matplotlib/backends/backend_tkagg.py b/lib/matplotlib/backends/backend_tkagg.py index 1cb38b7559ed..484d5a40edbe 100644 --- a/lib/matplotlib/backends/backend_tkagg.py +++ b/lib/matplotlib/backends/backend_tkagg.py @@ -8,12 +8,10 @@ class FigureCanvasTkAgg(FigureCanvasAgg, FigureCanvasTk): def draw(self): super().draw() _backend_tk.blit(self._tkphoto, self.renderer._renderer, (0, 1, 2, 3)) - self._master.update_idletasks() def blit(self, bbox=None): _backend_tk.blit( self._tkphoto, self.renderer._renderer, (0, 1, 2, 3), bbox=bbox) - self._master.update_idletasks() @_BackendTk.export diff --git a/lib/matplotlib/backends/backend_tkcairo.py b/lib/matplotlib/backends/backend_tkcairo.py index 99204f8d5287..a81fd0d92bb8 100644 --- a/lib/matplotlib/backends/backend_tkcairo.py +++ b/lib/matplotlib/backends/backend_tkcairo.py @@ -23,7 +23,6 @@ def draw(self): _backend_tk.blit( self._tkphoto, buf, (2, 1, 0, 3) if sys.byteorder == "little" else (1, 2, 3, 0)) - self._master.update_idletasks() @_BackendTk.export diff --git a/lib/matplotlib/tests/test_backends_interactive.py b/lib/matplotlib/tests/test_backends_interactive.py index 5bd06baa8b26..27b8793b5597 100644 --- a/lib/matplotlib/tests/test_backends_interactive.py +++ b/lib/matplotlib/tests/test_backends_interactive.py @@ -194,3 +194,32 @@ def test_webagg(): conn.close() proc.send_signal(signal.SIGINT) assert proc.wait(timeout=_test_timeout) == 0 + + +@pytest.mark.backend('TkAgg', skip_on_importerror=True) +def test_never_update(monkeypatch, capsys): + import tkinter + monkeypatch.delattr(tkinter.Misc, 'update') + monkeypatch.delattr(tkinter.Misc, 'update_idletasks') + + import matplotlib.pyplot as plt + fig = plt.figure() + plt.show(block=False) + + # regression test on FigureCanvasTkAgg + plt.draw() + # regression test on NavigationToolbar2Tk + fig.canvas.toolbar.configure_subplots() + + # check for update() or update_idletasks() in the event queue + # functionally equivalent to tkinter.Misc.update + # must pause >= 1 ms to process tcl idle events plus + # extra time to avoid flaky tests on slow systems + plt.pause(0.1) + + # regression test on FigureCanvasTk filter_destroy callback + plt.close(fig) + + # test framework doesn't see tkinter callback exceptions normally + # see tkinter.Misc.report_callback_exception + assert "Exception in Tkinter callback" not in capsys.readouterr().err