From 1e45f80b2273ceed882bb16016140746eb3e7d03 Mon Sep 17 00:00:00 2001 From: "Kurt B. Kaiser" Date: Fri, 26 Oct 2007 00:10:09 +0000 Subject: [PATCH] 1. Add comments to provide top-level documentation. 2. Refactor to use more descriptive names. 3. Enhance tests in main(). --- Lib/idlelib/WidgetRedirector.py | 96 +++++++++++++++++++++++---------- 1 file changed, 67 insertions(+), 29 deletions(-) diff --git a/Lib/idlelib/WidgetRedirector.py b/Lib/idlelib/WidgetRedirector.py index f941cc2b7f4..15b83307c1e 100644 --- a/Lib/idlelib/WidgetRedirector.py +++ b/Lib/idlelib/WidgetRedirector.py @@ -1,17 +1,38 @@ from Tkinter import * - class WidgetRedirector: - """Support for redirecting arbitrary widget subcommands.""" + """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 + processes the insert without calling back into Tkinter. + + Although a binding to could be made via Tkinter, what we really want + to do is to hook the Tk 'insert' operation itself. + + When a widget is instantiated, a Tcl command is created whose name is the + same as the pathname widget._w. This command is used to invoke the various + widget operations, e.g. insert (for a Text widget). We are going to hook + this command and provide a facility ('register') to intercept the widget + operation. + + In IDLE, the function being registered provides access to the top of a + Percolator chain. At the bottom of the chain is a call to the original + Tk widget operation. + + """ def __init__(self, widget): - self.dict = {} - self.widget = widget - self.tk = tk = widget.tk - w = widget._w + self._operations = {} + self.widget = widget # widget instance + self.tk = tk = widget.tk # widget's root + w = widget._w # widget's (full) Tk pathname self.orig = w + "_orig" + # Rename the Tcl command within Tcl: tk.call("rename", w, self.orig) + # Create a new Tcl command whose name is the widget's pathname, and + # whose action is to dispatch on the operation passed to the widget: tk.createcommand(w, self.dispatch) def __repr__(self): @@ -19,70 +40,87 @@ class WidgetRedirector: self.widget._w) def close(self): - for name in self.dict.keys(): - self.unregister(name) + for operation in self._operations: + self.unregister(operation) widget = self.widget; del self.widget orig = self.orig; del self.orig tk = widget.tk w = widget._w tk.deletecommand(w) + # restore the original widget Tcl command: tk.call("rename", orig, w) - def register(self, name, function): - self.dict[name] = function - setattr(self.widget, name, function) - return OriginalCommand(self, name) + def register(self, operation, function): + self._operations[operation] = function + setattr(self.widget, operation, function) + return OriginalCommand(self, operation) - def unregister(self, name): - if self.dict.has_key(name): - function = self.dict[name] - del self.dict[name] - if hasattr(self.widget, name): - delattr(self.widget, name) + def unregister(self, operation): + if operation in self._operations: + function = self._operations[operation] + del self._operations[operation] + if hasattr(self.widget, operation): + delattr(self.widget, operation) return function else: return None - def dispatch(self, cmd, *args): - m = self.dict.get(cmd) + def dispatch(self, operation, *args): + '''Callback from Tcl which runs when the widget is referenced. + + If an operation has been registered in self._operations, apply the + associated function to the args passed into Tcl. Otherwise, pass the + operation through to Tk via the original Tcl function. + + Note that if a registered function is called, the operation is not + passed through to Tk. Apply the function returned by self.register() + to *args to accomplish that. For an example, see ColorDelegator.py. + + ''' + m = self._operations.get(operation) try: if m: return m(*args) else: - return self.tk.call((self.orig, cmd) + args) + return self.tk.call((self.orig, operation) + args) except TclError: return "" class OriginalCommand: - def __init__(self, redir, name): + def __init__(self, redir, operation): self.redir = redir - self.name = name + self.operation = operation self.tk = redir.tk self.orig = redir.orig self.tk_call = self.tk.call - self.orig_and_name = (self.orig, self.name) + self.orig_and_operation = (self.orig, self.operation) def __repr__(self): - return "OriginalCommand(%r, %r)" % (self.redir, self.name) + return "OriginalCommand(%r, %r)" % (self.redir, self.operation) def __call__(self, *args): - return self.tk_call(self.orig_and_name + args) + return self.tk_call(self.orig_and_operation + args) def main(): root = Tk() + root.wm_protocol("WM_DELETE_WINDOW", root.quit) text = Text() text.pack() text.focus_set() redir = WidgetRedirector(text) - global orig_insert + global previous_tcl_fcn def my_insert(*args): print "insert", args - orig_insert(*args) - orig_insert = redir.register("insert", my_insert) + previous_tcl_fcn(*args) + previous_tcl_fcn = redir.register("insert", my_insert) root.mainloop() + redir.unregister("insert") # runs after first 'close window' + redir.close() + root.mainloop() + root.destroy() if __name__ == "__main__": main()