Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How to keep GUI alive?

116 views
Skip to first unread message

Frank

unread,
Mar 21, 2017, 1:00:49 PM3/21/17
to
Hi,

I have the following situation:

1) An application with a GUI and a TCL console built in.
2) I wrote a proc that is executed within the TCL console. It creates another top level window.
3) The proc is called within multiple levels of execution hierarchy (nested procedures).

I would like the user to make changes in the GUI and based on the inputs take action but this has to be done at the execution level (stack) where my proc was called (3 above).

I can not use a button in the GUI to call a proc to take action as it seems the GUI is at the top level of execution (#0) and the proc call will not be at the same execution level where my proc was called above.

How can I detect the action in the GUI (keeping the main application GUI and my top level window (2 above) active and don't leave the execution stack where my procedure was called?


set j "Top of exec stack."
proc a1 {} { set j "in a1" ; a2}
proc a2 {} { set j "in a2" ; a3}
proc a3 {} { set j "in a3" ; a4}
proc a4 {} { set j "in a4"

for {set i 0} {$i <= 20} {incr i} {
puts "i = $i"
if {$i == 10} {my_proc_call}
}

} ; # EndProc a4


if from my proc call I do
uplevel #1 puts "j = $j"

I get "in a1"

If the command is executed from a button in the GUI it will not find execution level #1 as it is in a different execution stack.


I have tried to use trace, onvar, etc. but all those are handled by the event handler and seems to be executed in a different execution stack.


Thanks in advance,
Frank

matthe...@gmail.com

unread,
Mar 21, 2017, 2:13:20 PM3/21/17
to
I'm not quite sure I follow what you want to have happen but it sounds like coroutines might be helpful to you.

https://www.tcl.tk/man/tcl/TclCmd/coroutine.htm


I should also note the difference between uplevel #1 and uplevel 1. With a number sign, it's absolute level. #1 = the second highest, outtermost context. uplevel 1 means the context above the current one.

Uwe Klein

unread,
Mar 21, 2017, 2:30:38 PM3/21/17
to
Am 21.03.2017 um 18:00 schrieb Frank:
> I have tried to use trace, onvar, etc. but all those are handled by the event handler and seems to be executed in a different execution stack.

Button events and similar all happen at the top level.
What you need is access to a global variable

> set ::j "Top of exec stack."
> proc a1 {} { set ::j "in a1" ; a2}
> proc a2 {} { set ::j "in a2" ; a3}
> proc a3 {} { set ::j "in a3" ; a4}
> proc a4 {} { set ::j "in a4"

uplevel #0 puts "j = $j"

or just

puts "j = $::j"


Frank

unread,
Mar 21, 2017, 4:31:23 PM3/21/17
to
Thanks guys for the recommendations.

I am not sure how to get coroutine to work in this situation. The GUI I generate will dictate what my procedure will do. The example below is very simple and in this case a global will not work as I want the value at the different execution levels. Not a single value.

I could use the general TK interface to get the information from the GUI.

The problem is that in my procedure I have to be monitoring the GUI (for example: a buttom press) I could have a loop looking for changes in the GUI. Once I detect them I take action.

What I don't understand is how my GUI is active while the main application GUI is not. I am not stealing focus.


matthe...@gmail.com

unread,
Mar 22, 2017, 9:08:20 AM3/22/17
to
What do you mean you have to monitor the GUI? Are you not launching a procedure when an action is taken in the GUI? For example, executing a proc when a button is pressed?

Can you share some sample code or perhaps a more complete stripped down example?

Frank

unread,
Mar 22, 2017, 10:18:17 AM3/22/17
to
Yes I am __not__ launching a proc when a button is pressed (cmd parameter in the button widget) because if I do that the proc will be called from the GUI execution stack which is normally top level #0 and not at the "a4" procedure level as shown in the example I posted.

To run at the "a4" execution stack I need to run my procedure from within "a4" and collect the information from the GUI.

From my procedure if I run:
uplevel #0 puts "j = $j" ---> Returns "Top of exec stack"
uplevel #1 puts "j = $j" ---> Returns "in a1"
uplevel #2 puts "j = $j" ---> Returns "in a2"
uplevel #3 puts "j = $j" ---> Returns "in a3"
uplevel #4 puts "j = $j" ---> Returns "in a4"

If I run it from the GUI it will tell me levels #1, #2, #3 and #4 are incorrect.


As my GUI is kept alive and the one from the main application is not, it may be an issue of how my toplevel GUI window is defined. I am not stealing focus which typically causes the keyboard and mouse inputs to be active only in the window with the focus. I have not played with window groups so I am not sure if that may be affecting. It seems the application main GUI is created in a separate namespace. I am not sure how to find the name of the application's top level window.

matthe...@gmail.com

unread,
Mar 22, 2017, 11:08:08 AM3/22/17
to
Without sample code, the best I can offer is "sounds like you're architecting it wrong."

Perhaps instead of nested procs, you should be using namespaces. Again, provide some actual code and I can probably help you.

Frank

unread,
Mar 22, 2017, 12:12:32 PM3/22/17
to
Sorry. I guess it is hard to show and it may be fairly complex to make a sample code as the main application has an embedded TKCON.

My code is a very simple proc (that creates my GUI and interacts with it and it is invoked within a4). The rest of the code is part of the main application. I have no control on its structure. The nested procs above is just an example of the structure in the application that I am dealing with and to reflect the importance of maintaining the execution stack for the commands that I execute in my proc.

Anyway, I appreciate you trying to help. Thanks again.

matthe...@gmail.com

unread,
Mar 22, 2017, 1:59:49 PM3/22/17
to
Hmm... if you're using Tkcon it might be that you're trying to do something between two different [interp]s.

Does not using [uplevel 1] not help? Since you can do relative, upward traversal rather than absolute?

Ralf Fassel

unread,
Mar 23, 2017, 4:27:22 AM3/23/17
to
* Frank <kra...@gmail.com>
| I have the following situation:
>
| 1) An application with a GUI and a TCL console built in.
| 2) I wrote a proc that is executed within the TCL console. It creates
| another top level window.
| 3) The proc is called within multiple levels of execution hierarchy
| (nested procedures).
>
| I would like the user to make changes in the GUI and based on the
| inputs take action but this has to be done at the execution level
| (stack) where my proc was called (3 above).
>
| I can not use a button in the GUI to call a proc to take action as it
| seems the GUI is at the top level of execution (#0) and the proc call
| will not be at the same execution level where my proc was called
| above.
>
| How can I detect the action in the GUI (keeping the main application
| GUI and my top level window (2 above) active and don't leave the
| execution stack where my procedure was called?

Unless you're running multi-threaded (do you?), you need to trigger an
'update' to actually give the GUI a chance to do anything.
If you simply loop inside a4, the GUI has no chance to do anything.

proc a4 {} {
# whatever
for {set i 0} {$i <= 20} {incr i} {
update ; # GUI takes action
# now check global variables indicating GUI changes
if {$::stop} {
puts "Stop button pressed"
return
}
if {$i == 10} {my_proc_call}
}
}

set ::stop 0
button .stop -command {set ::stop 1}

Note however that the 'update' may have unwanted side effects like
e.g. re-entering the proc-stack which leads to a4 etc (see
http://wiki.tcl.tk/1255 "Update considered harmful"), and that while the
loop is active (eg while in calling 'my_proc_call'), the GUI does not
react at all.

Either run multi-threaded or redesign the proc stack via coroutines (as
someone else suggested) or iterate via 'after':

proc a4 {i} {
if {$::stop || $i >= 20} {
# done
return
}
if {$i == 10} {my_proc_call}
after 0 [list a4 [incr i]]
# GUI gets a chance after we return
}

HTH
R'

Frank

unread,
Mar 23, 2017, 1:57:28 PM3/23/17
to
On Thursday, March 23, 2017 at 3:27:22 AM UTC-5, Ralf Fassel wrote:
> * Frank
Hi Ralf,

Thanks.

Yes, sadly I have also the 'update' inside the loop as you mentioned above.
I can not do multi-threading as I am bound to the TCL interpreter of the main application and it does not support multi-threading.

Matthew brings a good point. It may be that the GUI is in a separate interpreter. If that is the case it may explain why the 'update' command is not updating the main application GUI while the GUI I created is responsive.

Is there a way I can check for multiple interpreters in the application?
Assuming that I can find the interpreter that has the GUI, Is there a way to send the 'update' command to that interpreter from the one my procedure is running?

This goes past my knowledge. I have never dealt with multiple interpreters. I have seen that in TkCon but never had to use it. (TkCon menus are disabled so I can not access them that way).


Matthew Hiles

unread,
Mar 23, 2017, 2:26:31 PM3/23/17
to
So are you working out of tkcon? That is, the main application can launch tkcon and you're running scripts from there? I would say that complicates things quite a bit as the tkcon could be running in the same space as the main application or it could be a slave interp or a sibling interp.... depends on how the main app is setup.

Ralf Fassel

unread,
Mar 24, 2017, 6:17:45 AM3/24/17
to
* Frank <kra...@gmail.com>
| I can not do multi-threading as I am bound to the TCL interpreter of
| the main application and it does not support multi-threading.
>
| Matthew brings a good point. It may be that the GUI is in a separate
| interpreter. If that is the case it may explain why the 'update'
| command is not updating the main application GUI while the GUI I
| created is responsive.

Sorry, but that is beyond me. In my universe you either have
'multi-threading' or 'event-based' to keep the GUI alive, otherwise in a
tight loop te GUI will not be responsive no matter how many interps you
create. I never have worked with multiple interps, so I may be wrong
here.

| Is there a way I can check for multiple interpreters in the application?

interp slaves ?path?
Returns a Tcl list of the names of all the slave
interpreters associated with the interpreter identified by
path. If path is omitted, the invoking interpreter is used.

| Assuming that I can find the interpreter that has the GUI, Is there a
| way to send the 'update' command to that interpreter from the one my
| procedure is running?

As stated above, if that interp is not running in a separate thread, I
don't see how it would react while the loop is active (I.e. not yielding
via 'update' or 'vwait' or equivalent).

HTH
R'
0 new messages