4. Design issues
Upto here, everything was just more or less copying the orignal
TCL/Tk functionality in Python. However, where TCL/Tk code stays relatively compact,
with Pithon I now have a
script that is about the same number of lines
but implements only a very small part of the functionality. This is a strategy that is
not sustainable.
4.1. Splitting the GUI
In the TCL/Tk version it was one simple GUi; the tree-structure that the GUI has
is natural to that language.
In Python, it is not. So, in order to get a better and more readable code, we
have to create objects that provide a more high-level view of the GUI, instead
of the low-level Tk components.
4.1.1. Entryfields
As a first, I have a number of entry fields that are preceeded by a label that
tells what information should be in the entry field.

So, the combination of a label with an entry field is a good candidate for
an object.
class entryfield(object): def __init__(self,parent,label='text'): self.fieldframe=tk.Frame (parent) self.fieldframe.pack(side=tk.TOP) self.fieldname=tk.Label(self.fieldframe, text=label ,width=10,anchor='w') self.fieldname.pack(side=tk.LEFT) self.fieldvalue=tk.Entry(self.fieldframe, width=50) self.fieldvalue.pack(side=tk.RIGHT) def set(self,value='text'): self.fieldvalue.delete(0,tk.END) self.fieldvalue.insert(0,value) def get(self): return(self.fieldvalue.get())
And with that object, creating the part of the GUI in the picture above becomes:
self.fieldnumber=entryfield(self.fieldframe,'Number') self.fielddir=entryfield(self.fieldframe,'Directory') self.fieldfilename=entryfield(self.fieldframe,'Filename') self.fieldyear=entryfield(self.fieldframe,'Year') self.fieldmonth=entryfield(self.fieldframe,'Month') self.fielddescr=entryfield(self.fieldframe,'Description')
That is clearly more readable than the endless repeats of the tk.Frame, tk.Label, tk.Entry
and their packs.
You will notice, that their are a number of assumptions that make this object rather specific
for my GUI. The size of the entry fields and labels are fixed, packing is always TOP etcetera.
This makes the entryfield object good for this GUI, but perhaps a bit less re-usable.
4.1.2. Multilist
The object of the next object is to create a more reusable object.
The problem is that we have multiple list boxes that must scroll synchronously
and with a scrollbar. This functionality existed in the TCL version and in the
one-big-gui version, so the main problem is to create a more generally usable
object.
class multilist (object) : def __init__ (self,parent,columns=1,height=50,width=10) : self.listframe=tk.Frame (parent) self.listframe.pack() # The listframe contains 4 lists and a scrollbar which are synchronized self.sb = tk.Scrollbar(self.listframe, orient='vertical') self.cols=[] self.select=[] for i in xrange(columns): self.listnr=tk.Listbox (self.listframe, yscrollcommand=self.yscrollnr) self.listnr.config(height=height,width=width) self.listnr.bind('<<ListboxSelect>>',self.selectlist) self.listnr.pack(side=tk.LEFT, expand=True ) self.cols.append(self.listnr) self.sb.config(command=self.yview) self.sb.pack(side=tk.RIGHT, fill='y') def width(self,column=1,width=10): print "set column ",column-1,' to ',width self.cols[column-1].config(width=width) def yscrollnr(self, *args): for i in xrange(len(self.cols)): self.cols[i].yview_moveto(args[0]) self.sb.set(*args) def yview(self, *args): for i in xrange(len(self.cols)): self.cols[i].yview(*args) def subscribe (self,function): self.select.append(function) def selectlist(self,event): widget = event.widget sel=widget.curselection() if sel != (): for f in self.select: f(sel) def clear(self): for i in xrange(len(self.cols)): self.cols[i].delete(0,tk.END) def add(self, *args): i=0 for val in args: if i < len(self.cols): self.cols[i].insert(tk.END,val) i=i+1 else: print "No column for $val"
Their are still a number of things that make this object
a bit specific, but it should be clear that there are a number of choices to
generalize its use.
First of all, the size (width and height) and the number of columns are
part of the call to
__init__.
It is also possible to adjust the column width on a per-column basis. Second,
the clear and add methods are used to manipulate the listbox contents.
Perhaps more interesting is the callback for a selection in the list. Instead
of calling a specific function,
external functions/objects/programs can subscribe to the select in the
listbox. So multiple functions can be called, just by subscribing to this event.