TIP 306: Auto-Naming Widgets

Login
Bounty program for improvements to Tcl and certain Tcl packages.
Author:         Koen Danckaert <koen@retarget.com>
Author:         Richard Suchenwirth <richard.suchenwirth-bauersachs@siemens.com>
State:          Rejected
Type:           Project
Vote:           Done
Created:        11-Jun-2007
Post-History:   
Keywords:       automatic,Tk,widget,naming
Tcl-Version:    8.6

Abstract

A Tk programmer must give every widget a unique name. This is often quite annoying, especially for widgets whose name is stored in a variable (which also must be given a name). This TIP proposes a small extension to generate automatic names for widgets.

Rationale

Every Tk widget must be given a unique name. This is needed internally for Tk and also for the programmer to be able to refer to it. Quite often, and as recommended in many Tk coding guidelines (e.g. http://www.beedub.com/book/2nd/TKINTRO.doc.html\), the widget names are stored in variables and only referred to indirectly. These variables must also be given a name, which may lead to confusion, and requires more inventivity of the programmer than needed.

An example from the BWidget source code:

 set status   [frame $path.status -background $bg]
 set label    [label $status.label]
 set indframe [frame $status.indf -background $bg]
 set prgframe [frame $status.prgf -background $bg]

Other cases where widget names are unimportant from a programmer's viewpoint, are widgets which are never referred to after creation, and those which are only referred to by other means (e.g. by a textvariable). Example:

 pack [label .name_label -text "Enter name:"] -side left
 pack [entry .name_entry -textvariable name] -side left

In all those cases, it would be helpful if Tk could generate the widget names itself.

Further motivation for this can be found in the following:

  • Tcl/Tk already uses auto-naming for other things, e.g. for channels and for images.

  • In Python and Ruby's Tk implementation, automatic widget names are the default. For example, a label in TkInter is normally created as:

     mylabel = Label(parentframe, text="something")
    

    In this case the Tk widget name will be something like $parentframe.123. If you really want to assign a name yourself, you can use the optional name argument:

     mylabel = Label(parentframe, text="koen", name = "mylabel")
    

Currently the widget creation commands already return the widget name, which is always the exact name the programmer has supplied. This makes it easy to make an (almost) backwards compatible extension, as presented below.

Specification

When creating a new widget with a name that ends with "%", the "%" will be replaced by a counter. The actual widget name is then returned. Depending on the chosen implementation, there may be one global counter or each parent widget can have its own counter.

 % button .%
 .1
 % set f [frame .%]
 .2
 % label $f.%
 .2.3
 % set p [frame .prefix%]
 .prefix4
 % label $p.%a%b%c%%
 .prefix3.%a%b%c%5

The examples above can now be written as:

 set status   [frame $path.% -background $bg]
 set label    [label $status.%]
 set indframe [frame $status.% -background $bg]
 set prgframe [frame $status.% -background $bg]

 pack [label .% -text "Enter name:"] -side left
 pack [entry .% -textvariable name] -side left

Reference Implementation

See SourceForge patch #1735008http://sf.net/support/tracker.php?aid=1735008 . The reference implementation keeps a counter in the TkMainInfo structure, which is thread-local.

Compatibility

Backward Compatibility

The presented extension is backward compatible, except for existing code which uses widget names ending on "%". Even this will only be a problem if the widgets are referenced directly (i.e. not by a variable) after creation.

Forward Compatibility

This is another issue. Megawidgets which want to support the new naming scheme, will probably have to be adapted. In particular, when they are written as:

 proc mymegawidget {win args} {
     # do some stuff with $win
     # ...

     # Create the hull frame
     frame $win -class MyMegaWidget

     # Create internals
     label $win.title -text "Title"
     # ...
 }

they will have to be rewritten in the following way:

 proc mymegawidget {w args} {
     # Create the hull frame (returns the actual widget name)
     set win [frame $w -class MyMegaWidget]

     # do some stuff with $win
     # ...

     # Create internals
     label $win.title -text "Title"
     # ...
 }

In Tk itself, there is one such example which has to be rewritten: tk_optionMenu. This is not expected to be a significant hurdle.

Alternatives

  1. Implementing this TIP in Tcl. This would require all widget commands to be wrapped. That's a lot of work compared to the simple C code patch used here.

  2. Make a separate autoname command, and use this when creating widgets. For example:

     set frame [frame [autoname .%]]
     label [autoname $frame.%]
    

    This makes the code a longer and more difficult to read, which is the opposite of what this TIP tries to achieve.

  3. A very simple autonaming can be done by just setting aside one global variable and define an alias that increments that variable:

     set ::# 0
     interp alias {} % {} incr ::#
     set frame [frame .[%]]
     label $frame.[%]
    

    However, using a short procedure name like "%" has the drawback that extensions or modules which you want to incorporate in your code, may already have defined "%" for something else. Also, as each module may have its own autonaming scheme, there is a risk for overlapping widget names. So auto-naming really belongs in the core.

  4. Instead of using a special character, the widget creation commands could get a -parent option to define the parent widget. Then, allow the widget command to be called without an explicit widget path, in which case it creates an auto-named child of the parent. The use of -parent and an explicit pathname would be mutually exclusive. For example:

     set frame [frame .f]
     set button [button -parent $frame -borderwidth 2 ...]
    

    This may create confusion, since it seems to allow the parent to be modified (after creation) with the "configure" widget command. Furthermore, to implement this, all widget creation C functions have to be modified, including in extensions such as BLT and tktable, instead of just the NameWindow() function. Probably also the configure subcommands have to be adapted...

    Another drawback is that "normal" and "automatic" widget creation now have a different syntax.

  5. A variation of the previous version can be implemented in pure Tcl:

     proc newchildof {parent creator args} {
         (generate proper $child given $parent)
         $creator $child {*}$args
     }
    
     set frame [frame .f]
     set button [newchildof $frame button -borderwidth 2 ...]
    

    A drawback of this is that it is a bit verbose, and that "normal" and "automatic" widget creation have a different syntax. Also each extension may use its own autonaming scheme, which does not exactly favor code readability, and may cause name collisions.

Discussion

There was a short discussion about this TIP on comp.lang.tcl http://groups.google.com/group/comp.lang.tcl/browse_frm/thread/2ecd6fbf92de985b/e7471f5829933df2?lnk=gst&q=306#e7471f5829933df2 . Most alternatives which came forth from this discussion, were added to the previous section.

While some of the alternative proposals above are usable (especially (5)), they still have drawbacks. The TIP author believes that auto-naming really belongs in the core. Just like in Python and Ruby, auto widget names will probably be the default once they're added to Tk. A good standard syntax for this will not only increase the writability of the code, but also the readability. Indeed, in current Tk code many widget names are used but never referred to again. Just using ".%" on these places makes it clear that the actual widget name is unimportant. Also, the proposed syntax is clear and concise and does not divert from the normal widget creation syntax.

Copyright

This document has been placed in the public domain.

History