To demonstrate the ease in which an Fx application can be built, we designed an Fx-based replacement for the original (and now obsolete) xqddb called nxqddb. Nxqddb has all the standard Fx-style features and all the function of the original generic application. The entire nxqddb application is around 300 lines of Tcl code.
The generic application nxqddb builds its interface from the
format of the Schema. The Qddb Tcl command qddb_schema print
returns a list structure in a form similar to the Schema. This list
can be traversed to build an interface similar in appearance to the
Schema. Figure 8.1 shows the main Tcl procedure (NxqddbSetup
) used
by nxqddb to build the generic interface. NxqddbSetup
is called in the
following way:
NxqddbSetup .c.f $s [qddb_schema print $s] "" 0The Tk window path
.c.f
refers to a frame within a scrollable canvas.
$s refers to the schema returned by a call to qddb_schema open
.
Figure 8.1: Tcl procedure for building nxqddb(1) screen
NxqddbSetup
recursively traverses the list returned by the call
to [qddb_schema print $s]
, building Fx_Frame
s for all non-leaf
attributes and Fx_Entry
s for leaf attributes. The buttons for
expandable attributes are automatically included in both Fx_Frame
s and
Fx_Entry
s. The Tk frames and entries are repacked after the calls to
the Fx routines to give the application the look and feel of the original xqddb.
nxqddb(1) requires several features not available from the standard Fx toolkit:
To perform vertical scrolling of the Fx widgets, you must first build
the Fx_Menubar
, then a canvas and scrollbar. Place a frame inside the canvas in
which to place the Fx widgets:
set fx_config_dir .nxqddb_config if {[catch "qddb_schema open [lindex argv 0]" s] != 0} { puts "Cannot open schema for relation: [lindex argv 0]" exit 1 } Fx_Menubar menubar -w .mb -schema $s -array gv_myattr \ -config_dir .nxqddb_config set search_for_entry [menubar SearchForEntry] scrollbar .s -command {.c yview} pack .s -side right -expand on -fill y canvas .c -yscroll {.s set} pack .c -side left -expand on -fill both frame .c.f pack .c.f -side top -expand on -fill both .c create window 0 0 -anchor nw -window .c.f set gv_init 0 NxqddbSetup .c.f $s [qddb_schema print $s] "" 0 update idletasks set scrht [winfo screenheight .] set scrwid [winfo screenwidth .] incr scrwid -50 XqddbReconfigure bind . <Configure> { global fx_config set geo [split [wm geometry .] +] set x [lindex $geo 1] set y [lindex $geo 2] set fx_config(maingeom) [list %w %h $x $y] .c yview 0 } menubar configure -instances [Fx_Entry :: GetInstances] \ -frames [Fx_Frame :: GetInstances] # the following is a trick that knows 'this' will be a local # variable when the list is evaluated. Fx_Entry :: AfterReconfigure {XqddbReconfigure [$this GetEntry] 0} Fx_Menubar :: AfterReconfigure {XqddbReconfigure} Fx_Entry :: ScrollbarSide left menubar SearchModeProc
This code first opens the requested relation with qddb_schema
open
, then builds the menubar, a scrollbar, a canvas, and a frame to
place within the canvas. It then calls NxqddbSetup
, which
recursively traverses the schema to build Fx frames and entries. Next,
the code fixes the canvas width to the size of the frame built by
NxqddbSetup
by calling XqddbReconfigure
(shown below).
Finally, it informs Fx_Entry
and Fx_Menubar
that after any
configuration, XqddbReconfigure
needs to be called to resize the
canvas and toplevel window. XqddbReconfigure
looks like this:
proc XqddbReconfigure {{widg ""} {withdraw 1}} { global scrwid scrht fx_config if {$withdraw} { wm withdraw . } if {[string compare $widg ""] != 0} { pack $widg -expand off -fill none } wm minsize . $scrwid [expr $scrht / 10]; wm maxsize . $scrwid $scrht update update idletasks set wid [winfo reqwidth .c.f] set ht [winfo reqheight .c.f] .c configure -width $wid -height $ht .c configure -scrollregion [list 0 0 $wid $ht] update update idletasks set reqwid [winfo reqwidth .]; set reqht [winfo reqheight .] wm minsize . $reqwid [expr int(($reqht - $ht + $ht)/10)] wm maxsize . $reqwid [min $reqht $scrht] if {[info exists fx_config(maingeom)] && \ [lindex $fx_config(maingeom) 1] < [min $reqht $scrht]} { wm geometry . \ ${reqwid}x[lindex $fx_config(maingeom) 1]\ +[lindex $fx_config(maingeom) 2]+[lindex $fx_config(maingeom) 3] } else { wm geometry . ${reqwid}x[min $reqht $scrht] if {[info exists fx_config(maingeom)]} { wm geometry . +[lindex $fx_config(maingeom) 2]\ +[lindex $fx_config(maingeom) 3] } } if {$withdraw} { wm deiconify . } update update idletasks set geo [split [wm geom .] +] set l2 [lindex $geo 0] set l2 [split $l2 x] set fx_config(maingeom) [concat $l2 [lrange $geo 1 2]] }
Because of the general layout of nxqddb's main screen, entries
must be of similar widths for aesthetic reasons. A global Tcl variable
called fx_config(entry_widths)
stores the fixed width of all
entries. Whenever the contents of fx_config(entry_widths)
change, nxqddb must reconfigure all the Entry and Text widgets.
The following routine accomplishes this task:
proc BindConfigure {} { bind . <Configure> { if {[string compare %W .] != 0} { break } set geo [split [wm geometry .] +] set x [lindex $geo 1] set y [lindex $geo 2] set fx_config(maingeom) [list %w %h $x $y] .c yview scroll 0 units } } proc ReconfigureEntryWidths {} { global fx_config bind . <Configure> { } wm withdraw . foreach i [Fx_Entry :: GetInstances] { $i configure -width $fx_config(entry_width) } update XqddbReconfigure BindConfigure }
Cascaded menu options configure some pre-defined entry widths:
.mb.config.menu add separator .mb.config.menu add cascade -label "Entry Widths" \ -menu .mb.config.menu.entrywidth -underline 0 .mb.config.menu.entrywidth add radiobutton \ -variable fx_config(entry_width) -value 10 -label 10 \ -command ReconfigureEntryWidths .mb.config.menu.entrywidth add radiobutton \ -variable fx_config(entry_width) -value 20 -label 20 \ -command ReconfigureEntryWidths .mb.config.menu.entrywidth add radiobutton \ -variable fx_config(entry_width) -value 30 -label 30 \ -command ReconfigureEntryWidths
The source for nxqddb will show you further tutorial information on building generic screens.