Tcl/Tk - Custom Dialog

Tcl/Tk - Custom Dialog

How to create a custom dialogue window using Tcl/Tk

The following code demonstrates how to create a simple custom dialogue window using Tcl/Tk. This is an embellishment to the code presented in Practical Programming in Tcl and Tk - 4th Edition, chapter 39, pages 602-609.

Book Errata for 4th Edition: beedub.com/book/4th/Errata1.html Note: For performance reasons, preference is given to map and unmap windows instead of destroying them. Reference chapter 44, page 661 that captures the tricks used here.

This module can run independently


package require Tk

proc Dialog_Create {top title width height xScreenPos yScreenPos args} {
    global dialog
    set winexist [winfo exists $top]

    if [ winfo exists $top ] {
        switch -- [wm state $top] {
            normal -
            zoomed {
                raise $top
            }
            withdrawn -
            iconic {
                wm deiconify $top
                catch {wm geometry $top $::dialog(geo,$top)}
            }
        }
        return 0
    } else {
        eval {toplevel $top} $args
        wm title $top $title
        puts "Dialog_Create Window Geometry:  [wm geometry $top]"
        wm resizable $top 1 0
        return 1
    }
}

proc Dialog_Wait {top varName { focus {} }} {
    upvar $varName var

    bind $top <Destroy> [list set $varName 0]

    if { [ string length $focus ] == 0 } {
        set focus $top
    }
    set old [focus -displayof $top]
    focus $focus

    catch {tkwait visibility $top}
    catch {grab $top}

    tkwait variable $varName
    catch {grab release $top}
    focus $old
}

proc Dialog_Dismiss {top} {
    global dialog
    catch {
        set ::dialog(geo,$top) [list [wm geometry $top]]
        wm withdraw $top
    }
}

proc verifyEntry {} {
    global signIn

    puts "Entered validateEntry procedure"
    puts "String $::signIn(username)\nLength:  [string length $::signIn(username)]"
    puts "String $::signIn(password)\nLength:  [string length $::signIn(password)]"

    if { [string length $::signIn(username)] > 0 && [string length $::signIn(password)] > 0} {
        puts "Entry validated"
        set ::signIn(ok) 1
    } else {
        puts "Entry error:  Zero length entries.\nSignIn(ok) = $::signIn(ok)"
    }
}

proc validateEntry {} {
    puts "Entered proc validateEntry "
    puts "Exiting proc validateEntry "
}

proc Dialog_Manager { username password width height xpos ypos } {
    global signIn

    set f .prompt
    if { [Dialog_Create $f "Sign In" $width $height $xpos $ypos -borderwidth 10 ] } {

        labelframe $f.userframe -text $username
        label $f.userframe.msguser -text $username
        entry $f.userframe.username -textvariable ::signIn(username); #-validate validateEntry
        pack $f.userframe.username -side top -padx 4 -expand yes -fill x
        pack $f.userframe.msguser -side left -padx 4 -anchor w

        labelframe $f.passframe -text $password
        label $f.passframe.msgpass -text $password
        entry $f.passframe.password -show "*" -textvariable ::signIn(password); #-validate validateEntry
        pack $f.passframe.password -side top -padx 4 -expand yes -fill x
        pack $f.passframe.msgpass -side top -anchor w

        set b [frame $f.buttons]
        button $b.signin -text "Sign In" -command { verifyEntry }
        button $b.cancel -text Cancel -command {set ::signIn(ok) 0}

        pack $b.signin -side left -ipadx 5 -padx 20 -pady 5
        pack $b.cancel -side right -ipadx 5 -padx 20 -pady 5

        pack $f.buttons -side bottom -expand yes -padx 5 -pady 5
        pack $f.userframe $f.passframe -side top -expand no -fill x -anchor n -padx 5 -pady 5

        update idletasks
        puts "Dialog_Manager AFTER packing."
        puts "Dialog_Manager is $f Mapped? (0=No, 1=Yes):  [winfo ismapped ${f}]"
        puts "Dialog Manager - winfo reqwidth:  [winfo reqwidth $f]"
        puts "Dialog_Manager - winfo reqheight:  [winfo reqheight $f]"

        wm minsize $f $width [winfo reqheight $f]

        bind $f.userframe.username <Return> { verifyEntry }
        bind $f.userframe.username <Control-c> {set ::signIn(ok) 0 ; break }

        bind $f.passframe.password <Return> { verifyEntryEntry }
        bind $f.passframe.password <Control-c> {set ::signIn(ok) 0 ; break }
    }

    set ::signIn(ok) 0
    set ::signIn(username) ""
    set ::signIn(password) ""
    set ::signIn(result) ""

    bell -displayof .prompt -nice

    Dialog_Wait $f ::signIn(ok) $f.userframe.username
    Dialog_Dismiss $f
    if { $::signIn(ok) } {
        return [list $::signIn(username) $::signIn(password)]
    } else {
        return {}
    }
}

proc signIn {} {
    Dialog_Manager "Username" "Password" "300" "250" "50" "50"
    puts "UserName = $::signIn(username)"
    puts "PassWord = $::signIn(password)"
}

label .lblWelcome -text "Welcome to TclTk.ca\n\nSimple Custom Dialogue Demonstration"

frame .btnFrame
button .btnFrame.btnSignIn -text "Launch" -command { signIn }
button .btnFrame.btnExit -text "Exit/Cancel" -command exit
pack .btnFrame.btnSignIn -side left -ipadx 5 -padx 20
pack .btnFrame.btnExit -side right -ipadx 5 -padx 20

pack .btnFrame -side bottom -expand no -pady 10
pack .lblWelcome -side top -pady 10 -expand yes -fill x

wm geometry . 400x155+100+100
wm title . "Tcl/Tk Custom Dialogue"

This code will be refined in future posts to add real verification

Did you find this article valuable?

Support Redge Shepherd by becoming a sponsor. Any amount is appreciated!