Pragana's Tcl Guide

Old notes

A simple input dialog

The tk library include some commands generally useful as tk_getOpenFile, tk_getSaveFile, tk_messageBox, tk_chooseColor and tk_dialog. Sometimes though, we need a simple text entry to our program, that's why this inputbox arrived. Most of this code has been stolen from tk_dialog (that's the way open source works, isn't it?).
The idea is to open a new toplevel with a message, an entry where the user input our answer, and buttons to finish or cancel our result. This dialog will return the string got from the user or a null string if the cancel button was hit. Let's see how we can do that.

First, we destroy a toplevel with the same name as given for our inputbox, if it exists. The argument default will be used for setting in advance a value to our entry widget. Next, we create the toplevel for our inputbox, giving it the class "Inputbox", icon name, and make sure any WM_DELETE_WINDOW messages from the window manager will be honored (doing nothing). The global frmReturn will store the return value of the procedure. It needs to be global because of the way widget bindings work.

proc inputbox {w text {default {}}} {
    global frmReturn
    catch {destroy $w}
    toplevel $w -class Inputbox
    wm iconname $w Inputbox
    wm protocol $w WM_DELETE_WINDOW { }

Then we create a frame for the top and bottom regions of the inputbox. The first will contain the message and the input entry. The other will have the buttons with commands for setting the frmReturn variable with the entry value or a null string.

    wm transient $w 1
    pack [frame $ -relief raised -bd 1] -side bottom -fill both
    pack [frame $ -relief raised -bd 1] -side top -fill both -expand 1
    option add *Inputbox.msg.wrapLength 3i widgetDefault
    label $w.msg -justify left -text $text -font {Times 18}
    entry $w.entry
    pack $w.msg -in $ -side top -expand 1 -fill both -padx 3m -pady 3m
    pack $w.entry -in $ -side top -expand 1 -fill x -padx 3m -pady 3m
    grid [button $w.b0 -text ok -command "set frmReturn \[$w.entry get\]"] \
 	-in $ -column 0 -row 0 -stick sw -padx 10
    grid [button $w.b1 -text cancel -command {set frmReturn ""}] \
    	-in $ -column 1 -row 0 -stick sw -padx 10

If a default value is given we insert it at the entry. We put <Return> as a binding to the entry, so the user has only to type what he wants and press the carriage return key to finish. Next, we calculate the center of the screen, save the current focus and grab status and make our settings. Then we remain waiting for a frmReturn variable change.

    $w.entry insert end $default
    bind $w.entry <Return> "$w.b0 invoke"
    bind $w <Destroy> {set frmReturn {}}
    wm withdraw $w
    update idletasks
    set x [expr [winfo screenwidth $w]/2 - [winfo reqwidth $w]/2\
        - [winfo vrootx [winfo parent $w]]]
    set y [expr [winfo screenheight $w]/2 - [winfo reqheight $w]/2\
        - [winfo vrooty [winfo parent $w]]]
    wm geometry $w +$x+$y
    wm deiconify $w
    set oldfocus [focus]
    set oldgrab [grab current $w]
    if {$oldgrab != ""} {
        set grabstatus [grab status $oldgrab]
    grab $w
    focus $w.entry
    tkwait variable frmReturn

When the variable was modified, we arrive here, at the last part of the procedure. We have only to restore the old focus and grab status and return the string stored in frmReturn.

    catch {focus $oldfocus}
    catch {
        bind $w Destroy {}
        destroy $w
    if {$oldgrab != ""} {
        if {$grabstatus == "global"} {
            grab -global $oldgrab
        } else {
            grab $oldgrab
    return $frmReturn

The simple commands below show how to test it:

set name [inputbox .x "Please input your name:"]
puts "Hello $name, nice to meet you!"

Back Home