diff -r 66c7f30fe8c7 Lib/tkinter/__init__.py
--- a/Lib/tkinter/__init__.py Sun Aug 17 23:01:33 2014 -0500
+++ b/Lib/tkinter/__init__.py Mon Aug 18 15:37:33 2014 +0300
@@ -242,16 +242,9 @@
def get(self):
"""Return value of variable."""
return self._tk.globalgetvar(self._name)
- def trace_variable(self, mode, callback):
- """Define a trace callback for the variable.
-
- MODE is one of "r", "w", "u" for read, write, undefine.
- CALLBACK must be a function which is called when
- the variable is read, written or undefined.
-
- Return the name of the callback.
- """
- f = CallWrapper(callback, None, self).__call__
+
+ def _register(self, callback):
+ f = CallWrapper(callback, None, self._root).__call__
cbname = repr(id(f))
try:
callback = callback.__func__
@@ -265,6 +258,65 @@
if self._tclCommands is None:
self._tclCommands = []
self._tclCommands.append(cbname)
+ return cbname
+
+ def trace_add(self, mode, callback, *args):
+ """Define a trace callback for the variable.
+
+ Mode is one of "array", "read", "write", "unset", or a list or tuple
+ of such strings.
+ Callback must be a function which is called when the variable is
+ accessed or modified via the array command, read, written or unset.
+ Additional arguments are passed to the traceback call.
+
+ Return a tuple containg the name of the callback and additional
+ arguments.
+ """
+ cbname = self._register(callback)
+ self._tk.call('trace', 'add', 'variable',
+ self._name, mode, (cbname,) + args)
+ return (cbname,) + args
+
+ def trace_remove(self, mode, cbname_args):
+ """Delete the trace callback for a variable.
+
+ Mode is one of "array", "read", "write", "unset" or a list or tuple
+ of such strings. Must be same as were specified in trace_add().
+ Cbname_args must be a tuple returned from trace_add().
+ """
+ self._tk.call('trace', 'remove', 'variable',
+ self._name, mode, cbname_args)
+ cbname = cbname_args[0]
+ for m, ca in self.trace_info():
+ if ca[0] == cbname:
+ break
+ else:
+ self._tk.deletecommand(cbname)
+ try:
+ self._tclCommands.remove(cbname)
+ except ValueError:
+ pass
+
+ def trace_info(self):
+ """Return all trace callback information."""
+ splitlist = self._tk.splitlist
+ return [(splitlist(k), splitlist(v)) for k, v in map(splitlist,
+ splitlist(self._tk.call('trace', 'info', 'variable', self._name)))]
+
+ def trace_variable(self, mode, callback):
+ """Define a trace callback for the variable.
+
+ MODE is one of "r", "w", "u" for read, write, undefine.
+ CALLBACK must be a function which is called when
+ the variable is read, written or undefined.
+
+ Return the name of the callback.
+
+ This deprecated method wraps a deprecated Tcl method that will
+ likely be removed in the future. Use trace_add() instead.
+ """
+ # TODO: Add deprecation warning
+ cbname = self._register(callback)
self._tk.call("trace", "variable", self._name, mode, cbname)
return cbname
trace = trace_variable
@@ -273,15 +325,29 @@
MODE is one of "r", "w", "u" for read, write, undefine.
CBNAME is the name of the callback returned from trace_variable or trace.
+
+ This deprecated method wraps a deprecated Tcl method that will
+ likely be removed in the future. Use trace_remove() instead.
"""
+ # TODO: Add deprecation warning
self._tk.call("trace", "vdelete", self._name, mode, cbname)
- self._tk.deletecommand(cbname)
- try:
- self._tclCommands.remove(cbname)
- except ValueError:
- pass
+ cbname = self._tk.splitlist(cbname)[0]
+ for m, ca in self.trace_info():
+ if self._tk.splitlist(ca)[0] == cbname:
+ break
+ else:
+ self._tk.deletecommand(cbname)
+ try:
+ self._tclCommands.remove(cbname)
+ except ValueError:
+ pass
def trace_vinfo(self):
- """Return all trace callback information."""
+ """Return all trace callback information.
+
+ This deprecated method wraps a deprecated Tcl method that will
+ likely be removed in the future. Use trace_info() instead.
+ """
+ # TODO: Add deprecation warning
return [self._tk.split(x) for x in self._tk.splitlist(
self._tk.call("trace", "vinfo", self._name))]
def __eq__(self, other):
diff -r 66c7f30fe8c7 Lib/tkinter/test/test_tkinter/test_variables.py
--- a/Lib/tkinter/test/test_tkinter/test_variables.py Sun Aug 17 23:01:33 2014 -0500
+++ b/Lib/tkinter/test/test_tkinter/test_variables.py Mon Aug 18 15:37:33 2014 +0300
@@ -1,6 +1,6 @@
import unittest
-
-from tkinter import Variable, StringVar, IntVar, DoubleVar, BooleanVar, Tk
+import gc
+from tkinter import Variable, StringVar, IntVar, DoubleVar, BooleanVar, Tk, TclError
class Var(Variable):
@@ -86,6 +86,106 @@
v.set("value")
self.assertTrue(v.side_effect)
+ def test_trace_old(self):
+ v = Var()
+ vname = str(v)
+ trace = []
+ def read_tracer(*args):
+ trace.append(('read',) + args)
+ def write_tracer(*args):
+ trace.append(('write',) + args)
+ cb1 = v.trace_variable('r', read_tracer)
+ cb2 = v.trace_variable('wu', write_tracer)
+ self.assertEqual(sorted(v.trace_vinfo()), [('r', cb1), ('wu', cb2)])
+ self.assertEqual(trace, [])
+
+ v.set('spam')
+ self.assertEqual(trace, [('write', vname, '', 'w')])
+
+ trace = []
+ v.get()
+ self.assertEqual(trace, [('read', vname, '', 'r')])
+
+ trace = []
+ info = sorted(v.trace_vinfo())
+ v.trace_vdelete('w', cb1) # Wrong mode
+ self.assertEqual(sorted(v.trace_vinfo()), info)
+ with self.assertRaises(TclError):
+ v.trace_vdelete('r', 'spam') # Wrong command name
+ self.assertEqual(sorted(v.trace_vinfo()), info)
+ v.trace_vdelete('r', (cb1, 43)) # Wrong arguments
+ self.assertEqual(sorted(v.trace_vinfo()), info)
+ v.get()
+ self.assertEqual(trace, [('read', vname, '', 'r')])
+
+ trace = []
+ v.trace_vdelete('r', cb1)
+ self.assertEqual(v.trace_vinfo(), [('wu', cb2)])
+ v.get()
+ self.assertEqual(trace, [])
+
+ trace = []
+ del write_tracer
+ gc.collect()
+ v.set('eggs')
+ self.assertEqual(trace, [('write', vname, '', 'w')])
+
+ trace = []
+ del v
+ gc.collect()
+ self.assertEqual(trace, [('write', vname, '', 'u')])
+
+ def test_trace(self):
+ v = Var()
+ vname = str(v)
+ trace = []
+ def read_tracer(*args):
+ trace.append(('read',) + args)
+ def write_tracer(*args):
+ trace.append(('write',) + args)
+ tr1 = v.trace_add('read', read_tracer, 42)
+ tr2 = v.trace_add(['write', 'unset'], write_tracer)
+ self.assertEqual(sorted(v.trace_info()), [
+ (('read',), (tr1[0], '42')),
+ (('write', 'unset'), tr2)])
+ self.assertEqual(trace, [])
+
+ v.set('spam')
+ self.assertEqual(trace, [('write', vname, '', 'write')])
+
+ trace = []
+ v.get()
+ self.assertEqual(trace, [('read', '42', vname, '', 'read')])
+
+ trace = []
+ info = sorted(v.trace_info())
+ v.trace_remove('write', tr1) # Wrong mode
+ self.assertEqual(sorted(v.trace_info()), info)
+ with self.assertRaises(TclError):
+ v.trace_remove('read', ('spam',)) # Wrong command name
+ self.assertEqual(sorted(v.trace_info()), info)
+ v.trace_remove('read', (tr1[0], 43)) # Wrong arguments
+ self.assertEqual(sorted(v.trace_info()), info)
+ v.get()
+ self.assertEqual(trace, [('read', '42', vname, '', 'read')])
+
+ trace = []
+ v.trace_remove('read', tr1)
+ self.assertEqual(v.trace_info(), [(('write', 'unset'), tr2)])
+ v.get()
+ self.assertEqual(trace, [])
+
+ trace = []
+ del write_tracer
+ gc.collect()
+ v.set('eggs')
+ self.assertEqual(trace, [('write', vname, '', 'write')])
+
+ trace = []
+ del v
+ gc.collect()
+ self.assertEqual(trace, [('write', vname, '', 'unset')])
+
class TestStringVar(TestBase):