1
- from tkinter import *
1
+ from tkinter import TclError
2
2
3
3
class WidgetRedirector :
4
-
5
4
"""Support for redirecting arbitrary widget subcommands.
6
5
7
6
Some Tk operations don't normally pass through Tkinter. For example, if a
@@ -18,12 +17,22 @@ class WidgetRedirector:
18
17
this command and provide a facility ('register') to intercept the widget
19
18
operation.
20
19
21
- In IDLE, the function being registered provides access to the top of a
22
- Percolator chain . At the bottom of the chain is a call to the original
23
- Tk widget operation.
24
-
20
+ In IDLE, WidgetRedirector is used in Percolator to intercept Text
21
+ commands . The function being registered provides access to the top
22
+ of a Percolator chain. At the bottom of the chain is a call to the
23
+ original Tk widget operation.
25
24
"""
26
25
def __init__ (self , widget ):
26
+ '''Initialize attributes and setup redirection.
27
+
28
+ _operations: dict mapping operation name to new function.
29
+ widget: the widget whose tcl command is to be intercepted.
30
+ tk: widget.tk, a convenience attribute, probably not needed.
31
+ orig: new name of the original tcl command.
32
+
33
+ Since renaming to orig fails with TclError when orig already
34
+ exists, only one WidgetDirector can exist for a given widget.
35
+ '''
27
36
self ._operations = {}
28
37
self .widget = widget # widget instance
29
38
self .tk = tk = widget .tk # widget's root
@@ -40,22 +49,34 @@ def __repr__(self):
40
49
self .widget ._w )
41
50
42
51
def close (self ):
52
+ "Unregister operations and revert redirection created by .__init__."
43
53
for operation in list (self ._operations ):
44
54
self .unregister (operation )
45
- widget = self .widget ; del self .widget
46
- orig = self .orig ; del self .orig
55
+ widget = self .widget
47
56
tk = widget .tk
48
57
w = widget ._w
58
+ # Restore the original widget Tcl command.
49
59
tk .deletecommand (w )
50
- # restore the original widget Tcl command:
51
- tk .call ("rename" , orig , w )
60
+ tk .call ("rename" , self .orig , w )
61
+ del self .widget , self .tk # Should not be needed
62
+ # if instance is deleted after close, as in Percolator.
52
63
53
64
def register (self , operation , function ):
65
+ '''Return OriginalCommand(operation) after registering function.
66
+
67
+ Registration adds an instance function attribute that masks the
68
+ class instance method attribute. If a second function is
69
+ registered for the same operation, the first function is replaced.
70
+ '''
54
71
self ._operations [operation ] = function
55
72
setattr (self .widget , operation , function )
56
73
return OriginalCommand (self , operation )
57
74
58
75
def unregister (self , operation ):
76
+ '''Return the function for the operation, or None.
77
+
78
+ Deleting the instance attribute unmasks the class attribute.
79
+ '''
59
80
if operation in self ._operations :
60
81
function = self ._operations [operation ]
61
82
del self ._operations [operation ]
@@ -88,14 +109,29 @@ def dispatch(self, operation, *args):
88
109
89
110
90
111
class OriginalCommand :
112
+ '''Callable for original tk command that has been redirected.
113
+
114
+ Returned by .register; can be used in the function registered.
115
+ redir = WidgetRedirector(text)
116
+ def my_insert(*args):
117
+ print("insert", args)
118
+ original_insert(*args)
119
+ original_insert = redir.register("insert", my_insert)
120
+ '''
91
121
92
122
def __init__ (self , redir , operation ):
123
+ '''Create .tk_call and .orig_and_operation for .__call__ method.
124
+
125
+ .redir and .operation store the input args for __repr__.
126
+ .tk and .orig copy attributes of .redir (probably not needed).
127
+ '''
93
128
self .redir = redir
94
129
self .operation = operation
95
- self .tk = redir .tk
96
- self .orig = redir .orig
97
- self .tk_call = self .tk .call
98
- self .orig_and_operation = (self .orig , self .operation )
130
+ self .tk = redir .tk # redundant with self.redir
131
+ self .orig = redir .orig # redundant with self.redir
132
+ # These two could be deleted after checking recipient code.
133
+ self .tk_call = redir .tk .call
134
+ self .orig_and_operation = (redir .orig , operation )
99
135
100
136
def __repr__ (self ):
101
137
return "OriginalCommand(%r, %r)" % (self .redir , self .operation )
@@ -104,7 +140,10 @@ def __call__(self, *args):
104
140
return self .tk_call (self .orig_and_operation + args )
105
141
106
142
107
- def _widget_redirector (parent ):
143
+ def _widget_redirector (parent ): # htest #
144
+ from tkinter import Tk , Text
145
+ import re
146
+
108
147
root = Tk ()
109
148
root .title ("Test WidgetRedirector" )
110
149
width , height , x , y = list (map (int , re .split ('[x+]' , parent .geometry ())))
@@ -113,13 +152,16 @@ def _widget_redirector(parent):
113
152
text .pack ()
114
153
text .focus_set ()
115
154
redir = WidgetRedirector (text )
116
- global previous_tcl_fcn
117
155
def my_insert (* args ):
118
156
print ("insert" , args )
119
- previous_tcl_fcn (* args )
120
- previous_tcl_fcn = redir .register ("insert" , my_insert )
157
+ original_insert (* args )
158
+ original_insert = redir .register ("insert" , my_insert )
121
159
root .mainloop ()
122
160
123
161
if __name__ == "__main__" :
162
+ import unittest
163
+ ## unittest.main('idlelib.idle_test.test_widgetredir',
164
+ ## verbosity=2, exit=False)
165
+
124
166
from idlelib .idle_test .htest import run
125
167
run (_widget_redirector )
0 commit comments