Support for redirecting arbitrary widget subcommands. Some Tk operations don't normally pass through tkinter. For example, if a character is inserted into a Text widget by pressing a key, a default Tk binding to the widget's 'insert' operation is activated, and the Tk library proce
| 1 | from tkinter import TclError |
| 2 | |
| 3 | class WidgetRedirector: |
| 4 | """Support for redirecting arbitrary widget subcommands. |
| 5 | |
| 6 | Some Tk operations don't normally pass through tkinter. For example, if a |
| 7 | character is inserted into a Text widget by pressing a key, a default Tk |
| 8 | binding to the widget's 'insert' operation is activated, and the Tk library |
| 9 | processes the insert without calling back into tkinter. |
| 10 | |
| 11 | Although a binding to <Key> could be made via tkinter, what we really want |
| 12 | to do is to hook the Tk 'insert' operation itself. For one thing, we want |
| 13 | a text.insert call in idle code to have the same effect as a key press. |
| 14 | |
| 15 | When a widget is instantiated, a Tcl command is created whose name is the |
| 16 | same as the pathname widget._w. This command is used to invoke the various |
| 17 | widget operations, e.g. insert (for a Text widget). We are going to hook |
| 18 | this command and provide a facility ('register') to intercept the widget |
| 19 | operation. We will also intercept method calls on the tkinter class |
| 20 | instance that represents the tk widget. |
| 21 | |
| 22 | In IDLE, WidgetRedirector is used in Percolator to intercept Text |
| 23 | commands. The function being registered provides access to the top |
| 24 | of a Percolator chain. At the bottom of the chain is a call to the |
| 25 | original Tk widget operation. |
| 26 | """ |
| 27 | def __init__(self, widget): |
| 28 | '''Initialize attributes and setup redirection. |
| 29 | |
| 30 | _operations: dict mapping operation name to new function. |
| 31 | widget: the widget whose tcl command is to be intercepted. |
| 32 | tk: widget.tk, a convenience attribute, probably not needed. |
| 33 | orig: new name of the original tcl command. |
| 34 | |
| 35 | Since renaming to orig fails with TclError when orig already |
| 36 | exists, only one WidgetDirector can exist for a given widget. |
| 37 | ''' |
| 38 | self._operations = {} |
| 39 | self.widget = widget # widget instance |
| 40 | self.tk = tk = widget.tk # widget's root |
| 41 | w = widget._w # widget's (full) Tk pathname |
| 42 | self.orig = w + "_orig" |
| 43 | # Rename the Tcl command within Tcl: |
| 44 | tk.call("rename", w, self.orig) |
| 45 | # Create a new Tcl command whose name is the widget's pathname, and |
| 46 | # whose action is to dispatch on the operation passed to the widget: |
| 47 | tk.createcommand(w, self.dispatch) |
| 48 | |
| 49 | def __repr__(self): |
| 50 | w = self.widget |
| 51 | return f"{self.__class__.__name__,}({w.__class__.__name__}<{w._w}>)" |
| 52 | |
| 53 | def close(self): |
| 54 | "Unregister operations and revert redirection created by .__init__." |
| 55 | for operation in list(self._operations): |
| 56 | self.unregister(operation) |
| 57 | widget = self.widget |
| 58 | tk = widget.tk |
| 59 | w = widget._w |
| 60 | # Restore the original widget Tcl command. |
no outgoing calls
searching dependent graphs…