Поле со списком Python tkinter.ttk выдает исключение при выходе

В моем коде Python 3.3 я использую несколько полей со списком из библиотеки ttk, и они работают нормально, но если я использую любой из них, я получаю исключение, когда закрываю окно с помощью кнопки X. Вот пример:

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()

Если вы закроете его без выбора значения, все в порядке, но попробуйте закрыть его после того, как значение было выбрано, он выйдет, но выведет следующую ошибку в командную строку python:

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)

Как я могу это исправить? Я был бы признателен за любую помощь, которую вы могли бы предоставить.

Обновление 1: Моя версия Python — v3.3, я использую в комплекте Tcl/Tk и Tkinter. Пробовал и x86 и x64 версии.

Обновление 2: исключение выдается только в том случае, если я запускаю свой скрипт из командной строки. Он не будет отображаться в режиме ожидания.


person bardosd    schedule 16.03.2013    source источник
comment
Ваш код работает для меня. Я думаю, что проблема в версии Tkinter.   -  person A. Rodas    schedule 16.03.2013
comment
Спасибо за ваш ответ. Моя версия Tcl/Tk — 8.5, а ttk — v0.3.1. Это было частью Python v3.3, когда я его установил. Нашел обновление и скоро напишу свои впечатления после того как попробовал.   -  person bardosd    schedule 16.03.2013
comment
Мне не удалось обновить Tkinter. Tcl/Tk v8.6 находится по ссылке, но мне не удалось интегрировать его в Python. Могу я спросить, какую версию Tkinter и Python вы использовали? Я хотел бы попробовать сценарий на этой версии.   -  person bardosd    schedule 16.03.2013
comment
У меня тоже 8.5 с ттк 0.3.1. Я пробовал ваш код с Python 2.7.3 (изменение импорта) и с Python 3.0.1, и у меня не было никаких проблем. Я надеюсь, что кто-то еще может выйти с проблемой.   -  person A. Rodas    schedule 16.03.2013
comment
Я пробовал v3.0.1. ttk отсутствовал в Tkinter. Я тестировал свой скрипт в IDLE под v3.3. Вроде нормально (не исключение). Но я всегда получал исключение в командной строке.   -  person bardosd    schedule 16.03.2013


Ответы (2)


Это проблема кода привязки Tcl/Tk, используемого в ttk.

На проблему намекает комментарий в файле tcl/tk8.5/ttk/entry.tcl в типичной установке python Tkinter:

## 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.

По сути, отложенный вызов с after не отменяется и больше не может завершиться после закрытия последнего окна и завершения Tk, потому что процедура/функция 'winfo' больше не существует. При запуске IDLE все равно есть окно, поэтому Tk не дорабатывается и ошибка не появляется.

Вы можете исправить это с помощью привязки к сообщению WM_DELETE_WINDOW, которое останавливает таймер повторения. Код для этого будет (в Tcl/Tk):

proc shutdown_ttk_repeat {args} {
    ::ttk::CancelRepeat
}
wm protocol . WM_DELETE_WINDOW shutdown_ttk_repeat

Для Tkinter это должно работать аналогичным образом:

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()
person schlenk    schedule 16.03.2013
comment
Теперь я вижу, в чем проблема. Я попробовал решение Tkinter, но теперь оно печатает следующее исключение и не закрывает окно: Исключение в обратном вызове Tkinter Traceback (последний последний вызов): File C:\Program Files\Python33\lib\tkinter_init_< /i>.py, строка 1442, в __call __ return self.func(*args) TypeError: shutdown_ttk_repeat() отсутствует 1 обязательный позиционный аргумент: 'args' Что я делаю неправильно? Я новичок в питоне, так что извините за нуб. - person bardosd; 17.03.2013
comment
ах, моя ошибка, просто используйте: def shutdown_ttk_repeat() без каких-либо параметров, я предполагал, что он получит аргументы, как версия Tcl. - person schlenk; 17.03.2013
comment
Да, это помогло. :) На этот раз без исключений, но перестала работать кнопка закрытия. Я добавил строку an a.destroy() после строки a.eval('::ttk::CancelRepeat'), и теперь она работает безупречно. Я очень благодарен за вашу помощь! - person bardosd; 18.03.2013

Недавно я столкнулся с похожей проблемой. Сообщение об ошибке точно такое же. Я решил это, добавив a.quit() в метод Exit. (до этого в этом методе был только a.destroy()) Возможно, вы уже решили этот вопрос. Но ответ Шленка мне не подходит. Поэтому я надеюсь, что мой ответ может дать еще один ключ к такому вопросу.

person Jusdesoja    schedule 19.06.2015