diff --git a/lib/matplotlib/backends/_backend_tk.py b/lib/matplotlib/backends/_backend_tk.py index bdbbd6ad6c77..3199ff1e8083 100644 --- a/lib/matplotlib/backends/_backend_tk.py +++ b/lib/matplotlib/backends/_backend_tk.py @@ -1,4 +1,5 @@ import uuid +import weakref from contextlib import contextmanager import logging import math @@ -198,14 +199,33 @@ def __init__(self, figure=None, master=None): # event to the window containing the canvas instead. # See https://wiki.tcl-lang.org/3893 (mousewheel) for details root = self._tkcanvas.winfo_toplevel() - root.bind("", self.scroll_event_windows, "+") + + # Prevent long-lived references via tkinter callback structure GH-24820 + weakself = weakref.ref(self) + weakroot = weakref.ref(root) + + def scroll_event_windows(event): + self = weakself() + if self is None: + root = weakroot() + if root is not None: + root.unbind("", scroll_event_windows_id) + return + return self.scroll_event_windows(event) + scroll_event_windows_id = root.bind("", scroll_event_windows, "+") # Can't get destroy events by binding to _tkcanvas. Therefore, bind # to the window and filter. def filter_destroy(event): + self = weakself() + if self is None: + root = weakroot() + if root is not None: + root.unbind("", filter_destroy_id) + return if event.widget is self._tkcanvas: CloseEvent("close_event", self)._process() - root.bind("", filter_destroy, "+") + filter_destroy_id = root.bind("", filter_destroy, "+") self._tkcanvas.focus_set()