ยปCore Development>Code coverage>Lib/idlelib/redirector.py

Python code coverage for Lib/idlelib/redirector.py

#countcontent
1n/afrom tkinter import TclError
2n/a
3n/aclass WidgetRedirector:
4n/a """Support for redirecting arbitrary widget subcommands.
5n/a
6n/a Some Tk operations don't normally pass through tkinter. For example, if a
7n/a character is inserted into a Text widget by pressing a key, a default Tk
8n/a binding to the widget's 'insert' operation is activated, and the Tk library
9n/a processes the insert without calling back into tkinter.
10n/a
11n/a Although a binding to <Key> could be made via tkinter, what we really want
12n/a to do is to hook the Tk 'insert' operation itself. For one thing, we want
13n/a a text.insert call in idle code to have the same effect as a key press.
14n/a
15n/a When a widget is instantiated, a Tcl command is created whose name is the
16n/a same as the pathname widget._w. This command is used to invoke the various
17n/a widget operations, e.g. insert (for a Text widget). We are going to hook
18n/a this command and provide a facility ('register') to intercept the widget
19n/a operation. We will also intercept method calls on the tkinter class
20n/a instance that represents the tk widget.
21n/a
22n/a In IDLE, WidgetRedirector is used in Percolator to intercept Text
23n/a commands. The function being registered provides access to the top
24n/a of a Percolator chain. At the bottom of the chain is a call to the
25n/a original Tk widget operation.
26n/a """
27n/a def __init__(self, widget):
28n/a '''Initialize attributes and setup redirection.
29n/a
30n/a _operations: dict mapping operation name to new function.
31n/a widget: the widget whose tcl command is to be intercepted.
32n/a tk: widget.tk, a convenience attribute, probably not needed.
33n/a orig: new name of the original tcl command.
34n/a
35n/a Since renaming to orig fails with TclError when orig already
36n/a exists, only one WidgetDirector can exist for a given widget.
37n/a '''
38n/a self._operations = {}
39n/a self.widget = widget # widget instance
40n/a self.tk = tk = widget.tk # widget's root
41n/a w = widget._w # widget's (full) Tk pathname
42n/a self.orig = w + "_orig"
43n/a # Rename the Tcl command within Tcl:
44n/a tk.call("rename", w, self.orig)
45n/a # Create a new Tcl command whose name is the widget's pathname, and
46n/a # whose action is to dispatch on the operation passed to the widget:
47n/a tk.createcommand(w, self.dispatch)
48n/a
49n/a def __repr__(self):
50n/a return "%s(%s<%s>)" % (self.__class__.__name__,
51n/a self.widget.__class__.__name__,
52n/a self.widget._w)
53n/a
54n/a def close(self):
55n/a "Unregister operations and revert redirection created by .__init__."
56n/a for operation in list(self._operations):
57n/a self.unregister(operation)
58n/a widget = self.widget
59n/a tk = widget.tk
60n/a w = widget._w
61n/a # Restore the original widget Tcl command.
62n/a tk.deletecommand(w)
63n/a tk.call("rename", self.orig, w)
64n/a del self.widget, self.tk # Should not be needed
65n/a # if instance is deleted after close, as in Percolator.
66n/a
67n/a def register(self, operation, function):
68n/a '''Return OriginalCommand(operation) after registering function.
69n/a
70n/a Registration adds an operation: function pair to ._operations.
71n/a It also adds a widget function attribute that masks the tkinter
72n/a class instance method. Method masking operates independently
73n/a from command dispatch.
74n/a
75n/a If a second function is registered for the same operation, the
76n/a first function is replaced in both places.
77n/a '''
78n/a self._operations[operation] = function
79n/a setattr(self.widget, operation, function)
80n/a return OriginalCommand(self, operation)
81n/a
82n/a def unregister(self, operation):
83n/a '''Return the function for the operation, or None.
84n/a
85n/a Deleting the instance attribute unmasks the class attribute.
86n/a '''
87n/a if operation in self._operations:
88n/a function = self._operations[operation]
89n/a del self._operations[operation]
90n/a try:
91n/a delattr(self.widget, operation)
92n/a except AttributeError:
93n/a pass
94n/a return function
95n/a else:
96n/a return None
97n/a
98n/a def dispatch(self, operation, *args):
99n/a '''Callback from Tcl which runs when the widget is referenced.
100n/a
101n/a If an operation has been registered in self._operations, apply the
102n/a associated function to the args passed into Tcl. Otherwise, pass the
103n/a operation through to Tk via the original Tcl function.
104n/a
105n/a Note that if a registered function is called, the operation is not
106n/a passed through to Tk. Apply the function returned by self.register()
107n/a to *args to accomplish that. For an example, see colorizer.py.
108n/a
109n/a '''
110n/a m = self._operations.get(operation)
111n/a try:
112n/a if m:
113n/a return m(*args)
114n/a else:
115n/a return self.tk.call((self.orig, operation) + args)
116n/a except TclError:
117n/a return ""
118n/a
119n/a
120n/aclass OriginalCommand:
121n/a '''Callable for original tk command that has been redirected.
122n/a
123n/a Returned by .register; can be used in the function registered.
124n/a redir = WidgetRedirector(text)
125n/a def my_insert(*args):
126n/a print("insert", args)
127n/a original_insert(*args)
128n/a original_insert = redir.register("insert", my_insert)
129n/a '''
130n/a
131n/a def __init__(self, redir, operation):
132n/a '''Create .tk_call and .orig_and_operation for .__call__ method.
133n/a
134n/a .redir and .operation store the input args for __repr__.
135n/a .tk and .orig copy attributes of .redir (probably not needed).
136n/a '''
137n/a self.redir = redir
138n/a self.operation = operation
139n/a self.tk = redir.tk # redundant with self.redir
140n/a self.orig = redir.orig # redundant with self.redir
141n/a # These two could be deleted after checking recipient code.
142n/a self.tk_call = redir.tk.call
143n/a self.orig_and_operation = (redir.orig, operation)
144n/a
145n/a def __repr__(self):
146n/a return "%s(%r, %r)" % (self.__class__.__name__,
147n/a self.redir, self.operation)
148n/a
149n/a def __call__(self, *args):
150n/a return self.tk_call(self.orig_and_operation + args)
151n/a
152n/a
153n/adef _widget_redirector(parent): # htest #
154n/a from tkinter import Toplevel, Text
155n/a
156n/a top = Toplevel(parent)
157n/a top.title("Test WidgetRedirector")
158n/a x, y = map(int, parent.geometry().split('+')[1:])
159n/a top.geometry("+%d+%d" % (x, y + 175))
160n/a text = Text(top)
161n/a text.pack()
162n/a text.focus_set()
163n/a redir = WidgetRedirector(text)
164n/a def my_insert(*args):
165n/a print("insert", args)
166n/a original_insert(*args)
167n/a original_insert = redir.register("insert", my_insert)
168n/a
169n/aif __name__ == "__main__":
170n/a import unittest
171n/a unittest.main('idlelib.idle_test.test_redirector',
172n/a verbosity=2, exit=False)
173n/a
174n/a from idlelib.idle_test.htest import run
175n/a run(_widget_redirector)