===================================================
unknown widget .theTopPane.the0BuildQueue.lb.2007/07/11:13:48:07
unknown widget .theTopPane.the0BuildQueue.lb.2007/07/11:13:48:07
while executing
"Widget::cget $path $option"
(procedure "Widget::getoption" line 6)
invoked from within
"Widget::getoption $path.$item $option"
(procedure "_getoption" line 2)
invoked from within
"_getoption $path $item -foreground"
(procedure "_redraw_selection" line 11)
invoked from within
"_redraw_selection $path"
(procedure "_update_select_fill" line 13)
invoked from within
"_update_select_fill $path"
(procedure "ListBox::_resize" line 27)
invoked from within
"ListBox::_resize .theTopPane.the0BuildQueue.lb"
(command bound to event)
===================================================
I didn't write any of the code in this callstack, so I assume that
what I see is the internals of the BWidget ListBox, and it has a bug
when resizing? Or could I be doing something to cause this?
("ActiveState ActiveTcl 8.4.14.0.272572 Nov 26, 2006", containing
BWidget 1.8)
> "ListBox::_resize .theTopPane.the0BuildQueue.lb"
> (command bound to event)
> ===================================================
>
> I didn't write any of the code in this callstack, so I assume that
> what I see is the internals of the BWidget ListBox, and it has a bug
> when resizing? Or could I be doing something to cause this?
Do you use a BWidget ListBox? If so, how do you use it? If not, what
do you use in bwidget?
Sorry, yes I'm using a ListBox.
Creation:
ListBox .theTopPane.theQueue${someNum}.lb -selectmode single -
selectfill true \
-bg White -bd 0 -highlightthickness 0 -height 5 -padx 0
Then one binding:
bind .theTopPane.theQueue.lb <<ListboxSelect>> \
[list +ListboxSelected $someNum $someName]
And this procs use it extensively:
proc UpdateUIForMachine { inNumber inName } {
#update UI by removing everything, then re-adding everything. Save
selection (if possible)
set theSelection [.theTopPane.theQueue${inNumber}.lb selection
get]
.theTopPane.theQueue${inNumber}.lb delete [.theTopPane.theQueue$
{inNumber}.lb items]
foreach theQueueElement $::myListArray($inName) {
.theTopPane.theQueue${inNumber}.lb insert end [lindex
$theQueueElement 0] \
-text [lindex $theQueueElement 1] -indent 0
if { [lindex $theQueueElement 2] == "Current" } {
if { 0 == [string compare -nocase -length 8
"Finished" [.theTopPane.theStatus${inNumber} cget -text]] } {
.theTopPane.theQueue${inNumber}.lb itemconfigure
[lindex $theQueueElement 0] -fill Grey
} else {
.theTopPane.theQueue${inNumber}.lb itemconfigure
[lindex $theQueueElement 0] -fill Green
}
}
}
#restore selection. If the selected item was removed, this will
not select anything.
.theTopPane.theQueue${inNumber}.lb selection set $theSelection
#update the selection dependant UI, since we may have deselected
what was selected before.
ListboxSelected $inNumber $inName
}
A few other procs (buttons and the ListboxSelect) do only a 'selection
get' on it.
I'm assuming it's a BWidget ListBox bug because the stack trace above
does not originate in my code, so it probably happens as a response to
an event that causes a resize (changing the text of some other things
in the same grid I think). I'm not setting up any bindings of my own
on the resize event.
And 2007/07/11:13:48:07 is indeed the name of one of the items, and I
assume it was removed by the UpdateUIForMachine function just before
the crash. (the ListboxSelected at the end can cause resizes because
it fills out other things in the grid based on the selection. However,
if I understand the event queue correctly, that should not be a
problem, as no recursion is involved here (I don't call update/vwait/
whatever from ListboxSelected, it just does a configure -text on a
bunch of standard labels, based on data in the same myListArray).
Other possibilties: I work with the Threads extension, and the
UpdateUIForMachine is called by the other thread, but through sending
an event to the main Thread, like this:
thread::send $::tMainThreadID {UpdateUIForMachine 1 a}
Finally, I also use an iwidget listbox in the same UI, and for that I
have added this code, although I cna't see any interactions here right
away:
# Replace the mousewheel bindings. This is all taken from TIP
171 :
# http://www.tcl.tk/cgi-bin/tct/tip/171.html
# TIP 171 is currently (may2007) marked as something to be done in
# Tcl/tk 8.6, which is still faar away (8.5 is in alfa)
# when TIP 171 is implemetented, this should be removed.
## START CODE FROM TIP 171
set mw_classes [list Text Listbox Table TreeCtrl]
foreach class $mw_classes { bind $class <MouseWheel> {} }
if {[tk windowingsystem] eq "x11"} {
foreach class $mw_classes {
bind $class <4> {}
bind $class <5> {}
}
}
proc ::tk::MouseWheel {wFired X Y D {shifted 0}} {
# Set event to check based on call
set evt "<[expr {$shifted?{Shift-}:{}}]MouseWheel>"
# do not double-fire in case the class already has a
binding
if {[bind [winfo class $wFired] $evt] ne ""} { return }
# obtain the window the mouse is over
set w [winfo containing $X $Y]
# if we are outside the app, try and scroll the focus
widget
if {![winfo exists $w]} { catch {set w [focus]} }
if {[winfo exists $w]} {
if {[bind $w $evt] ne ""} {
# Awkward ... this widget has a MouseWheel binding, but
to
# trigger successfully in it, we must give it focus.
catch {focus} old
if {$w ne $old} { focus $w }
event generate $w $evt -rootx $X -rooty $Y -delta $D
if {$w ne $old} { focus $old }
return
}
# aqua and x11/win32 have different delta handling
if {[tk windowingsystem] ne "aqua"} {
set delta [expr {- ($D / 30)}]
} else {
set delta [expr {- ($D)}]
}
# scrollbars have different call conventions
if {[string match "*Scrollbar" [winfo class $w]]} {
catch {tk::ScrollByUnits $w \
[string index [$w cget -orient] 0] $delta}
} else {
set cmd [list $w [expr {$shifted ? "xview" : "yview"}] \
scroll $delta units]
# Walking up to find the proper widget handles cases like
# embedded widgets in a canvas
while {[catch $cmd] && [winfo toplevel $w] ne $w} {
set w [winfo parent $w]
}
}
}
}
bind all <MouseWheel> [list ::tk::MouseWheel %W %X %Y %D 0]
bind all <Shift-MouseWheel> [list ::tk::MouseWheel %W %X %Y %D
1]
if {[tk windowingsystem] eq "x11"} {
# Support for mousewheels on Linux/Unix commonly comes
through
# mapping the wheel to the extended buttons.
bind all <4> [list ::tk::MouseWheel %W %X %Y 120]
bind all <5> [list ::tk::MouseWheel %W %X %Y -120]
}
## END CODE FROM TIP 171
}
> And this procs use it extensively:
Sorry, let's try that again looking a bit better:
proc UpdateUIForMachine { inNumber inName } {
#update UI by removing everything, then re-adding
#everything. Save selection (if possible)
set theSelection [.theTopPane.theQueue${inNumber}.lb \
selection get]
.theTopPane.theQueue${inNumber}.lb delete \
[.theTopPane.theQueue${inNumber}.lb items]
foreach theQueueElement $::myListArray($inName) {
.theTopPane.theQueue${inNumber}.lb \
insert end [lindex $theQueueElement 0] \
-text [lindex $theQueueElement 1] -indent 0
if { [lindex $theQueueElement 2] == "Current" } {
if { 0 == [string compare -nocase -length 8 \
Finished" [.theTopPane.theStatus${inNumber} \
cget -text]] } {
.theTopPane.theQueue${inNumber}.lb \
itemconfigure [lindex $theQueueElement 0] \
-fill Grey
} else {
.theTopPane.theQueue${inNumber}.lb itemconfigure \
[lindex $theQueueElement 0] -fill Green
}
}
}
#restore selection. If the selected item was removed,
#this will not select anything.
.theTopPane.theQueue${inNumber}.lb selection set $theSelection
#update the selection dependant UI, since we may have
#deselected what was selected before.
ListboxSelected $inNumber $inName
}
(Not much better, but at least no line wraps.)
However, I now see that listbox calls 'update idletasks' in
ListBox::_redraw_items, which means that right at that time, the
commands in the event queue that change the contents of the listbox
may be run?
And, looking through the bwidget code,
ListBox::_resize
calls:
ListBox::_redraw_listbox $path
if {[Widget::cget $path -selectfill]} {
_update_select_fill $path
}
which means that when _update_select_fill is run, the $path it is
running on may have been deleted by events that ran during
_redraw_listbox (_redraw_listbox -> _redraw_items -> update
idletasks), I think?
Run the following script, hit the button, and on my 8.4.14 tcl on
windows, you get the error mentioned above. (Also, in the comments,
you'll see that attempting to name an item anything with :: in the
name, or : at the end will cause an error. Not sure if that is
considered a bug though.)
Should I report this somewhere?
#attempt to reproduce the error described here:
# http://groups.google.com/group/comp.lang.tcl/browse_thread/thread/97c4892c68312e6b?hl=en
package require BWidget
#create a ListBox in a ScrolledWindow
#Note: without -selectfill true, there is no error!
ScrolledWindow .sw
ListBox .sw.lb -selectfill true
.sw setwidget .sw.lb
button .b -command {testBug} -text "Show Bug!"
#drop the button and listbox on a grid, make the column resizable
grid .sw -column 0 -row 0 -sticky we
grid .b -column 1 -row 0 -sticky we
#without this, there is no error:
grid columnconfigure . 0 -weight 1
#for some reason, it does not happen at the default size.
wm minsize . 500 100
#fill the list
#Note: attempting to insert an item named "a:" causes a
#'parent namespace does not exist' error during insert.
#Same with anything that inclusdes "::" in the name.
.sw.lb insert end "a1" -text "a"
.sw.lb insert end "a2" -text "a"
.sw.lb selection set {a1}
proc testBug { } {
#remove selected item
.sw.lb delete [.sw.lb selection get]
#now cause a resize of the listbox by enlarging the button text
.b configure -text "[.b cget -text]aaaa"
}
> .sw.lb insert end "a2" -text "a"
Only one item is needed, so this line isn't needed.
> proc testBug { } {
> #remove selected item
> .sw.lb delete [.sw.lb selection get]
> #now cause a resize of the listbox by enlarging the button text
> .b configure -text "[.b cget -text]aaaa"
> }
And I've worked around the bug by changing the last line in that
function to
after 1 [list .b configure -text "[.b cget -text]aaaa"]
Arnt
> Should I report this somewhere?
yes, please visit http://tcllib.sf.net/ and report the bug, assigning
it to bwidget.
It is always helpful to get bug reports. Be sure to mention what
platform you are using, what version of bwidget, whether it is
activetcl, something you built yourself, etc.
Good job narrowing down the problem!
> yes, please visit http://tcllib.sf.net/and report the bug, assigning
> it to bwidget.
Filed as Request ID 1752755 . For others running into this: doing an
'update idletasks' after changing the listbox contents but before it
needs to resize will also fix this.