FE Tcl-Tk Systems

A Canonical Structure for
Tcl-Tk Coding of GUI's

with discussion of
some possible
variations of the
coding structure

(FE = Freedom Environment)

FE Home page > FE Downloads page >

FE 'tkGooies' Description & Code Samples page >

(almost every sample FE Tcl-Tk code page) >

This Canonical Tk Coding Structure page

INTRODUCTION to
'A Canonical Structure for Tk Code' --- and variations :

In essentially all of my posts of Tk code on this site (for various utilities), I have used a 'canonical' structure for the Tk code --- as follows:



 -1) Put documentation comments in the script from the
     very start of creating the script.

  0) Set general window parms (win-name, win-position,
     color-scheme, fonts, widget-geometry-parameters,
     win-size-control, text-array-for-labels-etc).

  1a) Define ALL frames (and sub-frames, if any).
  1b) Pack ALL the frames and sub-frames.

  2) Define & pack all widgets in the frames ---
     typically going top-to-bottom and/or left-to-right,
     frame by frame.

           After ALL widgets are defined for a frame,
           pack the widgets in the frame.

  3) Define key and mouse/touchpad/touch-sensitive-screen
     'event' BINDINGS, if needed.

  4) Define PROCS, if needed.

  5) Additional GUI initialization (typically with
     one or more of the procs), if needed.


And to cover the case when I find that I need to make a new widget (from existing widgets), via a proc, then I insert the step:



  1c) Define a 'new-widget' proc that is used to
      make one or more widgets in step 2.


I find this structure helps

  • when generating and testing a Tk script,

  • when looking, among my scripts, for code snippets to include in other scripts.

As I mentioned at the top of one of my first code-postings at wiki.tcl.tk --- titled 'A non-obfuscated color selector GUI':

"Way back around 1998 when I was first learning the Tcl-Tk language, I would do web searches on keyword strings such as 'usr bin wish button' or 'usr bin wish scale rgb' to find examples of complete Tcl-Tk scripts --- because the code 'snippets' in Tcl-Tk books and tutorials were not very helpful in creating 'production' scripts (relatively quickly)."

I was always amazed (and dismayed) at the variety of coding styles that I found --- many of the authors apparently coming from the C-programming world and putting all their code in procs at the top of their scripts --- sometimes even putting the code defining the widgets for the Tk GUI in a proc --- and putting a single-word line at the bottom of their scripts calling a proc named 'Main' or 'main' or 'doit' or the like.

Others seemed to follow a 'whatever works' or 'stream of consciousness' coding method.

From the same author, one script might have the Tk widget definitions at the top of the code --- another time in a proc in the middle of the code --- another time in statements at the bottom of the code.

That is all OK when you are writing code that is about a paragraph (or two or three) long.

But I found that when I was writing my Freedom Environment code . . . to be posted at the web site www.freedomenv.com . . . and was aiming for contributing the code to the public, I needed to come up with some Tk coding-structure standards before publishing the code to the world.

So I was strongly motivated to come up with a 'canonical' coding structure to use for Tk scripts in my Freedom Environment software --- as well as for the 2 reasons mentioned above --- (1) facilitation of the coding-and-testing process and (2) facilitation of finding useful code snippets in my own code (code re-use).


Ousterhout's application of Occam's Razor:

There is no obvious event-handling loop in Tk scripts.

At the bottom of my posting at wiki.tcl.tk titled 'YAFSG - Yet Another Font Selector GUI', I gave praise to Ousterhout's wonderful implementation of Tk --- and how he has spared us programmers from writing superfluous code --- over and over and over.

The hidden event handling loop is one of the wonderful decisions that Ousterhout made --- along with 1000's of others.

Just look at the typical C or C++ program that has an event handling loop for

  • window-manager events, or

  • OpenGL events, or

  • SDL events (where SDL = Simple DirectMedia Layer --- a library that provides low level access to audio, keyboard, mouse, joystick, 3D hardware via OpenGL, and 2D framebuffer across multiple platforms).

Here are a couple of examples:



while(!handler.quit)
{
   if(handler.redraw)
   {
      redraw();
      surface.swap();
      handler.redraw = false;
   }

   // Wait for events and call event handlers
   window.wait();
}


Here's an example from a game-programming site.



while ( game should run )
{
  ...

  renderFrame() // OpenGL frame drawing code goes here

  handleUserInput()

  ...
}


In a Tk script, we never have to put loops like that in our code to handle the events specified in our 'bind' commands or in the bindings that are built into the Tk widgets.

Furthermore, we are not required to put a 'main' routine in every one of our Tk scripts.

Ousterhout apparently had the vision to see that the event-handling-loop (and the 'main' routine requirement) could be handled by the 'wish' interpreter and not by the programmer.

Thank you, Ousterhout.

By the way, I mention the hidden event handler because it is helpful to know what is going on 'underneath the covers' in order to make some sense out of what is possible and what is not possible when structuring one's Tk code.


Variations on the Canonical Structure Above:

Experienced Tk scripters may have noticed that the sections in the canonical layout above can be put in almost any order --- with the exception of steps 1c (defining-widget-making procs) and 5 (calling-additional-GUI-initialization procs).

For example,the BINDINGS section can go almost any place, top to bottom.

This is because the 'bind' statements are not used until 'wish' drops us into the event-handling loop.

And all the window-and-frame-and-widget defining-and-packing --- steps 0, 1, and 2 --- those code sections can be placed almost anywhere, top to bottom --- because these definitions and packing are, typically, not required by any other section of code (procs and bindings) until 'wish' drops us into the event-handling loop and starts using the procs and bindings.


Some people might want to put BINDINGS below PROCS.

The main reason that I put BINDINGS first is that most of the code-detail is in the procs.

I prefer saving the detail for last --- or as close to last as possible.

Since I typically use one or two procs in the PROCS section to do the 'additional-GUI-initialization', the code in that last section will 'throw an error' if the proc is not available.

So I DO need to keep the PROCS section before the 'additional-GUI-initialization' section.

Nicely enough, the code in the BINDINGS section does not 'throw errors' if the procs that are used in bindings are not defined yet (i.e if the procs are not above the 'bind' statements).

So I can put the BINDINGS section almost anywhere.

Similarly, the 'wish' interpreter does not object if you use a proc in a '-command' option of a widget definition statement and the proc has not been defined yet (as the interpreter 'assimilates' the statements of the script, from top to bottom).

So widget definitions (and packing) can go almost anywhere in the code --- that is, the widget definition statements can be placed above or below the PROCS and BINDINGS sections.


Summary:
(See 'Conclusion' and UPDATE sections below.)

In summary, I could try some other orderings of the 'canonical' structure at the top of this page.

But that ordering has worked for me in making over a hundred Tk scripts. So I think I will stick with that for now.

One 'improvement' (that I see at this time - 2012sep), that I might add at some point, relates to the intialization of variables used in the various widgets of a GUI.

Sometimes I find that I would like to go back and change some intial settings for widgets --- like entry fields, checkbuttons, radiobuttons, scale variable, initial listbox selection, etc. --- either temporarily during testing or just before 'release' as I settle on appropriate initial settings.

Currently, I tend to put the initial values for widgets just before the definition of each widget. This scatters the initializations throughout code section 2, which can be a quite extensive section.

Instead, I may someday break section-2 into two sections:

  • 2a) Define & pack all widgets in the frames.

  • 2b) Set initial values for the widget variables.


2012oct28 UPDATE - on
INITIALIZING application/widget VARIABLES :

I have recently found that it is most helpful to put the section labelled (2b) above down in the section 5 --- the 'additional GUI initialization' section.

In fact, I have found that for some GUI's it is helpful to have multiple sets of initial values --- to be used as 'use-cases' for testing the Tk script under a variety of conditions, simulating various possible user inputs.

For example, in testing a plot utility with entry fields for titles, x-axis data range, y-axis data range, x-axis data, y-axis data, and tic-mark parameters, one can have many sets of data/parameters with which to test the Tk scripts.

Just put each set of data/parameters in an

if {0} { .... }

clause, and change the zero to a one for the set of data/parameters that you would like to test with next. Then start up the Tk script.

So in the future, I will probably be putting statements that 'set initial values for the widget variables' down at the bottom of my Tk scripts --- in so-called section 5.


Conclusion:

I have been a quite 'happy camper' so far with the canonical structure shown at the top of this page. It helps my productivity tremendously, while being quite flexible --- thus appealing to my appreciation of freedom in essentially all things.

    I should note that there are occasional cases where I have had to depart somewhat from the canonical structure outlined above.

    In particular, the 'make_chest.tk' Tcl-Tk scripts that are used in the 'feAppMenus' and 'tkGooies' FE systems are an exception.

    That type of script reads a 'chestdef' file and may need to make 'bind' statements as it makes 'drawers' for the toolchests.

    In that case, there is no contiguous BINDINGS section.

    The 'bind' statements (and the drawer-making statements) are contained within a read-loop.

    Some demos of 'toolchest-making' Tcl-Tk scripts are on the Embellished GUI demos - toolchests page.


2012oct14 UPDATE - on
'in-code' DOCUMENTATION :

Recently I ran across an Ousterhout document that reminded me that I should have put a 'step-minus-one' before 'step-zero' in the canonical code-structure list at the top of this page.

'Step-minus-one' is:
"Put documentation comments in the script from the very start of creating the script."

My reasons for that are the same ones that Ousterhout outlined in his September 1994 document 'Tcl/Tk Engineering Manual' that he authored when he was at Sun Microsystems.

In particular, I am referring to section 6.5 of that document --- titled 'Document as you go'.

I think it is worthwhile to reproduce the text of that section, here:

    "It is extremely important to write the documentation as you write the code. It's very tempting to put off the documentation until the end; after all, the code will change, so why waste time writing documentation now when you'll have to change it later? The problem is that the end never comes - there is always more code to write. Also, the more undocumented code that you accumulate, the harder it is to work up the energy to document it. So, you just write more undocumented code. I've seen many people start a project fully intending to go back at the end and write all the documentation, but I've never seen anyone actually do it.

    If you do the documentation as you go, it won't add much to your coding time and you won't have to worry about doing it later. Also, the best time to document code is when the key ideas are fresh in your mind, which is when you're first writing the code. When I write new code, I write all of the header comments for a group of procedures before I fill in any of the bodies of the procedures. This way I can think about the overall structure and how the pieces fit together before getting bogged down in the details of individual procedures."

Those are my feelings exactly.

There are drawbacks, however.

The main one that I encounter is that during the testing and debugging phase, I frequently revise some of the GUI design, change some of the bindings, and/or change the procedures (including adding new procs or consolidating previously written procs).

As a consequence, some of my original in-code documentation is no longer quite correct --- and frequently I forget to change all affected parts of the documentation.

But I would rather err on the side of having some slightly-out-of-sync documentation than have no documentation at all.


2012nov09 UPDATE - on
INTERNATIONALIZATION :

One new thing that I have added is the 'text-array-for-labels-etc' item in section 0.

Using 'set' statements for an array of text items (I use variable name 'aRtext') , located all together, in one section of the code, can make it easier for people to internationalize my scripts.

I may be using a text-array like this in most of my scripts in the future.


2013aug24 UPDATE on NAMING
(WIDGET NAMING and VARIABLE NAMING)

'frame' widgets:

Anyone who has seen my script donations on wiki.tcl.tk may have noticed that I consistently use the prefix .fR for names of frames.

This is because many of the times that I looked at other peoples' code, when I saw a name like '.c' I would generally have to look through the code to see if that name was referring to a frame.

Or was it a widget in the window? If so, what kind of widget? A 'canvas' widget?

I find that it is much nicer to see a widget name (a name prefixed by a period) and know immediately, from the name, that the widget is a frame --- or a non-frame, if the name does not start with '.fR'.

    (I originally tried 'FR' but found that 'leading' capital letters for widgets result in errors because the name clashes with the fact that 'class names' for widgets are the same name, but starting with a capital letter. '.fR' works fine for me.)

I started using this '.fR' naming convention many years ago --- before 2012.

I have slowly over the past several years (since about 2012) been developing other naming conventions that I like to use.

Here are some widget-naming conventions that I have started using.


WIDGET NAMING:

'label' widgets:

When I define a 'label' widget, I prefix the name with .lab or .label --- examples: .labelINFO, .labCOLOR1, .labCOLOR2.

Then I know right away, without looking through the code, that I am dealing with a label widget.

Compare this to seeing a name like '.fred' or '.l' somewhere within many, many lines of code.

Even the name '.l' is vague because that is often used by people for a 'listbox' widget.

'entry' widgets:

Similarly, when I define an 'entry' widget, I prefix the name with .ent or .entry.

'text' widgets:

Similarly, when I define an 'text' widget, I prefix the name with .txt or .text.

'button' widgets, 'scale' widgets, etc. :

Similarly, I use at-least-3-character prefixes for 'button', 'radiobutton', 'checkbutton', 'scale', 'canvas', and 'listbox' widgets.

Examples:

  • .but or .butt or .button
  • .radbut or .radbutt
  • .chkbut or .chkbutt
  • .scal or .scale
  • .can or .canvas
  • .lbox or .listbox


VARIABLE NAMING:

'radiobutton' variables:

I have started using the prefix RADVAR for the '-variable' used by a 'related' set of radiobutton widgets.

Again, with so many names of various types within a large Tk script, it is helpful to know right away that the name is the name of a variable --- a variable related to a group of 'radiobutton' widgets.

Example: 'RADVARcolors'

'checkbutton' variables:

Rather than using a 'CHKVAR' prefix, I have started using the suffix '0or1' for the '-variable' used by a checkbutton widget.

For example, for a checkbutton that is used to indicate whether outlines should be drawn around 'oval' or 'rect' items drawn on a Tk canvas, I would use a name like 'outline0or1'.

I know right away when I see that '0or1' suffix that it is a variable associated with a checkbutton on the GUI.

'scale' variables:

I may start using a prefix like SCALVAR for the variable typically set by a 'get' command on a scale widget --- or for a variable otherwise defined to hold the value of the slider-setting of a 'scale' widget.

Again, with so many names of various types within a large Tk script, it is helpful to know right away that the name is the name of a variable --- a variable related to the slider-setting of a 'scale' widget.

Example: SCALVARwidthPx

'entry' variables:

I have started using the prefix ENTRY for the '-textvariable' used by an entry widget.

Again, with so many names of various types within a large Tk script, it is helpful to know right away that the name is the name of a variable --- a variable related to a 'entry' widget.

Examples: 'ENTRYxmin' and 'ENTRYxmax'

Note that if a Tk script changes the value of an 'entry' widget variable, the next time the GUI is updated (due to handling of an 'event'), the 'entry' field of the GUI will show the changed value.

The same applies to 'RADVAR' and '0or1' variables.

Namely, a change to them within a Tk script will result in a change to a radiobutton-group or a checkbutton on the GUI, the next time the GUI is updated.

'array' names:

For array variable names, I have started using a prefix of aR.

Example: 'aRtext'
--- as mentioned in an 'internationalization' note above.

'list' names:

For list variable names, I have recently started using a prefix of LIST.

Example: 'LISTcolors'

'tag' names:

I have started using the prefix TAG for all tag names.

With all sorts of names populating Tk scripts that involve 'canvas' widgets, it is helpful for me to see a name starting with 'TAG' and know RIGHT AWAY (without scanning back and forth through the code) that the named item is a 'tag'.

Examples: 'TAGpoints' and 'TAGlines'

pixel variables:

I have recently started using the suffix px or Px or PX on variables that are meant to hold a number (non-negative integer or non-negative decimal number) of pixels.

Thus, when I see the variable name, I know right away, without scanning back and forth through the code, that the variable is intended to hold a number of pixels (and, hence, is to be treated, eventually, as a non-negative integer).

More to come?

I am still settling on naming conventions.

If I think of a few more naming conventions that I have found helpful, I will add them here.

    It turns out that I have been settling on naming conventions over an extended period of time --- years --- from 2012 to 2017, at least. (Actually, back to around 1999 --- when I started learning Tcl-Tk.)

    As I continue to adopt new naming conventions that seem quite useful (in coming years --- or decades), then I may return to this page to add a few more of my 'best practices in names'.

Bottom of this page on a
Canonical Coding Technique for Tk GUI's.

To return to a previously visited web page, click on the Back button of your web browser a sufficient number of times. OR, use the History-list option of your web browser.
OR ...

< Go to Top of Page, above. >

Page history

This description was originally posted on 2012sep18 at http://wiki.tcl.tk/36965. This page is probably much more up-to-date than the wiki page.

Page was created 2014 May 05.
(As a backup and alternative to the wiki.tcl.tk page.)

Page was changed 2017 Sep 24.
(Changes to the 'UPDATE on NAMING' section.)

Page was changed 2018 Jan 16.
(Reorganized the 'UPDATE on NAMING' section.)

Page was changed 2018 Aug 12.
(Added css and javascript to try to handle text-size for smartphones, esp. in portrait orientation.)

Page was changed 2019 Feb 25.
(Minor reformatting.)

Page was changed 2019 Jun 12.
(Specified an image width in percents to size the image according to width of the browser window.)


NOTE:
The content of this page will probably always be more 'up-to-date' than the 'Canonical Tk Code Structure' content posted on the Tcler's Wiki ---
wiki.tcl-lang.org --- formerly wiki.tcl.tk.