In my Python 3.3 code I use some comboboxes from the ttk library, and they function fine, but if I use any of them, I got an exception when I close the window with the X button. This is an example:
from tkinter import Tk,Label,Button
from tkinter import ttk
from tkinter.ttk import Combobox
def cbox_do(event):
'Used for cbox.'
clabel.config(text=cbox.get())
a = Tk()
cbox = Combobox(a, value=('Luke','Biggs','Wedge'), takefocus=0)
cbox.bind("<<ComboboxSelected>>", cbox_do)
cbox.pack()
clabel = Label(a)
clabel.pack()
a.mainloop()
If you close it without selecting a value, everithing is fine, but try to close it after a value was selected, it exits but prints the following error into the python command line:
can't invoke "winfo" command: application has been destroyed
while executing
"winfo exists $w"
(procedure "ttk::entry::AutoScroll" line 3)
invoked from within
"ttk::entry::AutoScroll .41024560"
(in namespace inscope "::" script line 1)
invoked from within
"::namespace inscope :: {ttk::entry::AutoScroll .41024560}"
("uplevel" body line 1)
invoked from within
"uplevel #0 $Repeat(script)"
(procedure "ttk::Repeat" line 3)
invoked from within
"ttk::Repeat"
("after" script)
How could I fix it? I would be grateful for any help you could provide.
Update 1: My Python version is v3.3, I use the bundled Tcl/Tk and Tkinter. I tried both the x86 and x64 versions.
Update 2: The exception is thrown only if I run my script from command line. It wont show up in Idle.
This is a problem with the Tcl/Tk binding code used in ttk.
The problem is hinted at by a comment in tcl/tk8.5/ttk/entry.tcl file in a typical python Tkinter installation:
## AutoScroll
# Called repeatedly when the mouse is outside an entry window
# with Button 1 down. Scroll the window left or right,
# depending on where the mouse is, and extend the selection
# according to the current selection mode.
#
# TODO: AutoScroll should repeat faster (50ms) than normal autorepeat.
# TODO: Need a way for Repeat scripts to cancel themselves.
Basically a deferred call with after
isn't canceled and cannot complete anymore after the last window gets closed and Tk is finalized, because the procedure/function 'winfo' no longer exists. When you run IDLE, there still is a window, so Tk does not get finalized and the error does not show up.
You can fix this with a binding on the WM_DELETE_WINDOW
message, which stops the repeat timer. The code for that would be (in Tcl/Tk):
proc shutdown_ttk_repeat {args} {
::ttk::CancelRepeat
}
wm protocol . WM_DELETE_WINDOW shutdown_ttk_repeat
For Tkinter it should work in a similar way:
from tkinter import Tk,Label,Button
from tkinter import ttk
from tkinter.ttk import Combobox
def cbox_do(event):
'Used for cbox.'
clabel.config(text=cbox.get())
a = Tk()
cbox = Combobox(a, value=('Luke','Biggs','Wedge'), takefocus=0)
cbox.bind("<<ComboboxSelected>>", cbox_do)
cbox.pack()
clabel = Label(a)
clabel.pack()
def shutdown_ttk_repeat():
a.eval('::ttk::CancelRepeat')
a.destroy()
a.protocol("WM_DELETE_WINDOW", shutdown_ttk_repeat)
a.mainloop()