• I am failing to get a hide/show dialog to show more than once

    From Mark Summerfield@mark@qtrac.eu to comp.lang.tcl on Wed Jun 26 09:59:48 2024
    From Newsgroup: comp.lang.tcl

    I've made a cut-down example shown below.
    If I click the Options button the Options dialog shows as it should.
    And if I click the Options dialog's Close button it is hidden.
    But if I click the Options button a second time I get the error:

    bad window path name ".optionsForm"

    Clearly I've made a mistake, but I don't know Tcl/Tk well enough to see
    what I've done wrong.

    #!/usr/bin/env wish9

    package require tepam

    tk scaling 1.67
    tk appname HideShow

    proc main {} {
    wm withdraw .
    wm title . [tk appname]
    wm attributes . -type dialog
    wm minsize . 200 200
    make_widgets
    wm deiconify .
    raise .
    }

    namespace eval test {
    variable optionsButton
    variable optionsForm
    }

    proc make_widgets {} {
    set test::optionsButton [ttk::button .optionsButton -text Options… \
    -underline 0 -command test::on_options]
    pack .optionsButton
    bind . <Alt-o> {$test::::optionsButton invoke}
    bind . <Escape> {destroy .}
    }

    # Hide/show dialog
    proc test::on_options {} {
    if {![info exists ::test::optionsForm]} {
    puts "on_options init"
    set ::test::optionsForm [toplevel .optionsForm]
    wm title .optionsForm "Options — [tk appname]"
    ttk::label .optionsForm.label -text "Options go here"
    grid .optionsForm.label -row 0 -column 0
    ttk::button .optionsForm.closeButton -text Close \
    -command {destroy $::test::optionsForm}
    grid .optionsForm.closeButton -row 1 -column 0
    dialog prepare .optionsForm
    wm protocol .optionsForm WM_DELETE_WINDOW \
    {dialog hide $::test::optionsForm}
    bind .optionsForm <Escape> {destroy $::test::optionsForm}
    }
    puts on_options
    dialog show .optionsForm
    }

    namespace eval dialog {}

    tepam::procedure {dialog prepare} {
    -named_arguments_first 0
    -args {
    {-modeless -type none}
    {-parent -default .}
    {window}
    }
    } {
    wm withdraw $window
    wm attributes $window -type dialog
    if {!$modeless} { wm transient $window $parent }
    }

    tepam::procedure {dialog show} {
    -named_arguments_first 0
    -args {
    {-modeless -type none}
    {-focuswidget -default ""}
    {window}
    }
    } {
    puts "dialog show $window"
    wm deiconify $window
    raise $window
    focus $window
    if {$focuswidget ne ""} {focus $focuswidget}
    if {!$modeless} {
    catch {grab set $window}
    catch {tkwait visibility $window}
    }
    }

    tepam::procedure {dialog hide} {
    -args { {window} }
    } {
    wm withdraw $window
    grab release $window
    }

    main
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Ralf Fassel@ralfixx@gmx.de to comp.lang.tcl on Wed Jun 26 12:23:13 2024
    From Newsgroup: comp.lang.tcl

    * Mark Summerfield <mark@qtrac.eu>
    | I've made a cut-down example shown below.
    | If I click the Options button the Options dialog shows as it should.
    | And if I click the Options dialog's Close button it is hidden.

    No, it's not, it is destroyed (as you requested by)

    | ttk::button .optionsForm.closeButton -text Close \
    | -command {destroy $::test::optionsForm}

    Part of the problem is that you only check for the existence of the
    variable, but not of the window it describes. The second time you call test::on_options, the variable exists, but the window does not since it
    was destroyed by the closeButton.

    | # Hide/show dialog
    | proc test::on_options {} {
    | if {![info exists ::test::optionsForm]} {

    I suggest to add
    || ![winfo exists $::test::optionsForm]

    in test::on_options (and probably to unify all the different ways to withdraw/destroy the window - Close button, WM_DELETE, Escape).

    HTH
    R'
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Mark Summerfield@mark@qtrac.eu to comp.lang.tcl on Thu Jun 27 06:31:30 2024
    From Newsgroup: comp.lang.tcl

    On Wed, 26 Jun 2024 12:23:13 +0200, Ralf Fassel wrote:

    * Mark Summerfield <mark@qtrac.eu>
    | I've made a cut-down example shown below.
    | If I click the Options button the Options dialog shows as it should. |
    And if I click the Options dialog's Close button it is hidden.

    No, it's not, it is destroyed (as you requested by)

    | ttk::button .optionsForm.closeButton -text Close \
    | -command {destroy $::test::optionsForm}

    Part of the problem is that you only check for the existence of the
    variable, but not of the window it describes. The second time you call test::on_options, the variable exists, but the window does not since it
    was destroyed by the closeButton.

    | # Hide/show dialog | proc test::on_options {} {
    | if {![info exists ::test::optionsForm]} {

    I suggest to add
    || ![winfo exists $::test::optionsForm]

    in test::on_options (and probably to unify all the different ways to withdraw/destroy the window - Close button, WM_DELETE, Escape).

    HTH R'

    You are quite right, I made a silly copy/paste mistake.

    I now use a lambda:

    set on_close [lambda {} {dialog hide .optionsForm}]

    and set this and the close button's command, the wm protocol for delete
    window and for Esc.

    I have not added the or-clause you suggested since my view is that if the variable exists and the window doesn't it is a coding error and I want the exception.

    Thanks for your help.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Mark Summerfield@mark@qtrac.eu to comp.lang.tcl on Thu Jun 27 06:51:18 2024
    From Newsgroup: comp.lang.tcl

    [snip]

    Based on your idea of using winfo exists I've now refined the on_options
    proc to this:

    proc test::on_options {} {
    if {![winfo exists .optionsForm]} {
    puts "on_options init"
    toplevel .optionsForm
    set on_close [lambda {} {dialog hide .optionsForm}]
    wm title .optionsForm "Options — [tk appname]"
    ttk::label .optionsForm.label -text "Options go here"
    grid .optionsForm.label -row 0 -column 0
    ttk::button .optionsForm.closeButton -text Close -command
    $on_close
    grid .optionsForm.closeButton -row 1 -column 0
    wm protocol .optionsForm WM_DELETE_WINDOW $on_close
    bind .optionsForm <Escape> $on_close
    dialog prepare .optionsForm
    }
    puts on_options
    dialog show .optionsForm
    }
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Ralf Fassel@ralfixx@gmx.de to comp.lang.tcl on Thu Jun 27 10:46:39 2024
    From Newsgroup: comp.lang.tcl

    * Mark Summerfield <mark@qtrac.eu>
    | proc test::on_options {} {
    | if {![winfo exists .optionsForm]} {
    | puts "on_options init"
    | toplevel .optionsForm
    | set on_close [lambda {} {dialog hide .optionsForm}]
    | wm title .optionsForm "Options — [tk appname]"
    --<snip-snip>--

    I make a habit of putting the layout of any widget structure in
    variables, so if I decide to add another frame or toplevel or whatever,
    I just need to change the variables and leave the rest of the code
    unchanged:

    proc test::on_options {} {
    set w(optionsForm) .optionsForm
    set w(optionsLabel) $w(optionsForm).label
    set w(optionsClose) $w(optionsForm).closeButton
    if {![winfo exists $w(optionsForm)]} {
    puts "on_options init"
    toplevel $w(optionsForm)
    set on_close [lambda {} [list dialog hide $w(optionsForm)]]
    wm title $w(optionsForm) "Options — [tk appname]"

    ttk::label $w(optionsLabel) -text "Options go here"
    grid $w(optionsLabel) -row 0 -column 0
    ttk::button $w(optionsClose) -text Close -command $on_close
    grid $w(optionsClose) -row 1 -column 0
    ...

    Say I need the options frame as child of a different widget than ".",
    I could just change the proc to

    proc test::on_options {top} {
    set w(optionsForm) $top.optionsForm
    ...

    and be done.

    HTH
    R'
    --- Synchronet 3.20a-Linux NewsLink 1.114