Example 22-3 is a browser for the code examples that appear in this book. The basic idea is to provide a menu that selects the examples, and a text window to display the examples. Before you can use this sample program, you need to edit it to set the proper location of the exsource directory that contains all the example sources from the book. Example 22-4 on page 329 extends the browser with a shell that is used to test the examples.
This example uses the wm minsize command to put a constraint on the minimum size of the window. The arguments specify the minimum width and height. These values can be interpreted in two ways. By default they are pixel values. However, if an internal widget has enabled geometry gridding, then the dimensions are in grid units of that widget. In this case the text widget enables gridding with its setgrid attribute, so the minimum size of the window is set so that the text window is at least 30 characters wide by five lines high:
wm minsize . 30 5
In older versions of Tk, Tk 3.6, gridding also enabled interactive resizing of the window. Interactive resizing is enabled by default in Tk 4.0 and later.
The example uses the browse array to collect its global variables. This makes it simpler to reference the state from inside procedures because only the array needs to be declared global. As the application grows over time and new features are added, that global command won't have to be adjusted. This style also serves to emphasize what variables are important. The browse array holds the name of the example directory (dir), the Tk pathname of the text display (text), and the name of the current file (current). The list and curix elements are used to implement the Next and Previous procedures.
The browser searches the file system to determine what it can display. The tcl_platform(platform) variable is used to select a different example directory on different platforms. You may need to edit the on-line example to match your system. The example uses glob to find all the files in the exsource directory. The file join command is used to create the file name pattern in a platform-independent way. The result of glob is sorted explicitly so the menu entries are in the right order. Each file is read one line at a time with gets, and then regexp is used to scan for keywords. The loop is repeated here for reference:
foreach f [lsort -dictionary [glob [file join $browse(dir) *]]] { if [catch {open $f}in] { puts stderr "Cannot open $f: $in" continue } while {[gets $in line] >= 0} { if [regexp {^# Example ([0-9]+)-([0-9]+)}$line x chap ex] { lappend examples($chap) $ex lappend browse(list) $f # Read example title gets $in line set title($chap-$ex) [string trim $line "# "] set file($chap-$ex) $f close $in break } } }
The example files contain lines like this:
# Example 1-1 # The Hello, World! program
The regexp picks out the example numbers with the ([0-9]+)-([0-9]+) part of the pattern, and these are assigned to the chap and ex variables. The x variable is assigned the value of the whole match, which is more than we are interested in. Once the example number is found, the next line is read to get the description of the example. At the end of the foreach loop the examples array has an element defined for each chapter, and the value of each element is a list of the examples for that chapter.
The values in the examples array are used to build up a cascaded menu structure. First a menubutton is created that will post the main menu. It is associated with the main menu with its menu attribute. The menu must be a child of the menubutton for its display to work properly:
menubutton $f.ex -text Examples -menu $f.ex.m set m [menu $f.ex.m]
There are too many chapters to put them all into one menu. The main menu has a cascade entry for each group of eight chapters. Each of these submenus has a cascade entry for each chapter in the group, and each chapter has a menu of all its examples. Once again, the submenus are defined as a child of their parent menu. Note the inconsistency between menu entries and buttons. Their text is defined with the -label option, not -text. Other than this they are much like buttons. Chapter 27 describes menus in more detail. The code is repeated here:
set limit 8 ; set c 0 ; set i 0 foreach key [lsort -integer [array names examples]] { if {$i == 0} { $m add cascade -label "Chapter $key..." -menu $m.$c set sub1 [menu $m.$c] incr c } set i [expr ($i +1) % $limit] $sub1 add cascade -label "Chapter $key" -menu $sub1.sub$i set sub2 [menu $sub1.sub$i] foreach ex [lsort -integer $examples($key)] { $sub2 add command -label "$key-$ex $title($key-$ex)" -command [list Browse $file($key-$ex)] } }
The Browse procedure is fairly simple. It sets browse(current) to be the name of the file. This changes the main label because of its textvariable attribute that links it to this variable. The state attribute of the text widget is manipulated so that the text is read-only after the text is inserted. You have to set the state to normal before inserting the text; otherwise, the insert has no effect. Here are a few commands from the body of Browse:
global browse set browse(current) [file tail $file] $t config -state normal $t insert end [read $in] $t config -state disabled
18.119.166.90