On Thursday, December 30, 2021 at 11:54:03 PM UTC+11,
j.l...@gmail.com wrote:
2. I checked and the line that makes a difference is this: window.after(0,reset)
Why does it help? What's happening here?
To understand what is happening, you have to understand the flow of execution. Please bear with me here, I'm not familiar with the ttk::xxx widgets or the code, but the one good thing about ttk is that we can examine a lot of the event binding code as
the modules are all pure tcl. So I had a look at the lib/tk8.6/ttk/combobox.tcl module. It's about 12K and 450 lines long but here are some important ones:
Around line 63 we have -
```### Combobox listbox bindings.
#
bind ComboboxListbox <ButtonRelease-1> { ttk::combobox::LBSelected %W }
bind ComboboxListbox <Return> { ttk::combobox::LBSelected %W }
```
and at line 93 -
```## LBSelected $lb -- Activation binding for listbox
# Set the combobox value to the currently-selected listbox value
# and unpost the listbox.
#
proc ttk::combobox::LBSelected {lb} {
set cb [LBMaster $lb]
LBSelect $lb
Unpost $cb
focus $cb
}```
This is probably the function that runs when you release the mouse after clicking on the listbox, and it will then go ahead and make a new selection in the widget. I inserted some debug output at each line here so I could "checkpoint" and see where the
error was popping up:
```proc ttk::combobox::LBSelected {lb} {
puts "ttk::combobox::LBSelected lb=$lb"
set cb [LBMaster $lb]
puts " calling LBSelect"
LBSelect $lb
puts " calling Unpost"
Unpost $cb
puts " calling focus"
focus $cb
puts " ---LBSelected finished!"
}
```
I also took the LBSelect proc and inserted some there:
```## LBSelect $lb --
# Transfer listbox selection to combobox value.
#
proc ttk::combobox::LBSelect {lb} {
puts "ttk::combobox::LBSelect lb=$lb"
set cb [LBMaster $lb]
puts " calling curselection"
set selection [$lb curselection]
if {[llength $selection] == 1} {
puts " calling SelectEntry"
SelectEntry $cb [lindex $selection 0]
}
puts " ---LBSelect finished"
}
```
When I ran the test script again-
ttk::combobox::LBSelected lb=.f.cb.popdown.f.l
calling LBSelect
ttk::combobox::LBSelect lb=.f.cb.popdown.f.l
calling curselection
calling SelectEntry
changed
Calling destroy
destroy returned
Background error:
errorInfo:
while executing
"$cb current $index"
(procedure "SelectEntry" line 2)
invoked from within
"SelectEntry $cb [lindex $selection 0]"
(procedure "LBSelect" line 8)
invoked from within
"LBSelect $lb"
(procedure "ttk::combobox::LBSelected" line 5)
invoked from within
"ttk::combobox::LBSelected .f.cb.popdown.f.l "
(command bound to event)
So what's happening is that the user is clicking on the combobox, the event runs and sets the selection and during the SelectEntry proc sets your variable is set, which activates the trace and calls combo_changed, which calls reset, which destroys the
combobox, and when execution returns to SelectEntry, the comboxbox no longer exists and we get a background error and the focus is never set to the entry box.
By putting the reset call into the after event ("after 0" basically translates to something like "after return") we decouple the combobox work from your reset function and the destroy will run later, no longer upsetting the combobox. I'll insert the
after 0 and try it again and it all runs properly now:
scottyw@officewinvm MINGW32 /n/Projects, Software/python/Test.comp.lang.tcl.26DEC2021
$ wish script.tcl
ttk::combobox::LBSelected lb=.f.cb.popdown.f.l
calling LBSelect
ttk::combobox::LBSelect lb=.f.cb.popdown.f.l
calling curselection
calling SelectEntry
changed
---LBSelect finished
calling Unpost
calling focus
---LBSelected finished!
Calling destroy
destroy returned
The thing to remember it that these actions are all happening as events, and if you want to do something drastic like destroy the widget that is calling you, during the call back itself, your best to place that outside the widget's execution in something
like an "after 0" call back. If you must do it immediately then as a safety you should wrap the calls in catch "..." however in this case it's not going to help you, because the call to destroy during the trace call back is the problem itself.
Hope these ramblings make sense. I'm 7 days into a very very bad flu I don't think what I've written it all that coherent. Normally when I'm trying to get to the bottom of problems with event code I'll use lots of puts "..." messages to see who is
calling who and when. I had major headaches with a snit "messageentry" widget I'd made a couple of years ago in one commercial application, and that widget had become a real swiss army knife and now included popup suggestions list using a listbox that
pops up dynamically. I found it most difficult to catch things like the user clicking away from the application and getting the button click in the widget to work correctly. It was all the added debug output from the event handlers that helped or at
least, gave me enough information to pour over and sort out how to get it working.
Scott
--- SoupGate-Win32 v1.05
* Origin: fsxNet Usenet Gateway (21:1/5)