This chapter describes the use of the resource database, and how users can define buttons and menus via resource specifications. This chapter describes the option
command.
Tk supports a resource database that holds specifications of widget attributes such as fonts and colors. You can control all attributes of the Tk widgets through the resource database. It can also be used as a more general database of application-specific parameter settings.
Because a Tk application can use Tcl for customization, it might not seem necessary to use the resource database. The resource database is, however, a useful tool for your Tk application. A developer can make global changes with just a few database entries. In addition, it lets users and site administrators customize applications without modifying the code.
When a Tk widget is created, its attributes are set by one of three sources. It is important to note that Tcl command specifications have priority over resource database specifications:
The most evident source of attributes are the options in Tcl commands, such as the -text quit
attribute specification for a button.
If an attribute is not specified on the command line, then the resource database is queried as described later.
If there is nothing in the resource database, then a hard-coded value from the widget implementation is used.
The resource database consists of a set of keys and values. Unlike many other databases, however, the keys are patterns that are matched against the names of widgets and attributes. This makes it possible to specify attribute values for a large number of widgets with just a few database entries. In addition, the resource database can be shared by many applications, so users and administrators can define common attributes for their whole set of applications.
The resource database is maintained in main memory by the Tk toolkit. On UNIX the database is initialized from the RESOURCE_MANAGER
property on the root window, or the .Xdefaults
file in your home directory. On Windows and Macintosh, there are a few resources added by the tk.tcl
library file. Additional files can be explicitly loaded with the option readfile
command, and individual database entries are added with the option add
Tcl command.
The initialization of the database is different from the Xt toolkit, which loads specifications from as many as five different files to allow per-user, per-site, per-application, per-machine, and per-user-per-application specifications. You can achieve the same effect in Tk, but you must do it yourself. Example 45-1 on page 672 gives a partial solution.
The pattern language for the keys is related to the naming convention for Tk widgets. Recall that a widget name reflects its position in the hierarchy of windows. You can think of the resource names as extending the hierarchy one more level at the bottom to account for all the attributes of each individual widget. There is also a new level of the hierarchy at the top to specify the application by name. For example, the database could contain an entry like the following in order to define a font for the quit
button in a frame called .buttons
:
Exmh.buttons.quit.font: fixed
The leading Exmh.
matches the class name for the Tcl/Tk application. The class name of the application is set from the name of the script file, with the first character capitalized. For example, if the script is /usr/local/bin/foobar
, then the class is set to Foobar
. You could also specify an asterisk to match any application:
*buttons.quit.font: fixed
Resource keys can also specify classes of widgets and attributes as opposed to individual instances. The quit
button, for example, is an instance of the Button
class. Class names for widgets are the same as the Tcl command used to create them, except for a leading capital letter. A class-oriented specification that would set the font for all buttons in the .buttons
frame would be:
Exmh.buttons.Button.font: fixed
The application class name becomes the class name for the main toplevel window. For example, if you use a script name like button.tcl
, the class for .
becomes Button
. This causes it to inherit all the standard Button
bindings and attribute values, which can cause problems in your application.
Patterns let you replace one or more components of the resource name with an asterisk (*
). For example, to set the font for all the widgets packed into the .buttons
frame, you can use the resource name *buttons*font
, or you can specify the font for all buttons with the pattern *Button.font
. In these examples, we have replaced the leading Tk
. with an asterisk as well. It is the ability to collapse several layers of the hierarchical name with a single asterisk that makes it easy to specify attributes for many widgets with just a few database entries.
The tables in this book list attributes by their resource name. The resource names use a capital letter at the internal word boundaries. For example, if the command line switch is -offvalue
, then the corresponding resource name is offValue
. There are also class names for attributes, which are distinguished with a leading capital (e.g., OffValue
).
The matching between a widget name and the patterns in the database can be ambiguous, with multiple patterns matching the same widget. The order of database entries determines which pattern is used, with later entries taking precedence. (This is different from the Xt toolkit, in which longer matching patterns have precedence, and instance specifications have priority over class specifications.) Suppose the database contained just two entries, in this order:
*Text*foreground: blue *foreground: red
Despite the more specific *Text*foreground
entry, all widgets will have a red foreground, even text
widgets. For this reason you should list your most general patterns early in your resource files and give the more specific patterns later.
Tk also supports different priorities among resources, as described in the next section. The ordering precedence described here applies to all resources with the same priority.
The option
command manipulates the resource database. The first form of the command loads a file containing database entries:
option readfile filename ?priority?
The priority
distinguishes different sources of resource information and gives them different priorities.
Priority levels are numeric, from 0 to 100. However, symbolic names are defined for standard priorities. From lowest to highest, the standard priorities are widgetDefault
(20), startupFile
(40), userDefault
(60), and interactive
(80). These names can be abbreviated. The default priority is interactive
.
The format of the entries in the file is:
key: value
The key has the pattern format previously described. The value can be anything, and there is no need to group multiword values with any quoting characters. In fact, quotes will be picked up as part of the value.
Comment lines are introduced by the exclamation mark (!
).
Example 31-2. A file containing resource specifications
! ! Grey color set ! These values match those used by the Tk widgets on UNIX ! *background: #d9d9d9 *foreground: black *activeBackground: #ececec *activeForeground: black *selectColor: #b03060 *selectBackground: #c3c3c3 *troughColor: #c3c3c3 *disabledforeground:#a3a3a3
The example resource file specifies the color scheme for the Tk widget set on UNIX that is based on a family of gray levels. Color highlighting shows up well against this backdrop. These colors are applied generically to all the widgets. The hexadecimal values for the colors specify two digits (eight bits) each for red, green, and blue. Chapter 41 describes the use of color in detail.
You can enter individual database entries with the option add
Tcl command. This is appropriate to handle special cases, or if you do not want to manage a separate per application resource specification file. The command syntax is:
option add pattern value ?priority?
The priority
is the same as that used with option readfile
. The pattern
and value
are the same as in the file entries, except that the key does not have a trailing colon when specified in an option add
command. If value
contains spaces or special characters, you will need to group it like any other argument to a Tcl command. Some of the specifications from the last example could be added as follows:
option add *foreground black option add *selectBackground #bfdfff
You can clear the option database:
option clear
However, on UNIX the database will be initialized from your ~/.Xdefaults
file, or the RESOURCE_MANAGER
property on the root window, the next time the database is accessed.
Often, it is sufficient just to set up the database and let the widget implementations use the values. However, it is also possible to record application-specific information in the database. To fetch a resource value, use option get
:
option get window name class
The window
is a Tk widget pathname. The name
is a resource name. In this case, it is not a pattern or a full name. Instead, it is the resource name as specified in the tables in this book. Similarly, the class
is a simple class name. It is possible to specify a null name or class. If there is no matching database entry, option get
returns the empty string.
It is not possible to enumerate the database, nor can you detect the difference between a value that is the empty string and the absence of a value. You can work around this by introducing well-known resource names that list other resources. This trick is used in the next section.
Suppose you want users to be able to define a set of their own buttons for frequently executed commands. Or perhaps users can augment the application with their own Tcl code. The following scheme, which is based on an idea from John LoVerso, lets them define buttons to invoke their own code or their favorite commands.
The application creates a special frame to hold the user-defined buttons and places it appropriately. Assume the frame is created like this:
frame .user -class User
The class specification for the frame means that we can name resources for the widgets inside the frame relative to *User
. Users specify the buttons that go in the frame via a personal file containing resource specifications.
The first problem is that there is no means to enumerate the database, so we must create a resource that lists the names of the user-defined buttons. We use the name buttonlist
and make an entry for *User.buttonlist
that specifies which buttons are being defined. It is possible to use artificial resource names (e.g., buttonlist
), but they must be relative to an existing Tk widget.
Example 31-3. Using resources to specify user-defined buttons
*User.buttonlist: save search justify quit *User.save.text: Save *User.save.command: File_Save *User.search.text: Search *User.search.command: Edit_Search *User.justify.text: Justify *User.justify.command: Edit_Justify *user.quit.text: Quit *User.quit.command: File_Quit *User.quit.background: red
In Example 31-3, we have listed four buttons and specified some of the attributes for each, most importantly the text
and command
attributes. We are assuming, of course, that the application manual publishes a set of commands that users can invoke safely. In this simple example, the commands are all one word, but there is no problem with multiword commands. There is no interpretation done of the value, so it can include references to Tcl variables and nested command calls. Example 31-4 uses these resource specifications to define the buttons:
Example 31-4. Resource_ButtonFrame
defines buttons based on resources
proc Resource_ButtonFrame { f class } { frame $f -class $class -borderwidth 2 pack $f -side top -fill x foreach b [option get $f buttonlist {}] { if [catch {button $f.$b}] { button $f.$b -font fixed } pack $f.$b -side right } }
The catch
phrase is introduced to handle a common problem with fonts and widget creation. If the user's resources specify a bogus or missing font, then the widget creation command will fail. The catch
phrase guards against this case by falling back to the fixed
font, which is guaranteed to exist. This problem is fixed in Tk 8.0 because the font mechanism will search for alternate fonts.
Example 31-5 assumes that the resource specifications from Example 31-2 are in the file button.resources
. It creates the user-defined buttons in the .users
frame.
User-defined menus can be set up with a similar scheme. However, it is more complex because there are no resources for specific menu entries. We must use more artificial resources to emulate this. We use menulist
to name the set of menus. Then, for each of these, we define an entrylist
resource. Finally, for each entry we define a few more resources. The name of the entry has to be combined with some type information, which leads to the following convention:
l_
entry
is the label for the entry.
t_
entry
is the type of the entry.
c_
entry
is the command associated with the entry.
v_
entry
is the variable associated with the entry.
m_
entry
is the menu associated with the entry.
Example 31-6. Specifying menu entries via resources
*User.menulist: stuff *User.stuff.text: My stuff *User.stuff.m.entrylist: keep insert find *User.stuff.m.l_keep: Keep on send *User.stuff.m.t_keep: check *User.stuff.m.v_keep: checkvar *User.stuff.m.l_insert: Insert File... *User.stuff.m.c_insert: InsertFileDialog *User.stuff.m.l_find: Find *User.stuff.m.t_find: cascade *User.stuff.m.m_find: find *User.stuff.m.find.entrylist: next prev *User.stuff.m.find.tearoff: 0 *User.stuff.m.find.l_next: Next *User.stuff.m.find.c_next: Find_Next *User.stuff.m.find.l_prev: Previous *User.stuff.m.find.c_prev: Find_Previous
In Example 31-6, .user.stuff
is a Tk menubutton. It has a menu as its child, .user.stuff.m
, where the menu .m
is set by convention. You will see this later in the code for Resource_Menubar
. The entrylist
for the menu is similar in spirit to the buttonlist
resource. For each entry, however, we have to be a little creative with the next level of resource names. The following does not work:
*User.stuff.m.keep.label: Keep on send
The problem is that Tk does not directly support resources for menu entries, so it assumes .stuff.m.keep
is a widget pathname, but it is not. You can add the resource, but you cannot retrieve it with option get
. Instead, we must combine the attribute information (i.e., label) with the name of the entry:
*User.stuff.m.l_keep: Keep on send
You must do something similar if you want to define resources for items on a canvas, too, because that is not supported directly by Tk. The code to support menu definition by resources is shown in the next example:
Example 31-7. Defining menus from resource specifications
proc Resource_Menubar { f class } { set f [frame $f -class $class] pack $f -side top foreach b [option get $f menulist {}] { set cmd [list menubutton $f.$b -menu $f.$b.m -relief raised] if [catch $cmd t] { eval $cmd {-font fixed} } if [catch {menu $f.$b.m}] { menu $f.$b.m -font fixed } pack $f.$b -side left ResourceMenu $f.$b.m } } proc ResourceMenu { menu } { foreach e [option get $menu entrylist {}] { set l [option get $menu l_$e {}] set c [option get $menu c_$e {}] set v [option get $menu v_$e {}] switch -- [option get $menu t_$e {}] { check { $menu add checkbutton -label $l -command $c -variable $v } radio { $menu add radiobutton -label $l -command $c -variable $v -value $l } separator { $menu add separator } cascade { set sub [option get $menu m_$e {}] if {[string length $sub] != 0} { set submenu [menu $menu.$sub] $menu add cascade -label $l -command $c -menu $submenu ResourceMenu $submenu } } default { $menu add command -label $l -command $c } } } }
The examples presented here are a subset of a package I use in some large applications, exmh and webtk. The applications define nearly every button and menu via resources, so users and site administrators can redefine them. The buttonlist
, menulist
, and entrylist
resources are generalized into user, site, and application lists. The application uses the application lists for the initial configuration. The site and user lists can add and remove widgets. For example:
buttonlist
–. the application list of buttons
l-buttonlist
–. the site-specific list of buttons to remove
lbuttonlist
–. the site-specific list of buttons to add
u-buttonlist
–. the per-user list of buttons to remove
ubuttonlist
–. the per-user list of buttons to add
This idea and the initial implementation was contributed to exmh by Achim Bonet. The Resource_GetFamily
procedure merges five sets of resources shown above. It can replace the option get
commands for the buttonlist
, menulist
, and entrylist
resources in Examples 31-4 and 31-7:
Example 31-8. Resource_GetFamily
merges user and application resources
proc Resource_GetFamily { w resname } { set res [option get $w $resname {}] set lres [option get $w l$resname {}] set ures [option get $w u$resname {}] set l-res [option get $w l-$resname {}] set u-res [option get $w u-$resname {}] # Site-local deletions from application resources set list [lsubtract $res ${l-res}] # Site-local additions set list [concat $list $lres] # Per-user deletions set list [lsubtract $list ${u-res}] # Per-user additions return [concat $list $ures] } proc lsubtract { orig nuke } { # Remove elements in $nuke from $orig foreach x $nuke { set ix [lsearch $orig $x] if {$ix >= 0} { set orig [lreplace $orig $ix $ix] } } return $orig }
If the command resource contains substitution syntax like $
and []
, then these are evaluated later when the command is invoked by the button or menu. This is because there is no interpretation of the command value when the widgets are created. However, it may be that you want variables substituted when the buttons and menus are defined. You can use the subst
command to do this:
set cmd [$button cget -command] $button config -command [subst $cmd]
Choosing the scope for the subst
can be tricky. The previous command does the subst
in the current scope. If this is the Resource_ButtonFrame
procedure, then there are no interesting application-specific variables defined. The next command uses uplevel
to do the subst
in the scope of the caller of Resource_ButtonFrame
. The list
is necessary so that uplevel
preserves the structure of the original subst
command.
$button config -command [uplevel [list subst $cmd]]
If you do a subst
in ResourceMenu
, then you need to keep track of the recursion level to get back to the scope of the caller of Resource_Menubar
. The next few lines show what changes in ResourceMenu
:
proc ResourceMenu { menu {level 1} } { foreach e [option get $menu entrylist {}] { # code omitted set c [option get $menu c_$e {}] set c [uplevel $level [list subst $c]] # And the recursive call is ResourceMenu $submenu [expr $level+1] # more code omitted } }
If you want the subst
to occur in the global scope, use this:
$button config -command [uplevel #0 [list subst $cmd]]
However, the global scope may not be much different when you define the button than when the button is invoked. In practice, I have used subst
to capture variables defined in the procedure that calls Resource_Menubar
.
18.227.111.33