• Why does geometry take a while?

    From Luc@21:1/5 to All on Fri Dec 15 13:58:53 2023
    I have two versions of an application. One is old, I'm working on the
    newer version.

    Both begin more or less the same way:

    proc p.createwindow {args} {
    package require Tk
    wm withdraw .
    eval destroy [winfo children .]
    catch {destroy .NAME errorsgui}
    set ::w [toplevel .topw -background #c0c0c0]
    wm resizable $::w 1 1
    ...
    }

    Then later on, this:

    regexp {([0-9]+)x.+} [winfo geometry $::w] -> ::WholeAppWidth
    w.debug $::WholeAppWidth

    The older one says my whole app width is 1920. Makes sense. It is in fact
    my screen's resolution.

    The new one says it's 968.

    The old one positions a pop-up menu exactly where it's supposed to be.
    The new one positions the menu very wrong.

    Both applications run maximized.

    I spent a long time looking over and over and over and couldn't find
    any hint of explanation for this discrepancy.

    Then I had an idea:

    after 1000 {
    regexp {([0-9]+)x.+} [winfo geometry $::w] -> ::WholeAppWidth
    w.debug $::WholeAppWidth
    }

    A-ha! Now the new version gets the correct measurement.

    But why? I moved that around multiple points of the code and got multiple results (even 1), and the number tends to grow the closer the debugging statement is to the end of the script.

    But still, at the end of the script, it's not ready. I had to add the after statement to get it right. But what if 1000 is not enough?

    So why does that happen and how am I supposed to know how long
    my code has to wait around to get the correct reading?

    --
    Luc


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Robert Heller@21:1/5 to luc@sep.invalid on Fri Dec 15 17:55:05 2023
    At Fri, 15 Dec 2023 13:58:53 -0300 Luc <luc@sep.invalid> wrote:


    I have two versions of an application. One is old, I'm working on the
    newer version.

    Both begin more or less the same way:

    proc p.createwindow {args} {
    package require Tk
    wm withdraw .
    eval destroy [winfo children .]
    catch {destroy .NAME errorsgui}
    set ::w [toplevel .topw -background #c0c0c0]
    wm resizable $::w 1 1
    ...
    }

    Then later on, this:

    regexp {([0-9]+)x.+} [winfo geometry $::w] -> ::WholeAppWidth
    w.debug $::WholeAppWidth

    The older one says my whole app width is 1920. Makes sense. It is in fact
    my screen's resolution.

    The new one says it's 968.

    The old one positions a pop-up menu exactly where it's supposed to be.
    The new one positions the menu very wrong.

    Both applications run maximized.

    I spent a long time looking over and over and over and couldn't find
    any hint of explanation for this discrepancy.

    Then I had an idea:

    after 1000 {
    regexp {([0-9]+)x.+} [winfo geometry $::w] -> ::WholeAppWidth
    w.debug $::WholeAppWidth
    }

    A-ha! Now the new version gets the correct measurement.

    But why? I moved that around multiple points of the code and got multiple results (even 1), and the number tends to grow the closer the debugging statement is to the end of the script.

    But still, at the end of the script, it's not ready. I had to add the after statement to get it right. But what if 1000 is not enough?

    So why does that happen and how am I supposed to know how long
    my code has to wait around to get the correct reading?

    Instead of the after command you should use

    update idle
    regexp {([0-9]+)x.+} [winfo geometry $::w] -> ::WholeAppWidth
    w.debug $::WholeAppWidth


    What is happening is the Tk is "lazy" (specificly it uses the "lazy eval" principal). Nothing gets "realized" or drawn in the screen, until it needs to be. Functions like toplevel return "right away" and don't wait for the toplevel to be "realized" or drawn -- these functions are defered and go into the event queue as an "idle" task (I'm probably not explaining this in a technically correct way, but for your purposes, that does not matter).

    So, after you have built your UI (eg the toplevel(s) and all of their children and menus and such) and you want to finalize the geometry, you use the "update idle" command to get eveything "realized" and then you can get the final geometry of the toplevel(s) and then get eventhing placed on the screen properly. You would also do this if you create popups or dialog boxes on-the-fly.

    "update idle" will run through all of the idle tasks in the event queue and complete them. It will take just exactly as long as that takes, depending
    on the complexity of the toplevels, the speed of the graphics system, etc.



    --
    Robert Heller -- Cell: 413-658-7953 GV: 978-633-5364
    Deepwoods Software -- Custom Software Services
    http://www.deepsoft.com/ -- Linux Administration Services
    heller@deepsoft.com -- Webhosting Services

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Luc@21:1/5 to Robert Heller on Fri Dec 15 15:44:09 2023
    On Fri, 15 Dec 2023 17:55:05 +0000, Robert Heller wrote:

    Instead of the after command you should use

    update idle
    regexp {([0-9]+)x.+} [winfo geometry $::w] -> ::WholeAppWidth
    w.debug $::WholeAppWidth


    No. That doesn't work.


    "update idle" will run through all of the idle tasks in the event queue
    and complete them. It will take just exactly as long as that takes, >depending on the complexity of the toplevels, the speed of the graphics >system, etc.

    Therein lies the problem.

    The 'update' man page has this example:


    set x 1000
    set done 0
    after 1000 set done 1
    while {!$done} {
    # A very silly example!
    set x [expr {log($x) ** 2.8}]
    # Test to see if our time-limit has been hit. This would
    # also give a chance for serving network sockets and, if
    # the Tk package is loaded, updating a user interface.
    update
    }


    Yeah well, but that still uses after. And the question remains: "after"
    how long exactly? How do I know?

    Ideally, I should have some way to know that all idle tasks have been completed. In pseudo code,

    if {[llength [idletasks pending]] == 0} {p.getResolution}

    --
    Luc


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Harald Oehlmann@21:1/5 to All on Fri Dec 15 20:45:24 2023
    Am 15.12.2023 um 19:44 schrieb Luc:
    On Fri, 15 Dec 2023 17:55:05 +0000, Robert Heller wrote:

    Instead of the after command you should use

    update idle
    regexp {([0-9]+)x.+} [winfo geometry $::w] -> ::WholeAppWidth
    w.debug $::WholeAppWidth


    No. That doesn't work.


    "update idle" will run through all of the idle tasks in the event queue
    and complete them. It will take just exactly as long as that takes,
    depending on the complexity of the toplevels, the speed of the graphics
    system, etc.

    Therein lies the problem.

    The 'update' man page has this example:


    set x 1000
    set done 0
    after 1000 set done 1
    while {!$done} {
    # A very silly example!
    set x [expr {log($x) ** 2.8}]
    # Test to see if our time-limit has been hit. This would
    # also give a chance for serving network sockets and, if
    # the Tk package is loaded, updating a user interface.
    update
    }


    Yeah well, but that still uses after. And the question remains: "after"
    how long exactly? How do I know?

    Ideally, I should have some way to know that all idle tasks have been completed. In pseudo code,

    if {[llength [idletasks pending]] == 0} {p.getResolution}


    Luc,
    thanks for the post.
    There are calculations on widgets, which need multiple rounds.
    And they are not all idle tasks.
    Advanced widgets may use an "after idle" or "after 1" to delay eventual geometry adjustments after the first round passed.

    TCL 9 gives you more control on the update command.

    On the practical side:
    Does just one "update" help ?

    If you don't have to many highly complex widgets (tablelist for
    example), than this should be enough.

    Ohterwise, do:
    update
    update

    But that should be enough.

    Take care,
    Harald

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Robert Heller@21:1/5 to luc@sep.invalid on Fri Dec 15 19:43:14 2023
    At Fri, 15 Dec 2023 15:44:09 -0300 Luc <luc@sep.invalid> wrote:


    On Fri, 15 Dec 2023 17:55:05 +0000, Robert Heller wrote:

    Instead of the after command you should use

    update idle
    regexp {([0-9]+)x.+} [winfo geometry $::w] -> ::WholeAppWidth
    w.debug $::WholeAppWidth


    No. That doesn't work.

    Are sure you are doing things right?

    Here is code *I* use (this is the pattern that works and is lifted from the Tk Dialog code):

    wm withdraw .
    update idle
    set x [expr {[winfo screenwidth .]/2 - [winfo reqwidth .]/2 \
    - [winfo vrootx .]}]
    set y [expr {[winfo screenheight .]/2 - [winfo reqheight .]/2 \
    - [winfo vrooty .]}]
    wm geom . +$x+$y
    wm deiconify .

    And this works perfectly everytime.

    This code positions the main window (.) exactly in the center of the screen.

    winfo reqwidth and winfo reqheight return the correct main window size, once
    it has been realized. update idle completes the main window realization and updates the required size after all of the children widgets have been built
    and have had their geometry managed (eg with pack, place, or grid).



    "update idle" will run through all of the idle tasks in the event queue
    and complete them. It will take just exactly as long as that takes, >depending on the complexity of the toplevels, the speed of the graphics >system, etc.

    Therein lies the problem.

    The 'update' man page has this example:


    set x 1000
    set done 0
    after 1000 set done 1
    while {!$done} {
    # A very silly example!
    set x [expr {log($x) ** 2.8}]
    # Test to see if our time-limit has been hit. This would
    # also give a chance for serving network sockets and, if
    # the Tk package is loaded, updating a user interface.
    update
    }


    This is a completely unrelated code fragment.


    Yeah well, but that still uses after. And the question remains: "after"
    how long exactly? How do I know?

    Ideally, I should have some way to know that all idle tasks have been completed. In pseudo code,

    if {[llength [idletasks pending]] == 0} {p.getResolution}


    update idle runs through *all pending* idletasks.

    Can you post your *complete* code? Something else is going on.

    --
    Robert Heller -- Cell: 413-658-7953 GV: 978-633-5364
    Deepwoods Software -- Custom Software Services
    http://www.deepsoft.com/ -- Linux Administration Services
    heller@deepsoft.com -- Webhosting Services

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Luc@21:1/5 to Robert Heller on Fri Dec 15 17:27:30 2023
    On Fri, 15 Dec 2023 19:43:14 +0000, Robert Heller wrote:

    Are sure you are doing things right?

    I wasn't, as usual. I've solved the mystery.


    Harald Oehlmann wrote:

    On the practical side:
    Does just one "update" help ?
    If you don't have to many highly complex widgets (tablelist for
    example), than this should be enough.
    Ohterwise, do:
    update
    update
    But that should be enough.

    That didn't work either. I tried a lot of updates. I made a for loop
    to fire update 10 thousand times. It still didn't work.

    "It never updates," I thought.

    That was the spark I needed.

    I realized that the two versions had the maximization command stated
    at different points, later in the newer version.

    So I brought it higher up:

    wm attributes $::w -zoomed 1
    update

    And now it works.

    Yay. Back to happy camping.

    Thank you again.

    --
    Luc


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Robert Heller@21:1/5 to luc@sep.invalid on Fri Dec 15 21:20:57 2023
    At Fri, 15 Dec 2023 17:27:30 -0300 Luc <luc@sep.invalid> wrote:


    On Fri, 15 Dec 2023 19:43:14 +0000, Robert Heller wrote:

    Are sure you are doing things right?

    I wasn't, as usual. I've solved the mystery.


    Harald Oehlmann wrote:

    On the practical side:
    Does just one "update" help ?
    If you don't have to many highly complex widgets (tablelist for
    example), than this should be enough.
    Ohterwise, do:
    update
    update
    But that should be enough.

    That didn't work either. I tried a lot of updates. I made a for loop
    to fire update 10 thousand times. It still didn't work.

    "It never updates," I thought.

    That was the spark I needed.

    I realized that the two versions had the maximization command stated
    at different points, later in the newer version.

    So I brought it higher up:

    wm attributes $::w -zoomed 1
    update

    And the above really should be

    update idle



    And now it works.

    Yay. Back to happy camping.

    Thank you again.


    --
    Robert Heller -- Cell: 413-658-7953 GV: 978-633-5364
    Deepwoods Software -- Custom Software Services
    http://www.deepsoft.com/ -- Linux Administration Services
    heller@deepsoft.com -- Webhosting Services

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)