Chapter 12. Entry and Spinbox Widgets

So far, all I’ve shown you are the read-only widgets such as labels and frames. This is the first of several chapters that discusses Tk’s widgets that permit entering text. This chapter introduces the entry and spinbox widgets. Tk’s entry widget is a specialized type of text-entry field best-suited to high-speed, head-down data entry but applicable for many types of data entry in which you want to control or validate the data that is input. It displays one line of text that you can edit (or not), subject to restrictions you set using widget-specific attributes and a validation routine that you write. The spinbox widget, often referred to as a spinner in other GUI toolkits, is based on the entry widget. In addition to enabling text entry in the manner of entry widgets, spinboxes can also scroll, or spin, through a fixed set of values from which you select the desired value (hence their name). The reason I’m covering these two special-purpose text widgets before the all-purpose text widget (see Chapter 14) is that the entry and spinbox widgets support a subset of the features and functionality of the more general text widget. As a result, when you get to Chapter 14, you can build on what you already know.

Mad Libs Revista

Mad Libs Revista rewrites the Mad Libs program from Chapter 4 to use entry widgets to create a mad lib silly sentence. To start the game, execute the script g_mad_lib.tcl in this chapter’s code directory. Figures 12.1, 12.2, and 12.3 show the game in progress.

g_mad_lib.tcl’s opening window.

Figure 12.1. g_mad_lib.tcl’s opening window.

Type the requested parts of speech to play the game.

Figure 12.2. Type the requested parts of speech to play the game.

Click the Replace button to see the completed sentence.

Figure 12.3. Click the Replace button to see the completed sentence.

Features of the Entry and Spinbox Widgets

The entry and spinbox widgets are very similar. They share many of the same features, attributes, and options. In fact, the spinbox widget is derived from the entry widget. Obviously, they have different behaviors and appearances. In the following discussion, I lump entry and spinbox widgets together to avoid having to repeat myself, but I’ll point out where the two widgets differ when necessary.

The entry widget displays a one-line, editable text string. By default, an entry’s string is empty. You can select all or part of an entry widget’s contents using the mouse, keyboard, or pro-grammatically using widget attributes and commands. If the text in an entry widget is too long to fit entirely within the widget’s window, only part of the text will be displayed. This much probably doesn’t surprise you. What you might not realize, though, is that you can change the view to display different portions of the text. A spinbox widget has all of the features of an entry widget plus the ability to allow users to spin through a fixed set of values (such as times or dates).

Entry and Spinbox Attributes

In addition to the standard options and attributes entry and spinbox widgets support, they have specific characteristics that I want to highlight (see Table 12.1).

Table 12.1. entry and spinbox Attributes

Attribute

Widget

Description

S: spinbutton only; E: entry only; B: both

-buttonbackground

S

Background color of the spin buttons themselves.

-buttoncursor

S

Cursor displayed when the mouse pointer hovers over the spin buttons themselves.

-buttondownrelief

S

Relief used for a depressed spin button.

-buttonuprelief

S

Relief used for a raised spin button.

-command

S

Specifies the Tcl command to execute when the spinbutton is invoked.

-exportselecton

B

If true, selected text also becomes the X selection.

-format

B

Defines the format string used when setting the string value (used with -from, -to, and -increment). This must be a format specifier of the form %<pad>.<pad>f, as it will format a floating-point number.

-from

S

Sets the lowest value for a spinbox (in floating point format); used with -to and -increment; -values overrides this setting.

-insertbackground

B

Background color of the insertion area.

-insertborderwidth

B

Width of the border of the insertion area.

-insertofftime

B

If non-zero, the cursor blinks, and this value defines the length of the off portion of the blink cycle in milliseconds.

-insertontime

B

If non-zero, the cursor blinks, and this value defines the length of the on portion of the blink cycle in milliseconds.

-insertwidth

B

Specifies the width in pixels of the insertion cursor.

-invalidcommand, -invcmd

B

Specifies the script to execute if -validatecommand returns 0.

-increment

S

Sets the increment interval between -from and -to.

-readonlybackground

S

Defines the background color when the widget is read-only; defaults to the normal background color.

-selectbackground

B

The background color of selected text.

-selectborderwidth

B

The width of the border around selected text.

-selectforeground

B

The foreground color of selected text.

-show

E

Masks the contents of the entry widget with the specified character, such as *.

-takefocus

B

Controls whether or not the widget accepts focus when using keyboard traversal (using Tab and Shift-Tab). A value of 0 skips the widget, a value of 1 includes the widget, and empty string leaves the decision up to the traversal scripts.

-textvariable

B

The name of a variable whose value is linked to the -text attribute; updates to this variable are reflected immediately in the -text attribute.

-to

B

Sets the highest value for a spinbox; used with -from and -increment; -values overrides this setting.

-validate

B

Determines the mode in which validation should operate; must be one of none (the default), focus, focusin, focusout, key, or all.

-validatecommand, -vcmd

B

Specifies the script to execute for validating input in the widget.

-values

B

Defines list of valid values for the widget’s string; overrides -from, -to, and -increment.

-wrap

S

If true, values in a spinbox will roll over (wrap) at the bottom and top of the defined range.

-xscrollcommand

B

Connects the widget to a procedure to use for scrolling the widget’s viewable area horizontally.

Many commands for the entry and spinbox widgets accept index arguments. An index argument defines a particular character in the widget’s string value. These values are usually used in the context of selecting text or to refer to a portion of a widget’s text value that is already selected. You can use one of the following index arguments (out-of-range indices round to the nearest legal value):

  • anchor—. Sets the anchor point for the selection, which is set with the select from and select adjust widget commands.

  • end—. Represents the character immediately following the last one in the widget’s string.

  • insert—. Represents the character immediately after the insertion point.

  • sel.first—. Represents the first selected character.

  • sel.last—. Represents the last selected character.

  • numberSpecifies the character as a zero-based numeric index.

  • @numberSpecifies number as an x-coordinate in the widget’s window. The character that spans the specified coordinate will be used. For example, @0 corresponds to the left-most character.

Validating User Input

To use the input validation capabilities of entry and spinbox widgets, set the -validate attribute to one of the values shown in Table 12.2 and set the -validatecommand attribute to a script or procedure that validates the text (by default, validation is disabled). If the validation script returns 1, true, or another valid Tcl Boolean value, the changes to the widget’s text will be accepted. Otherwise, the changes will be ignored, and the widget will not accept or show the change.

Table 12.2. Options for the validate Attribute

Option

Description

all

Executes the validation script for all conditions.

focus

Executes the validation script when the widget receives or loses focus.

focusin

Executes the validation script when the widget receives focus.

focusout

Executes the validation script when the widget loses focus.

key

Executes the validation script when the widget is edited.

none

No validation occurs (default).

It is possible to perform percent substitutions on the -validatecommand and -invalidcommand, just as you would in a bind script. Tk recognizes substitutions shown in Table 12.3.

Table 12.3. Percent Substitutions for Validation Scripts

Substitution

Description

%d

Validation type (0 for delete, 1 for insert, -1 for focus, forced or textvariable validation).

%i

Index of the character inserted/deleted, if any;, -1 otherwise.

%P

The widget’s value after the edit.

%s

The widget’s value before the edit.

%S

The text string being inserted/deleted, if any;, otherwise an empty string.

%v

The current validation mode (see Table 12.3).

%V

The validation mode that triggered the callback (key, focusin, focusout, forced).

%W

The widget’s name.

Mixing -textvariable and -validatecommand might cause unpleasant results. If you use -textvariable to set the value of entry and spinbox widgets for read-only purposes, there should be no problems. Problems occur when you try to set -textvariable to a value that your validation command (which is controlled by the -validatecommand). The issue is that setting -textvariable to an invalid value causes the -validate attribute to be reset to none, which means that your -invalidcommand script will not be triggered. Why does validate get set to none? To prevent an infinite loop: setting -textvariable to an invalid value causes -invalidcommand to execute, which might set -textvariable to an invalid value, which causes -invalidcommand to execute. Disabling validation avoids the possibility of the infinite loop.

Likewise, if an error occurs in the -validatecommand or -invalidcommand while evaluating their respective scripts (or if -validatecommand does not return a valid Tcl boolean value), then validation will be disabled. In addition, if you edit the widget’s value inside either the -validatecommand or -invalidcommand scripts, validation will be disabled. Again, one reason this occurs is to prevent infinite loops. In addition, editing the widget’s value during validation overrides the edit that was being validated.

Tip: If You Must Edit During Validation

Tip: If You Must Edit During Validation

If you absolutely must edit a widget’s value during validation and want to ensure that the -validate option remains set, include the following command in your validation script:

%W config -validate %v

This command reenables validation. However, your best course of action is to avoid edits during validation and code your scripts to edit widgets after validation.

Finally, avoid setting an associated -textVariable during validation, because doing so causes the widget to get out of sync with its associated variable.

Building a Better Message Box

In Chapter 11, I introduced the toplevel widget and used it to create a crude but effective message box. However, Tk comes with a very nice and easy-to-use message box, created with the tk_messageBox command. Its general syntax is:

tk_messageBox ?opt val? ?...?

The icons, text, and buttons displayed in the message box depend on the options you specify in opt and val. Table 12.4 shows the options that tk_messageBox supports.

Table 12.4. Options for tk_messageBox

Option

Description

-icon img

Defines the icon type to display in the message box.

-message str

Specifies the message text to display.

-parent win

Sets the message box’s parent window to win; the message box appears over win.

-title str

Defines the title that appears in the title bar.

-type type

Specifies the set of buttons to display in the message box.

When you click one of the buttons in the window, the window closes and returns a text string corresponding to the clicked button. The -type attribute determines the buttons that appear in the window. You can use one of the following values for -type:

  • abortretryignoreDisplays Abort, Retry, and Ignore buttons, which return abort, retry, or ignore, respectively, when pressed.

  • okDisplays an OK button, which returns ok when pressed.

  • okcancelDisplays OK and Cancel buttons, which return ok or cancel, respectively, when pressed.

  • retrycancelDisplays Retry and Cancel buttons, which return retry or cancel, respectively, when pressed.

  • yesnoDisplays Yes and No buttons, which return yes or no, respectively, when pressed.

  • yesnocancelDisplays Yes, No, and Cancel buttons, which return yes, no, or cancel, respectively, when pressed.

This chapter’s game uses tk_messageBox, so please refer to g_mad_lib.tcl for an example of using this very useful command.

Analyzing Mad Libs Revista

g_mad_lib.tcl is a classic demonstration that turning a simple, text-based program into a graphical program is rarely a simple “rewrite” at all. Rather, it is a completely new program. In this case, the only element of my original Mad Libs program that survived the rewrite is the source sentence and the code for extracting the text that needed to be replaced.

Looking at the Code

#!/usr/bin/wish
# g_mad_lib.tcl
# Demonstrate the entry widget

# Block 1
# Validation command
proc NotEmpty {val} {
    if {$val != {}} {
        return true
    } else {
        tk_messageBox -icon error -type ok -parent . 
            -message "Replacement word or phrase cannot be empty!"
        return false
    }
}

# Block 2
# Find all of the prompts in the text
proc FindPrompts {source} {
    global prompts inputs

    set i 0
    set j 0
    while {[string first "?" $source $j] != -1} {
        set start [string first "?" $source $j]
        set end [string first "?" $source [expr $start + 1]]
        set j [expr $end + 1];
        set prompt [string range $source [expr $start + 1] 
            [expr $end - 1]]
        lappend prompts [label .l$i -text [string totitle $prompt]]
        lappend inputs [entry .e$i -bg white -validate focusout 
            -vcmd {NotEmpty %P}]
        incr i
    }
}

# Block 3
# Draw the game prompts and entry boxes
proc DisplayPrompts {prompts inputs} {
    set len [llength $prompts]
    for {set i 0} {$i < $len} {incr i} {
        grid [lindex $prompts $i] [lindex $inputs $i] -sticky w
    }
    button .bShow -text "Replace" -command ShowMadLib
    button .bExit -text "Exit" -command exit
    grid .bShow .bExit -padx 5 -pady {20 5} -sticky e
}

# Block 4
# Create a list of replacement phrases from the player's input
proc GetFields {} {
    global inputs

    foreach input $inputs {
        lappend fields [$input get]
    }
    return $fields
}

# Block 5
# Build and display the completed mad lib
proc ShowMadLib {} {
    global line

    foreach field [GetFields] {
        set start [string first "?" $line]
        set end [string first "?" $line [expr $start + 1]]
        set line [string replace $line $start $end $field]
    }
    toplevel .w
    message .w.m -text $line
    grid .w.m
}

# Block 6
# The source sentence
set line "One day while I was ?verb ending in -ing? in my living room, "
append line "a ?adjective? ?mythical creature? fell through the roof. "
append line "It jumped on the ?piece of furniture? and knocked over the "
append line "?noun?. Then it ran into the dining room and ?past tense verb? "
append line "a ?noun?. After ?number? minutes of chasing it through the "
append line "house I finally caught it and put it outside. It quickly "
append line "flew away."

# List variables to contain the prompts and the player's input
set prompts {}
set inputs {}

# Parse the source sentence
FindPrompts $line

# Display the game window
DisplayPrompts $prompts $inputs

Understanding the Code

As usual, g_mad_lib.tcl begins with procedure definitions. Block 1 defines my validation routine, NotEmpty. It accepts a single argument, the text string the player types in an entry widget. If the text is not empty, NotEmpty returns true. Otherwise, I display a message box complaining that the field cannot be empty and then return false.

Block 2 defines the FindPrompts procedure, which accepts a string argument, $source, that stores the sentence to parse. First, I declare two global variables, $prompts and $inputs. $prompts is a list variable containing labels (actually, variable references to label widgets) that display the prompts for the items the player must provide. $inputs stores variable references to the entry widgets in which the player types.

Next, I create two counter variables, $i and $j. $i increments by one each iteration of the loop; I use it to create sequentially numbered label and entry widgets. $j helps me keep track of my current position in the $source string, as I will explain shortly.

As with the original Mad Lib program, I use a while loop to iterate through the string, using the string first command to locate text situated between pairs of question marks. The algorithm I use in g_mad_lib.tcl is slightly different. Whereas in mad_lib.tcl I replaced text onthe-fly as I parsed the source sentence, in g_mad_lib.tcl all I want to do is extract the prompts; I perform the replacements in a separate statement. As a result, I need to keep track of the position of the second or closing question mark so I can start the search for the next prompt at that position. The method I use is the following:

  1. Search for the first or opening ?, starting from the index stored in $j, which is 0 for the first iteration of the loop. Store this value in the variable $start.

  2. Search for the second or closing ?, starting at the character index immediately following the index stored in $start. Store this value in the variable $end.

  3. Set $j to the character index immediately following the index stored in $end. The next search will start from this index in the string.

  4. Extract the text between the two ?s and store it in the variable $prompt.

  5. Create a label widget using the value of $prompt and append a variable reference to the newly created label widget to the list variable $prompts. I could have combined Steps 4 and 5, but I didn’t want a line of code extending over several lines. Breaking it up into two operations also makes the code easier to read. Otherwise, it would have looked something like the following:

    lappend prompts [label .l$i -text [string totitle 
       [string range $source [expr $start + 1] [expr $end • 1]]]]
  6. Create an entry widget corresponding to the label widget created in Step 5 and append a variable reference to the newly created widget to the list variable $inputs. I set the -validate attribute to focusout and -vcmd to NotEmpty %P. This means that when input focus leaves the entry widget, the NotEmpty procedure will be called with the value of the edited text in the widget.

  7. Increment the value of $i.

  8. Repeat Steps 1–7 until no more ?s are found in $source, at which point the loop and the procedure terminate.

Block 3 consists of the DisplayPrompts procedure. It creates the game window shown in Figures 12.1, 12.2, and 12.3 at the beginning of this chapter. DisplayPrompts accepts two arguments, the $prompts and $inputs variables populated by the FindPrompts procedure. After determining the number of elements in the $prompts list, I use a for loop to iterate through both $prompts and $inputs, laying out rows that contain a label widget and its associated entry widget and sticking them to the west or left side of the parent (root) window. Next, DisplayPrompts creates two buttons, .bShow, which invokes the ShowMadLib procedure, and .bExit, which exits the program. These buttons appear on their own row.

The GetFields procedure in Block 4 iterates through the entry widgets, extracting their text strings and appending the extracted values to the $fields list variable. It returns the completed list of fields to the calling procedure, which is ShowMadLib, defined in Block 5. ShowMadLib, in turn, iterates through the source sentence a second time, this time replacing text between ?s with the player-provided input. After completing the string replacements, I create a new toplevel window, embed a message widget in it containing the modified sentence, and display the completed Mad Lib.

Modifying the Code

Here are some exercises you can try to practice what you learned in this chapter:

  1. As it is, you can click the Replace button in g_mad_lib.tcl without providing any input in the entry widgets. Modify g_mad_lib.tcl to require all entry widgets to have valid text.

  2. Modify g_lib_mad.tcl to read its source sentence from a file rather than using a hard-coded sentence.

  3. The validation scheme in g_mad_lib.tcl has the annoying side effect of not allowing the player to click in one entry widget and then click in a second one without entering text. If you change your mind this way, the validation routine nags you with a message box telling you that the first entry is still empty. Fix the validation routine.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.73.207