"""
Convenience layer for Tkinter.

Allows window specifications to be created with far less,
and much more readable code, than doing it by hand.

Contains classes tRow and tCol, for building rows and columns
of widgets, and building these into a live frame.

Example:

    from Tkinter import *
    from tVector import *
    
    class mywin:
        def __init__(self, parent):
            self.layout = tCol(tW(Label, 'L1',
                                  text="Window Heading",
                                  font="helvetica 16 bold"),
                               tW(Checkbutton, 'Chk1',
                                  text="A checkbutton"),
                               tRow(tW(Button, 'but1',
                                       text="Button 1",
                                       command=self.on_button1),
                                    tW(Button, 'but2',
                                       text="Button 2",
                                       command=self.on_button2),
                                    tW(Button, 'but3',
                                       text="Button 3",
                                       command=self.on_button3)),
                               tRow(tW(Entry, 'fld1',
                                       text="field 1"),
                                    tW(Entry, 'fld2',
                                       text="field 2")))
            self.layout.tBuild(parent)
    
        def on_button1(self):
            print "clicked button1"
            self.layout.wid('but1', text="New Button1 label")
    
        def on_button2(self):
            print "clicked button2"
            self.layout.wid('but1', text='Button 1')
    
        def on_button3(self):
            print "clicked button3"
    
        def mainloop(self):
            self.layout.mainloop()
        
    root = Tk()
    app = mywin(root)
    app.mainloop()

"""

import Tkinter
from Tkinter import *


class tVector(Frame):
    """
    Tkinter vectors.

    A helper class for the easy creation and use of Tkinter layouts.

    (don't use this class directly - you should use the derived 'tRow'
    and 'tVer' classes instead)

    For a working example, see this module's docstring
    """
    def __init__(self, direction, *widgets, **kwds):
        """
        Creates an array of widgets (or widget vectors)
        
        Best not to use this class directly - use one of the derived
        classes 'HOR' or 'VER' instead.
        """
        if direction == 'right':
            self.packside = LEFT
        else:
            self.packside = TOP

        self.kwds = kwds
        self.widlist = widgets
        self.wids = {} # populate this during build

    def tBuild(self, parent):
        """
        Recursively descend through the widgets list, and construct/pack
        the window structure
        """
        self.wids = {}

        # construct Frame for this vector
        Frame.__init__(self, parent, self.kwds)
        self.pack(side=self.packside, fill=BOTH, expand=1)

        for wid in self.widlist:
            if isinstance(wid, tVector):
                wname = None
                wref = wid.tBuild(self)
                self.wids.update(wid.wids)
            else:
                wcls = wid['class']
                wname = wid['name']
                wargs = wid['args']
                wkwds = wid['kwds']

                wref = wcls(self, *wargs, **wkwds)
                    
            wref.pack(side=self.packside, fill=BOTH, expand=1)

            if wname:
                self.wids[wname] = wref

        return self

    def wid(self, widname=None, **kwds):
        """
        Versatile method.

        Looks up component widgets by name. If any keywords are supplied,
        the widget's config() method will be called with these keywords.

        3 ways of using:
         - with no arguments at all, returns a list of widget names
         - with just a widget name argument, returns a ref to that widget
         - with widget name and some keywords, calls config() on that widget
           and passes the given keywords.

        Examples:

          # get a list of widgets for vector 'mywin'
          widgets = mywin.wid()

          # get a handle to the widget 'label1'
          mylabel = mywin.wid('label1')

          # change the text of 'label1'
          mywin.wid('label1', text="Different text for label1")

        """
        if not widname:
            return self.wids.keys()
        else:
            try:
                wid = self.wids[widname]
                if len(kwds) > 0:
                    wid.config(**kwds)
                else:
                    return self.wids[widname]
            except:
                raise AttributeError

class tRow(tVector):
    """
    Creates a horizontal vector of widgets.

    For usage info, refer to tVector docstring
    """
    def __init__(self, *widgets, **kwds):
        """
        Constructor for tRow - a horizontal vector of widgets

        Arguments:
         - widget1
         - widget2
         - ...

        Each widget should be either be something returned from tW,
        or a tRow or tCol instance.

        For an example, refer to docstring for tVector class
        """
        tVector.__init__(self, 'right', *widgets, **kwds)

class tCol(tVector):
    """
    Creates a vertical column of widgets.

    For usage info, refer to tVector docstring
    """
    def __init__(self, *widgets, **kwds):
        """
        Constructor for tRow - a vertical vector of widgets

        Arguments:
         - widget1
         - widget2
         - ...

        Each widget should be either be something returned from tW,
        or a tRow or tCol instance.

        For an example, refer to docstring for tVector class
        """
        tVector.__init__(self, 'down', *widgets, **kwds)


def tW(widclass, widname, *args, **kwds):
    """
    Helper function for tRow and tCol classes.
    
    Generates a widget entry and translates it into a dict that can be
    fed straight to the tRow and tCol constructors.

    Refer to the tVector docstring for more info.

    Arguments:
     - widclass - a widget class object that behaves like normal tk widgets
     - widname - name to give to this widget (accessible later via
       tRow.wid and tCol.wid methods)
     - extra arguments are passed to the widget constructor
    Keywords:
     - anything you give gets passed straight to widget constructor

    Example:

       tW(Tkinter.Button, 'mybutton1', text="Button 1")
    """
                
    return {'name':widname,
            'class':widclass,
            'args':args,
            'kwds':kwds}
