Flowsizer implementation, BSD License

Hey all:

I know that occasionally there are posts about flowsizers, that is
sizers that layout elements left to right, top to bottom without
specifying any hard constraints on the number of rows or columns. I
was in need of one, and I happened to stumble upon one that is
released under the BSD license (http://pthree.org/2007/08/08/bsd-
license-explained-in-layman-terms/) and figured I would share. =)

<code>

···

#-------------------------------------------------------------------------------
# Class: FlowSizer
# Defines a horizontal or vertical flow layout sizer for wxPython
# Written by: David C. Morrill
# Date: 01/12/2006
# (c) Copyright 2006 by Enthought, Inc.
# License: BSD Style.
#-------------------------------------------------------------------------------
import wx

class FlowSizer(wx.PySizer):
    """
    A sizer which lays out component left to right top to bottom. Java
uses
    these quite heavily
    """

    def __init__(self, orient = wx.HORIZONTAL):
        '''
        Initializes the object:
        '''
        super(FlowSizer, self).__init__()
        self._orient = orient
        self._frozen = False
        self._needed_size = None

    def CalcMin(self):
        """
        Calculates the minimum size needed by the sizer.
        """
        if self._needed_size is not None:
            return self._needed_size

        horizontal = (self._orient == wx.HORIZONTAL)
        dx = dy = i = 0

        while True:
            try:
                item = self.GetItem(i)
                if item is None:
                    break
                i += 1
            except:
                break

            idx, idy = item.CalcMin()
            if horizontal:
                dy = max(dy, idy)
            else:
                dx = max(dx, idx)

        return wx.Size(dx, dy)

    def RecalcSizes(self):
        """
        Layout the contents of the sizer based on the sizer's current
size
        and position.
        """
        horizontal = (self._orient == wx.HORIZONTAL)
        x, y = self.GetPosition()
        dx, dy = self.GetSize()
        x0, y0 = x, y
        ex = x + dx
        ey = y + dy
        mdx = mdy = sdx = sdy = i = 0

        visible = True
        cur_max = 0
        while True:
            try:
                item = self.GetItem(i)
                if item is None:
                    break
                i += 1
            except:
                break

            idx, idy = item.CalcMin()
            expand = item.GetFlag() & wx.EXPAND
            if horizontal:
                if (x > x0) and ((x + idx) > ex):
                    x = x0
                    y += (mdy + sdy)
                    mdy = sdy = 0
                    if y >= ey:
                        visible = False

                cur_max = max(idy, cur_max)
                if expand:
                    idy = cur_max

                if item.IsSpacer():
                    sdy = max(sdy, idy)
                    if x == x0:
                        idx = 0
                item.SetDimension(wx.Point(x, y), wx.Size(idx, idy))
                item.Show(visible)
                x += idx
                mdy = max(mdy, idy)
            else:
                if (y > y0) and ((y + idy) > ey):
                    y = y0
                    x += (mdx + sdx)
                    mdx = sdx = 0
                    if x >= ex:
                        visible = False

                cur_max = max(idx, cur_max)
                if expand:
                    idx = cur_max

                if item.IsSpacer():
                    sdx = max(sdx, idx)
                    if y == y0:
                        idy = 0

                item.SetDimension(wx.Point(x, y), wx.Size(idx, idy))
                item.Show(visible)
                y += idy
                mdx = max(mdx, idx)

        if (not visible) and (self._needed_size is None):
            max_dx = max_dy = 0
            if horizontal:
                max_dy = max(dy, y + mdy + sdy - y0)
            else:
                max_dx = max(dx, x + mdx + sdx - x0)
            self._needed_size = wx.Size(max_dx, max_dy)
            if not self._frozen:
                self._do_parent('_freeze')
            do_later(self._do_parent, '_thaw')
        else:
            self._needed_size = None

    def _freeze(self, window):
        """
        Prevents the specified window from doing any further screen
updates.
        """
        window.Freeze()
        self._frozen = True

    def _thaw(self, window):
        """
        Lays out a specified window and then allows it to be updated
again.
        """
        window.Layout()
        window.Refresh()
        if self._frozen:
            self._frozen = False
            window.Thaw()

    def _do_parent(self, method):
        """
        Does a specified operation on the sizer's parent window.
        """
        i = 0
        while True:
            try:
                item = self.GetItem(i)
                if item is None:
                    break
                i += 1
            except:
                return

            if item.IsWindow():
                getattr(self, method)(item.GetWindow().GetParent())
                return

#-------------------------------------------------------------------------------
# Author: David C. Morrill
# Date: 05/18/2005
# (c) Copyright 2005 by Enthought, Inc.
# License: BSD Style.
#-------------------------------------------------------------------------------
class DoLaterTimer(wx.Timer):
    """
    Provides a simple function for scheduling some code to run at some
time in
    the future.
    """

    # List of currently active timers:
    active_timers = []

    def __init__(self, interval, callable, args, kw_args):
        """
        Initializes the object:
        """
        global active_timers
        wx.Timer.__init__(self)
        for timer in self.active_timers:
            if ((timer.callable == callable) and
                (timer.args == args) and
                (timer.kw_args == kw_args)):
                timer.Start(interval, True)
                return
        self.active_timers.append(self)
        self.callable = callable
        self.args = args
        self.kw_args = kw_args
        self.Start(interval, True)

    def Notify(self):
        """
        Handles the timer pop event:
        """

        global active_timers

        self.active_timers.remove(self)
        self.callable(*self.args, **self.kw_args)

def do_later(callable, *args, **kw_args):
    """
    Does something 50 milliseconds from now.
    """
    DoLaterTimer(50, callable, args, kw_args)

def do_after(interval, callable, *args, **kw_args):
    """
    Does something after some specified time interval.
    """
    DoLaterTimer(interval, callable, args, kw_args)

</code>