From 3c110e72b9d1389f5d7c1930343c5c64dd8027ca Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Sun, 2 May 2021 23:39:17 +0200 Subject: [PATCH] Backport PR #19959: Bugfix Tk start_event_loop --- lib/matplotlib/backends/_backend_tk.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/backends/_backend_tk.py b/lib/matplotlib/backends/_backend_tk.py index c13546ddca04..adf0a9dfc698 100644 --- a/lib/matplotlib/backends/_backend_tk.py +++ b/lib/matplotlib/backends/_backend_tk.py @@ -166,6 +166,7 @@ def __init__(self, figure=None, master=None, resize_callback=None): super().__init__(figure) self._idle = True self._idle_callback = None + self._event_loop_id = None w, h = self.figure.bbox.size.astype(int) self._tkcanvas = tk.Canvas( master=master, background="white", @@ -374,11 +375,20 @@ def flush_events(self): def start_event_loop(self, timeout=0): # docstring inherited if timeout > 0: - self._master.after(int(1000*timeout), self.stop_event_loop) + milliseconds = int(1000 * timeout) + if milliseconds > 0: + self._event_loop_id = self._tkcanvas.after( + milliseconds, self.stop_event_loop) + else: + self._event_loop_id = self._tkcanvas.after_idle( + self.stop_event_loop) self._master.mainloop() def stop_event_loop(self): # docstring inherited + if self._event_loop_id: + self._master.after_cancel(self._event_loop_id) + self._event_loop_id = None self._master.quit() @@ -454,6 +464,8 @@ def destroy(*args): def destroy(self, *args): if self.canvas._idle_callback: self.canvas._tkcanvas.after_cancel(self.canvas._idle_callback) + if self.canvas._event_loop_id: + self.canvas._tkcanvas.after_cancel(self.canvas._event_loop_id) # NOTE: events need to be flushed before issuing destroy (GH #9956), # however, self.window.update() can break user code. This is the @@ -466,7 +478,8 @@ def delayed_destroy(): if self._owns_mainloop and not Gcf.get_num_fig_managers(): self.window.quit() - self.window.after_idle(delayed_destroy) + # "after idle after 0" avoids Tcl error/race (GH #19940) + self.window.after_idle(self.window.after, 0, delayed_destroy) def get_window_title(self): return self.window.wm_title()