[aricb] Too often I find that when I write Tk code to do anything non-trivial, I end up with a sprawling disaster that is a pain to modify or even understand six months later. As I'm sure other programmers have encountered this problem, I searched for some guidelines on the Wiki and on the web. I'll share what I found below. However, I came to the conclusion that much more could (and should) be said on this topic. Here's what I've found so far (please add to this list): Tom Tromey's style guide [http://cafe.colorado.edu/~tromey/tcl-style-guide.html] has an excellent section on Tk. By contrast, Ray Johnson's [Tcl style guide] [http://www.tcl.tk/doc/styleGuide.pdf], which has become the de facto standard for Tcl, doesn't address Tk specifically. A handful of wiki pages address this topic: * [Design patterns in Tcl] * [Writing Tk programs so that the user can do simple custimizations without modifying the application] * [Writing Tk programs so that the user can extend or interact with them without modifying the application] * [Tips for writing quality software] ---- Here's what little I currently do to make my code more readable. Please share any reactions to these: * In all but the most trivial scripts, I create new toplevels instead of using . * I try to combine geometry management with widget creation when possible, i.e. grid [text .mytoplevel.text] -row 0 -column 0 However, when I have to specify lots of options for the widget and/or the geometry manager, I end up with long lines which IMHO look bad even if I break them up with backslashes. * I try to break things up into small procs, where (for example) one proc will create a frame and its children and another proc will create bindings for those widgets. ---- [PWQ] 12 Aug 2003 I use a table driven approach to gui creation. As an example set widgetlist { {button $par.b -command xx} {entry $par.e -width 6}} foreach w $widgetlist { pack [eval $w] -anc nw } ([Peter Lewerin] did minor edit to make it executable.) The command as listed in the list (which can be spread out over multiple lines) become much easier to read. I have a system that extends this concept but is to complex to outline here, here is an example screen as an example (Stored in a file/database etc is the gui definition ): Options { *LogoEntry*Entry*font {Times 24} *LogoPanel*Canvas*width 600 *LogoPanel*Canvas*height 400 *LogoMenu*Button*ipadx 0 *LogoMenu*Button*padX 2 *LogoMenu*Button*relief solid } Form Logo { Script { namespace eval Logo {set __create 1.0} } V3S#LogoPanel { H#LogoMenu { V3s { H3r { PackOptions {-anc w -padx 1} H1s { PackOptions {-anc w -padx 1} T { } T {Test:} B {{Clear} {::Logo::test:clear} } B {{Check} {::Logo::test:check} } B {{Load} {::Logo::test:load} } B {{Save} {::Logo::test:save} } } Sb/Lv {{{-orient v -command {!Logo yview} -width 10 }}} {-fill y} Lb/Logo:Logo::data(_cmds) {-font {{{Times 18 bold}}} -width 0 {{ -yscrollc {!Lv set}}} } {-fill y -expand 0} V+b { E/Vars:Logo::data(_vars) {0} {-fill x} H+b { T { } T {Programme: } B {{Save} {turtleSave [!Prog get 0 end] [!Vars get]} } B {{Load} {turtleLoad !Prog}} ........ Notes: '''H''' means arrange horizontally '''V''' means vertically. The # defines the class. The second item in each list are short cuts for the most common widget option. Ie '''B {{Save} -justify c}''' equals ''button -text Save -justify c.'' The third list item are the manager options (ie pack). /xx associates a variable with the widget. :xx defines a reference to the widget that can be used in other widget definitions Names above are abbreviations (is B is button, Sb scrollbar, Lb listbox etc. ---- [Bryan Oakley] 12-Aug-2003 I keep widget creation and geometry management separate. I also break up my UI into manageable units and build them separately. Within the unit things are managed however it makes sense for the unit, but the overall layout of these units is done in a separate proc. That makes it easy to create a View menu with items like "show toolbar", 'show statusbar", etc. I also store in a global array any widget paths that I use in other parts of the program. This way I can change the widget hierarchy as needed without having to track down hard-coded path names in other parts of the code. My initialization code, then, looks something like this: proc main {} { ... widgets widgets.layout .... } proc widgets {} { widgets.menubar widgets.toolbar widgets.statusbar widgets.main } proc widgets.toolbar {} { global widgets set widgets(toolbar) .toolbar button $widgets(toolbar).cut ... button $widgets(toolbar).copy ... ... pack $widgets(toolbar).cut $widgets(toolbar).copy \ -side left } ... proc widgets.layout {} { global widgets global options . configure -menu $widgets(menubar) grid $widgets(toolbar) -row 0 -column ... grid $widgets(main) -row 1 -column ... grid $widgets(statusbar) -row 2 -column ... if {!$options(-showtoolbar)} { grid remove $widgets(toolbar) } .... } In reality I pass in the toplevel window to each of the procs so that, in theory, I can reparent the widgets if later I choose to embed them in a larger program. I left that out of this example to make the example a little easier to understand. ---- [Bryan Oakley] 12-Aug-2003 here's another one: never put more than two statements in a widget callback or binding, and if you use more than one, the second one must always be "break". Put another way, always have your callbacks and bindings call procs. It makes quoting easier, makes it easier to modify what bindings do, and makes it easier to share code between bindings and callbacks (ie: the "cut" toolbar can call the same proc as the "cut" menubar item and the "cut" accelerator). ---- Please contribute any principles that help you write Tk code in a more readable, manageable, and/or reusable way. ---- [Category Design] | [Category Concept] | [Category GUI]