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 key could be made via tkinter what we really want to do is to hook the tk insert operation itself for one thing we want a text insert call in idle code to have the same effect as a key press 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 we will also intercept method calls on the tkinter class instance that represents the tk widget in idle widgetredirector is used in percolator to intercept text commands 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 initialize attributes and setup redirection operations dict mapping operation name to new function widget the widget whose tcl command is to be intercepted tk widget tk a convenience attribute probably not needed orig new name of the original tcl command since renaming to orig fails with tclerror when orig already exists only one widgetdirector can exist for a given widget rename the tcl command within tcl 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 restore the original widget tcl command if instance is deleted after close as in percolator return originalcommandoperation after registering function registration adds an operation function pair to operations it also adds a widget function attribute that masks the tkinter class instance method method masking operates independently from command dispatch if a second function is registered for the same operation the first function is replaced in both places return the function for the operation or none deleting the instance attribute unmasks the class attribute 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 colorizer py callable for original tk command that has been redirected returned by register can be used in the function registered redir widgetredirectortext def myinsertargs printinsert args originalinsertargs originalinsert redir registerinsert myinsert create tkcall and origandoperation for call method redir and operation store the input args for repr tk and orig copy attributes of redir probably not needed these two could be deleted after checking recipient code 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 key could be made via tkinter what we really want to do is to hook the tk insert operation itself for one thing we want a text insert call in idle code to have the same effect as a key press 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 we will also intercept method calls on the tkinter class instance that represents the tk widget in idle widgetredirector is used in percolator to intercept text commands 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 initialize attributes and setup redirection _operations dict mapping operation name to new function widget the widget whose tcl command is to be intercepted tk widget tk a convenience attribute probably not needed orig new name of the original tcl command since renaming to orig fails with tclerror when orig already exists only one widgetdirector can exist for a given widget widget instance widget s root widget s full tk pathname rename the tcl command within tcl 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 restore the original widget tcl command should not be needed if instance is deleted after close as in percolator return originalcommand operation after registering function registration adds an operation function pair to _operations it also adds a widget function attribute that masks the tkinter class instance method method masking operates independently from command dispatch if a second function is registered for the same operation the first function is replaced in both places return the function for the operation or none deleting the instance attribute unmasks the class attribute 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 colorizer py callable for original tk command that has been redirected returned by register can be used in the function registered redir widgetredirector text def my_insert args print insert args original_insert args original_insert redir register insert my_insert create tk_call and orig_and_operation for __call__ method redir and operation store the input args for __repr__ tk and orig copy attributes of redir probably not needed redundant with self redir redundant with self redir these two could be deleted after checking recipient code htest
from tkinter import TclError

class WidgetRedirector:
    
    def __init__(self, widget):
        
        self._operations = {}
        self.widget = widget            
        self.tk = tk = widget.tk        
        w = widget._w                   
        self.orig = w + "_orig"
        
        tk.call("rename", w, self.orig)
        
        
        tk.createcommand(w, self.dispatch)

    def __repr__(self):
        w = self.widget
        return f"{self.__class__.__name__,}({w.__class__.__name__}<{w._w}>)"

    def close(self):
        "Unregister operations and revert redirection created by .__init__."
        for operation in list(self._operations):
            self.unregister(operation)
        widget = self.widget
        tk = widget.tk
        w = widget._w
        
        tk.deletecommand(w)
        tk.call("rename", self.orig, w)
        del self.widget, self.tk  
        

    def register(self, operation, function):
        
        self._operations[operation] = function
        setattr(self.widget, operation, function)
        return OriginalCommand(self, operation)

    def unregister(self, operation):
        
        if operation in self._operations:
            function = self._operations[operation]
            del self._operations[operation]
            try:
                delattr(self.widget, operation)
            except AttributeError:
                pass
            return function
        else:
            return None

    def dispatch(self, operation, *args):
        
        m = self._operations.get(operation)
        try:
            if m:
                return m(*args)
            else:
                return self.tk.call((self.orig, operation) + args)
        except TclError:
            return ""


class OriginalCommand:
    

    def __init__(self, redir, operation):
        
        self.redir = redir
        self.operation = operation
        self.tk = redir.tk  
        self.orig = redir.orig  
        
        self.tk_call = redir.tk.call
        self.orig_and_operation = (redir.orig, operation)

    def __repr__(self):
        return f"{self.__class__.__name__,}({self.redir!r}, {self.operation!r})"

    def __call__(self, *args):
        return self.tk_call(self.orig_and_operation + args)


def _widget_redirector(parent):  
    from tkinter import Toplevel, Text

    top = Toplevel(parent)
    top.title("Test WidgetRedirector")
    x, y = map(int, parent.geometry().split('+')[1:])
    top.geometry("+%d+%d" % (x, y + 175))
    text = Text(top)
    text.pack()
    text.focus_set()
    redir = WidgetRedirector(text)
    def my_insert(*args):
        print("insert", args)
        original_insert(*args)
    original_insert = redir.register("insert", my_insert)

if __name__ == "__main__":
    from unittest import main
    main('idlelib.idle_test.test_redirector', verbosity=2, exit=False)

    from idlelib.idle_test.htest import run
    run(_widget_redirector)