Home | History | Annotate | Download | only in idlelib
      1 from Tkinter import *
      2 
      3 class WidgetRedirector:
      4 
      5     """Support for redirecting arbitrary widget subcommands.
      6 
      7     Some Tk operations don't normally pass through Tkinter.  For example, if a
      8     character is inserted into a Text widget by pressing a key, a default Tk
      9     binding to the widget's 'insert' operation is activated, and the Tk library
     10     processes the insert without calling back into Tkinter.
     11 
     12     Although a binding to <Key> could be made via Tkinter, what we really want
     13     to do is to hook the Tk 'insert' operation itself.
     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.
     20 
     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 
     25     """
     26     def __init__(self, widget):
     27         self._operations = {}
     28         self.widget = widget            # widget instance
     29         self.tk = tk = widget.tk        # widget's root
     30         w = widget._w                   # widget's (full) Tk pathname
     31         self.orig = w + "_orig"
     32         # Rename the Tcl command within Tcl:
     33         tk.call("rename", w, self.orig)
     34         # Create a new Tcl command whose name is the widget's pathname, and
     35         # whose action is to dispatch on the operation passed to the widget:
     36         tk.createcommand(w, self.dispatch)
     37 
     38     def __repr__(self):
     39         return "WidgetRedirector(%s<%s>)" % (self.widget.__class__.__name__,
     40                                              self.widget._w)
     41 
     42     def close(self):
     43         for operation in list(self._operations):
     44             self.unregister(operation)
     45         widget = self.widget; del self.widget
     46         orig = self.orig; del self.orig
     47         tk = widget.tk
     48         w = widget._w
     49         tk.deletecommand(w)
     50         # restore the original widget Tcl command:
     51         tk.call("rename", orig, w)
     52 
     53     def register(self, operation, function):
     54         self._operations[operation] = function
     55         setattr(self.widget, operation, function)
     56         return OriginalCommand(self, operation)
     57 
     58     def unregister(self, operation):
     59         if operation in self._operations:
     60             function = self._operations[operation]
     61             del self._operations[operation]
     62             if hasattr(self.widget, operation):
     63                 delattr(self.widget, operation)
     64             return function
     65         else:
     66             return None
     67 
     68     def dispatch(self, operation, *args):
     69         '''Callback from Tcl which runs when the widget is referenced.
     70 
     71         If an operation has been registered in self._operations, apply the
     72         associated function to the args passed into Tcl. Otherwise, pass the
     73         operation through to Tk via the original Tcl function.
     74 
     75         Note that if a registered function is called, the operation is not
     76         passed through to Tk.  Apply the function returned by self.register()
     77         to *args to accomplish that.  For an example, see ColorDelegator.py.
     78 
     79         '''
     80         m = self._operations.get(operation)
     81         try:
     82             if m:
     83                 return m(*args)
     84             else:
     85                 return self.tk.call((self.orig, operation) + args)
     86         except TclError:
     87             return ""
     88 
     89 
     90 class OriginalCommand:
     91 
     92     def __init__(self, redir, operation):
     93         self.redir = redir
     94         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)
     99 
    100     def __repr__(self):
    101         return "OriginalCommand(%r, %r)" % (self.redir, self.operation)
    102 
    103     def __call__(self, *args):
    104         return self.tk_call(self.orig_and_operation + args)
    105 
    106 
    107 def main():
    108     root = Tk()
    109     root.wm_protocol("WM_DELETE_WINDOW", root.quit)
    110     text = Text()
    111     text.pack()
    112     text.focus_set()
    113     redir = WidgetRedirector(text)
    114     global previous_tcl_fcn
    115     def my_insert(*args):
    116         print "insert", args
    117         previous_tcl_fcn(*args)
    118     previous_tcl_fcn = redir.register("insert", my_insert)
    119     root.mainloop()
    120     redir.unregister("insert")  # runs after first 'close window'
    121     redir.close()
    122     root.mainloop()
    123     root.destroy()
    124 
    125 if __name__ == "__main__":
    126     main()
    127