I took the time to clean up the implementation of Percolator and add
some comments and doc-strings. I plan to do the same for Delegator
soon. I have found these to be very confusing and hard to understand,
although they could be a good piece of code to learn from. I hope I've
made the code more Pythonic :)

This version of Percolator inherits from Delegator (since a Percolator
"is a" Delegator, after all). I've also refactored the
Tk-Text-widget-specific stuff into a subclass, TkTextPercolator, which
is functionally equivalent to the original Percolator.

To use this in IDLE, use this file instead of Percolator.py (either
replace it or import this file instead of the original), and use the
TkTextPercolator class instead of Percolator (in EditorWindow.py).

I'm willing to post this as a patch on SF if there is any interest.

Comments are most welcome :)
- Tal
from WidgetRedirector import WidgetRedirector
from Delegator import Delegator

class Percolator(Delegator):
    """A linked list of Delegator objects, each delegating to the next.

    A Percolator is a Delegator, and therefore inherits from Delegator.

    Use 'insertfilter' to add a Delegator to the beginning of the list,
    and 'removefilter' to remove a Delegator from (anywhere in) the list.

    'setdelegate' and 'getdelegate' refer to the original delegate, to which
    the last delegator in the list delegates.

    """

    def __init__(self, delegate):
        Delegator.__init__(self)
        self.top = self.bottom = Delegator(delegate)

    # Always actually delegate to self.top
    top = property(lambda self: Delegator.getdelegate(self),
                   lambda self, top: Delegator.setdelegate(self, top))

    # setdelegate and getdelegate refer to the original delegate
    def setdelegate(self, delegate):
        self.bottom.setdelegate(delegate)

    def getdelegate(self):
        return self.bottom.getdelegate()


    def close(self):
        while self.top is not self.bottom:
            self.removefilter(top)
        self.top = None
        self.setdelegate(None)

    def insertfilter(self, filter):
        # Perhaps rename to pushfilter()?
        assert isinstance(filter, Delegator)
        assert filter.delegate is None
        filter.setdelegate(self.top)
        self.top = filter

    def removefilter(self, filter):
        # XXX Perhaps should only support popfilter()?
        assert isinstance(filter, Delegator)
        assert filter.delegate is not None
        f = self.top
        if f is filter:
            self.top = filter.getdelegate()
            filter.setdelegate(None)            
        else:
            while f.delegate is not filter:
                assert f is not self.bottom
                f.resetcache()
                f = f.delegate
            f.setdelegate(filter.getdelegate())


class TkTextPercolator(Percolator):
    """A specialized Percolator used to wrap a Tk Text widget.

    The Text widget's insert and delete methods are redirected to the top of
    the Percolator. The bottom-most Delegator has the original insert and
    delete methods.

    This uses the WidgetRedirector class which allows redirecting Tcl/Tk calls
    to Python functions/methods.

    """
    def __init__(self, text):
        Percolator.__init__(self, text)
        self.redir = WidgetRedirector(text)

        # Register functions which dynamically retrieve the methods (see below)
        self.bottom.insert = self.redir.register("insert", self.insert)
        self.bottom.delete = self.redir.register("delete", self.delete)

    def close(self):
        self.redir.close(); self.redir = None
        Percolator.close(self)

    # The 'insert' and 'delete' methods ensure dynamic resolution of
    # 'self.top.insert'. Without this, the widget's 'insert' method would be
    # redirected to whatever self.insert is delegated to during construction,
    # since self.insert is evaluated when __init__ is run. (likewise for
    # 'delete')
    def insert(self, index, chars, tags=None):
        self.top.insert(index, chars, tags)

    def delete(self, index1, index2=None):
        self.top.delete(index1, index2)


def main():
    class Tracer(Delegator):
        def __init__(self, name):
            self.name = name
            Delegator.__init__(self, None)
        def insert(self, *args):
            print self.name, ": insert", args
            self.delegate.insert(*args)
        def delete(self, *args):
            print self.name, ": delete", args
            self.delegate.delete(*args)
    from Tkinter import Tk, Text
    root = Tk()
    root.wm_protocol("WM_DELETE_WINDOW", root.quit)
    text = Text()
    text.pack()
    text.focus_set()
    p = TkTextPercolator(text)
    t1 = Tracer("t1")
    t2 = Tracer("t2")
    p.insertfilter(t1)
    p.insertfilter(t2)
    root.mainloop()
    p.removefilter(t2)
    root.mainloop()
    p.insertfilter(t2)
    p.removefilter(t1)
    root.mainloop()
    root.destroy()

if __name__ == "__main__":
    main()
_______________________________________________
IDLE-dev mailing list
[email protected]
http://mail.python.org/mailman/listinfo/idle-dev

Reply via email to