It appears that a menu's -postcommand on Aqua is not
called until after the menu is posted, or else if it
makes changes to the menu they are not reflected until
the next time the menu is posted. A short example:
menu .m
.m add cascade -label Foo -menu .m.f
menu .m.f -postcommand Foo
.m.f add command -label "(Empty)"
set i 1
proc Foo {} {
.m.f delete 0 end
.m.f add command -label "$::i - Foo1"
.m.f add command -label "$::i - Foo2"
.m.f add command -label "$::i - Foo3"
.m.f add command -label "$::i - Foo4"
incr ::i
}
. config -menu .m
Each time the menu is posted, its contents will change
to some so the number indicates how many times it's
been posted. It's expected that the -first- time it's
posted it will show "1 - Foo1" etc. Instead it shows
"(Empty)". It's expected that the second time it's
posted it will show "2 - Foo1" etc. Instead it shows
"1 - Foo1" etc.
Logged In: YES
user_id=498198
Possibly related or partly responsible: in trying to track
down the source of the problem, I tried the following script
that periodically switches the state of a menu entry:
menu .m
.m add cascade -label Foo -menu .m.f
menu .m.f
.m.f add command -label Bar
set i 0
proc toggle {} {
if {($::i % 2) == 0} {
set state normal
} else {
set state disabled
}
.m.f entryconfig Bar -state $state
puts $state
after 1000 toggle
}
. config -menu .m.f
after 1000 toggle
I run this and click on the Foo menu. On Unix (X/Windows)
and Windows, the Bar entry periodically switches back and
forth between states and "normal" and "disabled" are written
to the console. Under Aqua, "normal" and "disabled" are
periodically written to the console only while the menu is
unposted. As soon as I click on the menu the output stops
and the entry does not toggle. It resumes once the menu is
unposted.
I'm almost led to believe that the post command is getting
triggered when the menu is posted but actually gets blocked
until the menu is unposted, but as far as I can tell so far
in reading the source code it's not called as a n idle task
or anything, so I don't see why that would be the case.
Logged In: YES
user_id=498198
Okay, it turns out the post command actually *is* getting
called, but the changes to the menus aren't getting applied
until the next post. Adding to the top of the post command:
puts "POSTED - $::i"
Shows that the command is indeed called, but the number
printed is one less than what's displayed in the actual menu
entries. So maybe an issue with CompleteIdlers()?
Logged In: YES
user_id=498198
After noticing that the problem doesn't appear for tk_popup
posted menus and sprinkling printf()s here and there to see
what's going on, I've isolated the problem and have a
workaround, but I'm not sure about a proper fix.
It turns out that CompleteIdlers() is never called for menu
bar menus. The reconfiguration is done in the postcommand
and a couple idle callbacks are set up to reconfigure the
menus via Tcl_DoWhenIdle(ReconfigureMacintoshMenu, ..), in
this case in TkpDestroyMenuEntry(), but are not called until
the menu unposted.
Adding an "update idletasks" to the end of the postcommand
proc that reconfigures the menu works around the problem.
It would seem that a call to CompleteIdlers() needs to be
made some time after TkPreprocessMenu() is called; I tried
doing it there before returning and it only catches the
first of the two ReconfigureMacintoshMenu() callbacks; the
other seems to get added some time after TkPreprocessMenu()
returns.
bar menus
Logged In: YES
user_id=431773
A presumably-related issue:
If a pull-down menu contains submenus and if postcommand is used to
update the submenus (at least if it's used to delete all entries from the
submenu and add new ones) then the submenus stop working. The
submenu entries exist and are not grayed out, but they do nothing.
The only way I've found to make the pull-down menu work again is to
bring a different toplevel to the top or bring a different application to the
top. Then all is well. But it's a bizarre hackaround and not something one
wants to ask users to do.
A variant of this hack that sort of works in code is:
Whenever a submenu is changed, do the following:
- create a toplevel
- update idletasks
- destroy the toplevel
the submenus will fail the first time the pull-down menu is used, but the
pull-down menu will work after that.
Logged In: YES
user_id=90580
Russell,
I am looking into the events during menutracking issue ATM, it would be
useful if you could post a small sample script that exhibits the problem
you see.
Thanks
bar menus --> TkAqua: CompleteIdlers() is never called for menubar menus
Hi, how is the status of this issue?
i've the problem that submenus, from a menubar, created using the -postcommand do not appear, as Michale Kirkham explained.
Here is a variant of the script by Michael Kirkham, where the problem mentioned by 'reowen' which i also have: (only the Foo proc is changed):
menu .m
.m add cascade -label Foo -menu .m.f
menu .m.f -postcommand Foo
.m.f add command -label "(Empty)"
set i 1
proc Foo {} {
.m.f delete 0 end
.m.f add command -label "$::i - Foo1"
.m.f add command -label "$::i - Foo2"
.m.f add command -label "$::i - Foo3"
.m.f add cascade -label "C $::i" -menu .m.f.c
if { [ winfo exists .m.f.c]} {
.m.f.c delete 0 end
} else {
menu .m.f.c
}
.m.f.c add command -label "C $::i Foo 1"
.m.f.c add command -label "C $::i Foo 2"
incr ::i
}
. config -menu .m
The submenus are not created.
At the moment i disable the post command ( .m.f configure -postcommand "") the submenu options appear. i tried also inserting 'update idletasks' in the Foo procedure but does not work.
¿Any suggestions?
Miguel Pasenau
Hi Miguel,
this issue is fixed in the new Cocoa-based TkAqua 8.6 (which has a completely new menu implementation written from scratch) and your script works correctly there.
Note that a backport of TkAqua Cocoa to Tk 8.5 is also available from http://github.com/das/tcltk/tree/de-carbon-8-5\).
I am not aware of any solution to this for the obsolete TkAqua Carbon sourcebase, its menu code is exceedingly baroque and very different to other platforms (in particular w.r.t the idle time processing that is at the root of this issue), at this point I have no plans nor time to work on the numerous issues in the old codebase.
Hi Daniel,
thanks for the quick reply.
As the original application i'm working on, has several menus which have this problem, a temporary solution i adopted is to
- register the widget menu identifier together with the postcomand to be issued;
- then delete the postcommand of the menus (this step is necessary, otherwise the submenus are not visualized/created when i change them later)
- update explicitly the menus whenever i need to change them and then create a temporary toplevel window (with geometry 0x0+0+0), update tk, and destroy it.
With the temporary creation and destruction of this window, the menus are correctly refreshed, although they disappear momentarily. A couple of users which used the application doesn't find it that annoying.
I'll check the back-port if it can be easily applicable, or wait until 8.6 is released.
Thanks
Miguel