Commit 44ae5134 authored by Serhiy Storchaka's avatar Serhiy Storchaka

Issue #22068: Avoided reference loops with Variables and Fonts in Tkinter.

parent c82c4c34
...@@ -201,6 +201,7 @@ class Variable: ...@@ -201,6 +201,7 @@ class Variable:
Subclasses StringVar, IntVar, DoubleVar, BooleanVar are specializations Subclasses StringVar, IntVar, DoubleVar, BooleanVar are specializations
that constrain the type of the value returned from get().""" that constrain the type of the value returned from get()."""
_default = "" _default = ""
_tclCommands = None
def __init__(self, master=None, value=None, name=None): def __init__(self, master=None, value=None, name=None):
"""Construct a variable """Construct a variable
...@@ -214,7 +215,7 @@ class Variable: ...@@ -214,7 +215,7 @@ class Variable:
global _varnum global _varnum
if not master: if not master:
master = _default_root master = _default_root
self._master = master self._root = master._root()
self._tk = master.tk self._tk = master.tk
if name: if name:
self._name = name self._name = name
...@@ -227,9 +228,15 @@ class Variable: ...@@ -227,9 +228,15 @@ class Variable:
self.set(self._default) self.set(self._default)
def __del__(self): def __del__(self):
"""Unset the variable in Tcl.""" """Unset the variable in Tcl."""
if (self._tk is not None and if self._tk is None:
self._tk.getboolean(self._tk.call("info", "exists", self._name))): return
if self._tk.getboolean(self._tk.call("info", "exists", self._name)):
self._tk.globalunsetvar(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): def __str__(self):
"""Return the name of the variable in Tcl.""" """Return the name of the variable in Tcl."""
return self._name return self._name
...@@ -248,7 +255,20 @@ class Variable: ...@@ -248,7 +255,20 @@ class Variable:
Return the name of the callback. 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) self._tk.call("trace", "variable", self._name, mode, cbname)
return cbname return cbname
trace = trace_variable trace = trace_variable
...@@ -259,7 +279,11 @@ class Variable: ...@@ -259,7 +279,11 @@ class Variable:
CBNAME is the name of the callback returned from trace_variable or trace. CBNAME is the name of the callback returned from trace_variable or trace.
""" """
self._tk.call("trace", "vdelete", self._name, mode, cbname) 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): def trace_vinfo(self):
"""Return all trace callback information.""" """Return all trace callback information."""
return map(self._tk.split, self._tk.splitlist( return map(self._tk.split, self._tk.splitlist(
......
...@@ -66,9 +66,10 @@ class Font: ...@@ -66,9 +66,10 @@ class Font:
def __init__(self, root=None, font=None, name=None, exists=False, **options): def __init__(self, root=None, font=None, name=None, exists=False, **options):
if not root: if not root:
root = Tkinter._default_root root = Tkinter._default_root
tk = getattr(root, 'tk', root)
if font: if font:
# get actual settings corresponding to the given 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: else:
font = self._set(options) font = self._set(options)
if not name: if not name:
...@@ -78,20 +79,18 @@ class Font: ...@@ -78,20 +79,18 @@ class Font:
if exists: if exists:
self.delete_font = False self.delete_font = False
# confirm font exists # confirm font exists
if self.name not in root.tk.splitlist( if self.name not in tk.splitlist(tk.call("font", "names")):
root.tk.call("font", "names")):
raise Tkinter._tkinter.TclError, "named font %s does not already exist" % (self.name,) raise Tkinter._tkinter.TclError, "named font %s does not already exist" % (self.name,)
# if font config info supplied, apply it # if font config info supplied, apply it
if font: if font:
root.tk.call("font", "configure", self.name, *font) tk.call("font", "configure", self.name, *font)
else: else:
# create new font (raises TclError if the font exists) # 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 self.delete_font = True
# backlinks! self._tk = tk
self._root = root self._split = tk.splitlist
self._split = root.tk.splitlist self._call = tk.call
self._call = root.tk.call
def __str__(self): def __str__(self):
return self.name return self.name
...@@ -116,7 +115,7 @@ class Font: ...@@ -116,7 +115,7 @@ class Font:
def copy(self): def copy(self):
"Return a distinct copy of the current font" "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): def actual(self, option=None):
"Return actual font attributes" "Return actual font attributes"
......
...@@ -19,6 +19,8 @@ Core and Builtins ...@@ -19,6 +19,8 @@ Core and Builtins
Library Library
------- -------
- Issue #22068: Avoided reference loops with Variables and Fonts in Tkinter.
- Issue #21448: Changed FeedParser feed() to avoid O(N**2) behavior when - Issue #21448: Changed FeedParser feed() to avoid O(N**2) behavior when
parsing long line. Original patch by Raymond Hettinger. parsing long line. Original patch by Raymond Hettinger.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment