• how to pass a window to after idle

    From Mark Summerfield@m.n.summerfield@gmail.com to comp.lang.tcl on Tue Jul 29 17:12:16 2025
    From Newsgroup: comp.lang.tcl

    I have a program that works fine but I want to move one function call
    from one place to another: when I do so the program fails.

    In particular I want to move the line marked *1* to *2*.
    But when I do so, as shown below, I get this error:

    can't read "form": no such variable
    can't read "form": no such variable
    while executing
    "focus $form"
    ("after" script)

    Here's the example code:

    #!/usr/bin/env wish9

    proc main {} {
    tk scaling 2
    wm title . Focus
    ttk::button .quitButton -text Quit -command exit
    ttk::button .formButton -text Form -underline 0 \
    -command focus_form::show_form
    bind . <Escape> exit
    bind . <Alt-q> exit
    bind . <Alt-f> focus_form::show_form
    pack .formButton
    pack .quitButton
    }

    namespace eval focus_form {}

    proc focus_form::show_form {} {
    if {![winfo exists .form]} {
    toplevel .form
    wm title .form "Focus Form"
    ttk::button .form.closeButton -text Close \
    -command focus_form::on_close
    bind .form <Escape> focus_form::on_close
    pack .form.closeButton
    }
    form::show_modal .form
    # after idle {focus .form} ;# *1* I am moving this
    }

    proc focus_form::on_close {} { form::hide .form }

    namespace eval form {}

    proc form::prepare {window on_close {modal true} {x 0} {y 0}} {
    wm withdraw $window
    if {$modal} {
    wm transient $window .
    }
    set parent [winfo parent $window]
    if {!($x && $y)} {
    set x [expr {[winfo x $parent] + [winfo width $parent] / 3}]
    set y [expr {[winfo y $parent] + [winfo height $parent] / 3}]
    }
    wm geometry $window "+$x+$y"
    wm protocol $window WM_DELETE_WINDOW $on_close
    }

    proc form::show_modal form {
    wm deiconify $form
    raise $form
    focus $form
    grab set $form
    after idle {focus $form} ;# *2* to here
    }

    proc form::hide form {
    grab release $form
    wm withdraw $form
    }

    main
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From saito@saitology9@gmail.com to comp.lang.tcl on Tue Jul 29 13:21:33 2025
    From Newsgroup: comp.lang.tcl

    On 7/29/2025 1:12 PM, Mark Summerfield wrote:
    I have a program that works fine but I want to move one function call
    from one place to another: when I do so the program fails.

    In particular I want to move the line marked *1* to *2*.
    But when I do so, as shown below, I get this error:


    proc form::show_modal form {
    wm deiconify $form
    raise $form
    focus $form
    grab set $form
    after idle {focus $form} ;# *2* to here
    }


    You are preventing/delaying the normal substitution of $form here, and
    since there is no variable named "form" in scope when it fires, you get
    the error.

    By the way, you did not just move the line, you changed ".form" to
    "$form". If you change it back, it will work.

    Another way to make it work:
    after idle [list focus $form]



    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Mark Summerfield@m.n.summerfield@gmail.com to comp.lang.tcl on Tue Jul 29 17:52:03 2025
    From Newsgroup: comp.lang.tcl

    On Tue, 29 Jul 2025 13:21:33 -0400, saito wrote:

    On 7/29/2025 1:12 PM, Mark Summerfield wrote:
    I have a program that works fine but I want to move one function call
    from one place to another: when I do so the program fails.

    In particular I want to move the line marked *1* to *2*.
    But when I do so, as shown below, I get this error:


    proc form::show_modal form {
    wm deiconify $form
    raise $form
    focus $form
    grab set $form
    after idle {focus $form} ;# *2* to here
    }


    You are preventing/delaying the normal substitution of $form here, and
    since there is no variable named "form" in scope when it fires, you get
    the error.

    By the way, you did not just move the line, you changed ".form" to
    "$form". If you change it back, it will work.

    Another way to make it work:
    after idle [list focus $form]

    Thank you that worked great. It fixed the tiny example above,
    and it also worked in the application I'm developing.
    Thanks too for the explanation.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Rich@rich@example.invalid to comp.lang.tcl on Tue Jul 29 18:55:05 2025
    From Newsgroup: comp.lang.tcl

    Mark Summerfield <m.n.summerfield@gmail.com> wrote:
    On Tue, 29 Jul 2025 13:21:33 -0400, saito wrote:

    On 7/29/2025 1:12 PM, Mark Summerfield wrote:
    I have a program that works fine but I want to move one function call
    from one place to another: when I do so the program fails.

    In particular I want to move the line marked *1* to *2*.
    But when I do so, as shown below, I get this error:


    proc form::show_modal form {
    wm deiconify $form
    raise $form
    focus $form
    grab set $form
    after idle {focus $form} ;# *2* to here
    }


    You are preventing/delaying the normal substitution of $form here, and
    since there is no variable named "form" in scope when it fires, you get
    the error.

    By the way, you did not just move the line, you changed ".form" to
    "$form". If you change it back, it will work.

    Another way to make it work:
    after idle [list focus $form]

    Thank you that worked great. It fixed the tiny example above,
    and it also worked in the application I'm developing.
    Thanks too for the explanation.

    Do keep in mind several aspects of Tcl:

    1) $ means variable substitution in Tcl;
    2) Tcl does not have lexical scoping for variables;
    3) after scripts (and Tk callback -command options) are executed with
    only global variables in scope for the execution.

    #3 means that a script passed to after is not executing as part of a
    proc which may have set it up, it is seperately executing in the global namespace and can only see global variables by default.

    #2 means that none of the local variables in the proc that setup the
    after script are visible to the after script itself. In fact, given
    that the defining proc returns just after setting up the after, all the
    local variables are garbage collected and no longer exist when the
    after script is running.

    #2 also means Tcl does not, by itself, support closures. The fact that
    the after script referenced local variable $form does not make the
    local 'form' variable from the proc hang around to later be visible to
    the after script.

    All of the above is why most documentation/tutorials/books on Tk and
    widget callbacks almost always recommend defining a proc to be a
    callback target from a Tk -command option. It is easy to "pass
    variables" into a proc in a callback or after command such that they
    are available to the resulting script or callback. It is much more
    tricky to dereference variables inline properly to make the same work
    for a raw script snippet.

    --- Synchronet 3.21a-Linux NewsLink 1.2