Issue #22068: Avoided reference loops with Variables and Fonts in Tkinter.
diff --git a/Lib/lib-tk/Tkinter.py b/Lib/lib-tk/Tkinter.py
index aade649..bf5bc28 100644
--- a/Lib/lib-tk/Tkinter.py
+++ b/Lib/lib-tk/Tkinter.py
@@ -201,6 +201,7 @@
Subclasses StringVar, IntVar, DoubleVar, BooleanVar are specializations
that constrain the type of the value returned from get()."""
_default = ""
+ _tclCommands = None
def __init__(self, master=None, value=None, name=None):
"""Construct a variable
@@ -214,7 +215,7 @@
global _varnum
if not master:
master = _default_root
- self._master = master
+ self._root = master._root()
self._tk = master.tk
if name:
self._name = name
@@ -227,9 +228,15 @@
self.set(self._default)
def __del__(self):
"""Unset the variable in Tcl."""
- if (self._tk is not None and
- self._tk.getboolean(self._tk.call("info", "exists", self._name))):
+ if self._tk is None:
+ return
+ if self._tk.getboolean(self._tk.call("info", "exists", self._name)):
self._tk.globalunsetvar(self._name)
+ if self._tclCommands is not None:
+ for name in self._tclCommands:
+ #print '- Tkinter: deleted command', name
+ self._tk.deletecommand(name)
+ self._tclCommands = None
def __str__(self):
"""Return the name of the variable in Tcl."""
return self._name
@@ -248,7 +255,20 @@
Return the name of the callback.
"""
- cbname = self._master._register(callback)
+ f = CallWrapper(callback, None, self._root).__call__
+ cbname = repr(id(f))
+ try:
+ callback = callback.im_func
+ except AttributeError:
+ pass
+ try:
+ cbname = cbname + callback.__name__
+ except AttributeError:
+ pass
+ self._tk.createcommand(cbname, f)
+ if self._tclCommands is None:
+ self._tclCommands = []
+ self._tclCommands.append(cbname)
self._tk.call("trace", "variable", self._name, mode, cbname)
return cbname
trace = trace_variable
@@ -259,7 +279,11 @@
CBNAME is the name of the callback returned from trace_variable or trace.
"""
self._tk.call("trace", "vdelete", self._name, mode, cbname)
- self._master.deletecommand(cbname)
+ self._tk.deletecommand(cbname)
+ try:
+ self._tclCommands.remove(cbname)
+ except ValueError:
+ pass
def trace_vinfo(self):
"""Return all trace callback information."""
return map(self._tk.split, self._tk.splitlist(
diff --git a/Lib/lib-tk/tkFont.py b/Lib/lib-tk/tkFont.py
index dfdee42..113c983 100644
--- a/Lib/lib-tk/tkFont.py
+++ b/Lib/lib-tk/tkFont.py
@@ -66,9 +66,10 @@
def __init__(self, root=None, font=None, name=None, exists=False, **options):
if not root:
root = Tkinter._default_root
+ tk = getattr(root, 'tk', root)
if font:
# get actual settings corresponding to the given font
- font = root.tk.splitlist(root.tk.call("font", "actual", font))
+ font = tk.splitlist(tk.call("font", "actual", font))
else:
font = self._set(options)
if not name:
@@ -78,20 +79,18 @@
if exists:
self.delete_font = False
# confirm font exists
- if self.name not in root.tk.splitlist(
- root.tk.call("font", "names")):
+ if self.name not in tk.splitlist(tk.call("font", "names")):
raise Tkinter._tkinter.TclError, "named font %s does not already exist" % (self.name,)
# if font config info supplied, apply it
if font:
- root.tk.call("font", "configure", self.name, *font)
+ tk.call("font", "configure", self.name, *font)
else:
# create new font (raises TclError if the font exists)
- root.tk.call("font", "create", self.name, *font)
+ tk.call("font", "create", self.name, *font)
self.delete_font = True
- # backlinks!
- self._root = root
- self._split = root.tk.splitlist
- self._call = root.tk.call
+ self._tk = tk
+ self._split = tk.splitlist
+ self._call = tk.call
def __str__(self):
return self.name
@@ -116,7 +115,7 @@
def copy(self):
"Return a distinct copy of the current font"
- return Font(self._root, **self.actual())
+ return Font(self._tk, **self.actual())
def actual(self, option=None):
"Return actual font attributes"
diff --git a/Misc/NEWS b/Misc/NEWS
index 5f731bf..937d103 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -19,6 +19,8 @@
Library
-------
+- Issue #22068: Avoided reference loops with Variables and Fonts in Tkinter.
+
- Issue #21448: Changed FeedParser feed() to avoid O(N**2) behavior when
parsing long line. Original patch by Raymond Hettinger.