mirror of
https://github.com/python/cpython.git
synced 2025-08-03 16:39:00 +00:00
Issue #22115: Added methods trace_add, trace_remove and trace_info in the
tkinter.Variable class. They replace old methods trace_variable, trace, trace_vdelete and trace_vinfo that use obsolete Tcl commands and might not work in future versions of Tcl.
This commit is contained in:
parent
523ccd135c
commit
8122174af1
5 changed files with 219 additions and 35 deletions
|
@ -343,16 +343,9 @@ class Variable:
|
|||
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__
|
||||
|
@ -366,25 +359,99 @@ class Variable:
|
|||
if self._tclCommands is None:
|
||||
self._tclCommands = []
|
||||
self._tclCommands.append(cbname)
|
||||
return cbname
|
||||
|
||||
def trace_add(self, mode, callback):
|
||||
"""Define a trace callback for the variable.
|
||||
|
||||
Mode is one of "read", "write", "unset", or a list or tuple of
|
||||
such strings.
|
||||
Callback must be a function which is called when the variable is
|
||||
read, written or unset.
|
||||
|
||||
Return the name of the callback.
|
||||
"""
|
||||
cbname = self._register(callback)
|
||||
self._tk.call('trace', 'add', 'variable',
|
||||
self._name, mode, (cbname,))
|
||||
return cbname
|
||||
|
||||
def trace_remove(self, mode, cbname):
|
||||
"""Delete the trace callback for a variable.
|
||||
|
||||
Mode is one of "read", "write", "unset" or a list or tuple of
|
||||
such strings. Must be same as were specified in trace_add().
|
||||
cbname is the name of the callback returned from trace_add().
|
||||
"""
|
||||
self._tk.call('trace', 'remove', 'variable',
|
||||
self._name, mode, cbname)
|
||||
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_info(self):
|
||||
"""Return all trace callback information."""
|
||||
splitlist = self._tk.splitlist
|
||||
return [(splitlist(k), 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
|
||||
|
||||
def trace_vdelete(self, mode, cbname):
|
||||
"""Delete the trace callback for a variable.
|
||||
|
||||
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.splitlist(x) for x in self._tk.splitlist(
|
||||
self._tk.call("trace", "vinfo", self._name))]
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Comparison for equality (==).
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import unittest
|
||||
|
||||
import gc
|
||||
from tkinter import (Variable, StringVar, IntVar, DoubleVar, BooleanVar, Tcl,
|
||||
TclError)
|
||||
|
||||
|
@ -87,6 +87,105 @@ class TestVariable(TestBase):
|
|||
v.set("value")
|
||||
self.assertTrue(v.side_effect)
|
||||
|
||||
def test_trace_old(self):
|
||||
# Old interface
|
||||
v = Var(self.root)
|
||||
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(self.root)
|
||||
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)
|
||||
tr2 = v.trace_add(['write', 'unset'], write_tracer)
|
||||
self.assertEqual(sorted(v.trace_info()), [
|
||||
(('read',), tr1),
|
||||
(('write', 'unset'), tr2)])
|
||||
self.assertEqual(trace, [])
|
||||
|
||||
v.set('spam')
|
||||
self.assertEqual(trace, [('write', vname, '', 'write')])
|
||||
|
||||
trace = []
|
||||
v.get()
|
||||
self.assertEqual(trace, [('read', 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.get()
|
||||
self.assertEqual(trace, [('read', 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):
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue