Options and Tk - A Beginner's Guide




0. The Underlying Hierarchy

The widget hierarchy is really a forest (multi-rooted tree) with each application being one tree. The branches of the tree are the widgets, and the leaves of the tree are the options of the widgets.

1. What is Happening with Options

1.1 Basic Option Specification Use

To specify an option to a widget using the option database, you need to specify the pathname of the widget whose option you want to set, the option which is going to be set, and the value you want to set it to. The option database is a collection of these.

If the application is called spong, and you have a widget called .foo.bar, then you can set the cursor for that widget to be a watch (or semantic equivalent on non-Unix platforms) using a resource database entry of:


  spong.foo.bar.cursor: watch

^^^^^^^^^^^^^ ^^^^^^ ^^^^^ path option value

There are several ways of making this entry into the resource database (under Unix/X):

  1. Placing the entry in your ~/.Xdefaults file which Tk reads in when it starts up. [*] This is a system-wide file.

  2. Using xrdb to enter it into the system-wide resource database, which Tk reads when it starts up. [*]

  3. Using:

    option add spong.foo.bar.cursor watch

    This is specific to the particular application that performed the operation.

  4. Placing the entry in a file somewhere and using option readfile to load it in. This is probably the easiest technique for application writers. This is specific to the particular app. that performed the operation, but it is especially useful for loading the defaults for an application.

You can always find out the name of a window using:

winfo name $win

though this is mostly only useful when examining . [**]

The name of an option is either listed in the manual page where the widget is described, or in the options(n) manual page. It is usually the same as the -option name, except with the second and all subsequent words within the name capitalised. Thus, -borderwidth has the option name borderWidth

1.2 Widget Classes

Each widget has a class as well as a name. Classes are distinguished from names by starting with a capital letter, and they are useful for when you want to set the options of a collection of similar windows.

The class of most widgets is fixed, but for frames and toplevels, you can set it to be something new. I strongly recommend doing this for toplevels, and particularly those where you will be creating then with more than one name, as it lets you separate different kinds of windows (particularly non-modal dialog boxes) from each other. The use of programmer-defined classes for frames is mostly less useful, but can come into its own in highly complex widget hierarchies, and is crucial to supporting megawidgets correctly in the option database.

When specifying the class of a widget (via the -class option, which cannot be specified via the resource mechanism) you should take care not to use the class of any of the normal widgets (like Button, Scrollbar, Text, etc.) to prevent confusion, if nothing else.

The resource entry:

spong.foo.Button.cursor: watch

tells all the buttons that are children of .foo in the application called spong to have a watch cursor.

You can always find out the class of window using:

winfo class $win

1.3 Application Classes

Applications also have a class, which starts out as a capitalised version of the name of . This can be probed in the obvious way.

The name and class of . is rarely of any use to the application writer, and should probably be left to users as a way for them to use when adding their own resource specifications.

1.4 Option Classes

Each option, in addition to its name, has a class so that related options can be referred to by a single entry in the database. The class of an option is usually the same as the name of the option, except with the first letter capitalised as well. Note that this is not always true (particularly for less common options,) and that you should look up the option in the appropriate manual page if you are not sure.

e.g. To set all the background-related options for .foo.bar, you could use a database entry like this:

spong.foo.bar.Background: white

1.5 Wildcards in Paths

It quickly becomes very tedious to write the full path to a widget whenever you want to specify an option using the database, and quite often you want to refer to widgets that are at an uncertain depth. By using the wildcard, *, for a section of the path, you can make a database entry that refers to the options of many widgets at once.

This is particularly useful when used at the beginning of the path, as it can also match any application name, so allowing for the case when a user starts up the application twice and Tk is forced to choose a different name for the app.

The following sets the background-related options for all buttons to be white. If used in a system-wide resource database, this will be applied across all applications (and may well be recognised by non-Tk applications.) It is perfectly safe if used in the context of something loaded into the application's database using option add or option readfile

*Button.Background: white

Obviously, you cannot use a wildcard for a particular option name since there is no value that is universally accepted for all options, and wildcards have no meaning in the context of values.

1.6 Option Priorities

Tk manages options according to their priority, taking the most recently encountered option at the highest priority where something can be found to match. There are five primary priority levels. From lowest to highest, they are:

widgetDefault

The hard-coded default for the particular option. It is only useful to set something at this level when you are writing a megawidget, as everything else overrides it.

startupFile

This level should be used by application writers when reading application-level option database entries. More on this later.

userDefault

This level should be used by application writers when reading in per-user option database entries. The system-wide options in the user's .Xdefaults file and attached to the user's display are loaded at this level.

interactive

This level should be used for all entries made because of user actions (e.g. because of what a user does in a configuration management dialog.) This is the default level for the option command to work at.

"programmed"

This is not a conventional level, but rather the pseudo-level that all options that are specified as part of a widget configure subcommand are introduced at. Anything introduced by this route will override all entries in the option database for the particular option and widget, though it will have no truly persistent effect, and if you destroyed and recreated the widget, the effects of this setting would be lost.

For more information, see the option(n) manual page.

2. Implications for Programmers

2.1 Setting Up the Option Database

It is generally the case that it is a good thing that users should be able to configure the way that their applications look. Doing this with Tk is relatively easy, provided an application defaults file is used. Otherwise, it can be very tricky.

When creating an application defaults file, you should make all specifications of the look of a widget via that file. Thus, things like the border width, relief, cursor, foreground and background of the widgets can be usefully defined via this route. The easiest place to create the file is in the same directory as the Tcl script that loads the app-def file, so you can load it using a command like:

set scriptdir [file dirname [info script]]
option readfile [file join $scriptdir myapp.def] startup

If you then add another command to load a user's app-def file, like:

catch {option readfile [file join ~ myapp.def] user}

it becomes easy for a user to override any of the settings in the application's defaults.

2.2 Introducing Your Own Options

It is easy to add your own options to a widget.[***] To do this, make it so that every time you need to option the value, you perform an option get command. This requires three arguments:

It is acceptable to only read the option database once, when the widget being used as the pathname is created.

3. Problems and Caveats

Some things cannot be specified by defaults files (notably the -class option to some widgets) and some probably should not be specified by defaults files (particularly those options that affect the behaviour of the interface, like -command, -show, -variable, -xscrollcommand, etc.) as otherwise the interface can end up completely non-functional, or even become a security hole. Often those options are only usefully set in the context of part of the application anyway.

Furthermore, some things cannot be specified by options (layout options for pack and grid, or the items on a menu) even though it would be useful to do so. You can work around this, but it is not especially easy.

To make things worse, some window/session managers (notably Mwm/CDE) set "useful" options like *Background, which can make life rather interesting. I've had problems with debugging the interface of an application from user reports because of this.

Also, the way Tk handles the priorities of options is fundamentally different to the way that other toolkits (such as those based on Xt, like the Athena or Motif widget sets) and this can cause a lot of confusion. Put your more general options first, followed by your more specific ones, and you will be OK...

Note that there is no automatic update of existing widgets when the database is modified. If you want this, you will need to implement it yourself.

There are probably more problems involved with the option database, but that is all I can think of right now... :^)


4. Copyright, etc.

Copyright © 1997 Donal K. Fellows. Permission to distribute this message without payment is granted, but all commercial use is forbidden. If you want to use this document commercially (or in any other way not explicitly permitted above,) please contact the author. He'll probably say yes... :^)


[*]

This also occurs after an option clear operation.

[**]

The linkage between the name of . and the value returned by winfo name . can be broken by the tk appname command.

[***]

This is only from the point-of-view of the option database. Adding them from the point of view of the configure command is a business for megawidgets only.