next up previous contents
Next: References Up: 8 Designing Custom Qddb Previous: 8.4 Implementing custom features

8.5 The design and implementation of nxqddb(1)

  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.

8.5.1 Building a generic screen with Fx

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] "" 0
The 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.

  figure1097
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_Frames for all non-leaf attributes and Fx_Entrys for leaf attributes. The buttons for expandable attributes are automatically included in both Fx_Frames and Fx_Entrys. 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.

8.5.2 Implementing nxqddb's look and feel

nxqddb(1) requires several features not available from the standard Fx toolkit:

  1. Vertical scrolling of all the entries
  2. Resizable windows
  3. Fixed-width entry boxes and configuration menu
This section discusses the implementation of each.

Vertical scrolling and resizable windows

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]]
}

Fixed-width entry boxes

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.


next up previous contents
Next: References Up: 8 Designing Custom Qddb Previous: 8.4 Implementing custom features

Herrin Software Development, Inc.