python 3.2.3 виджет масштаба Tkinter зависает при вложении в фрейм

У меня относительно мало опыта работы с Tk, но я уже делал несколько простых проектов, и это поставило меня в тупик:

У меня есть виджет масштаба, который представляет ползунок времени. Threading.Timer срабатывает через определенные промежутки времени, чтобы перемещать виджет масштаба. При нажатии кнопки нить отключена; при нажатии кнопки поток снова активируется. Таким образом, пользователь может щелкнуть ползунок времени, чтобы изменить положение «времени» приложения. Отключение потока во время операции пользовательского интерфейса до сих пор предотвращало сбои, которые у меня были, которые, как я предполагал, были связаны с безопасностью потоков (по-видимому, недетерминированные сбои происходили примерно в то время, когда поток пытался обновить ползунок).

Единственными другими «активными» элементами пользовательского интерфейса являются кнопки, которые настраиваются с помощью параметра command=fnc. Масштаб - единственное место, где я использую bind(). Приложение является полноэкранным, поэтому я использую self.root.overridedirect(1) и геометрию() на корневом уровне, чтобы удалить меню и границы и сделать его полноэкранным.

Я изменил код, поэтому виджет масштабирования теперь находится внутри фрейма, а не на абсолютном корневом уровне:

self.scale_timescrub = tkinter.Scale(self.root.master, from_=0, to=60, width=height_one, orient=tkinter.HORIZONTAL, showvalue=0)
self.scale_timescrub.grid(row=2,column=0,sticky=tkinter.N+tkinter.S+tkinter.E+tkinter.W)
self.scale_timescrub.bind('<Button-1>', self.press_scrub)
self.scale_timescrub.bind('<ButtonRelease-1>', self.release_scrub)

Теперь приложение зависает всякий раз, когда я пытаюсь выполнить какие-либо операции пользовательского интерфейса с виджетом масштабирования. Нажатие в любом месте ползунка привязывает процессор, и приложение никогда не возвращается. Я знаю, что это связано с воспитанием детей, потому что, когда я взламываю приложение, чтобы вернуть масштаб на корневой уровень, оно снова работает. Виджет, кажется, никогда не вводит свои связанные функции, такие как press_scrub, когда он связан с фреймом.

Может ли кто-нибудь посоветовать мне, что я делаю неправильно здесь?

Запуск Python 3.2.3 64b в Windows 7.


person Mayur Patel    schedule 21.05.2012    source источник
comment
Остальной код доступен где-нибудь? Было бы определенно полезно иметь минимальный работоспособный пример, с которым мы можем поиграть, демонстрирующий проблему.   -  person mgilson    schedule 21.05.2012


Ответы (2)


Вы написали: «Таймер threading.Timer срабатывает через определенные промежутки времени, чтобы перемещать виджет масштаба». При обработке этого события таймера вы вызываете методы виджета из другого потока? Если да, то это определенно проблема. Вы просто не можете изменить пользовательский интерфейс из другого потока.

Вам никогда не понадобится объект Timer — знаете ли вы, что можете запланировать выполнение чего-то через регулярные промежутки времени, используя after?

Еще одна вещь, которую следует учитывать, но которую нельзя определить, глядя на фрагмент кода, — используете ли вы pack в одном и том же виджете-контейнере. Объединение pack и grid в одном мастере вызовет поведение, которое вы видите. Обычно в таком случае вы увидите такое поведение при запуске, но не всегда.

person Bryan Oakley    schedule 21.05.2012
comment
Я не использую pack() и grid() в одном приложении. Как я пытался указать, при нажатии кнопки я убиваю Thread.timer(), а при нажатии кнопки повторно активирую Thread.timer(). В прошлом не было проблем с правильностью этого, даже если after() может быть лучшим стилем программирования. Я рассмотрю after(), но доказательства указывают на то, что родитель Frame имеет к этому какое-то отношение. - person Mayur Patel; 21.05.2012
comment
@Mayur Patel: Даже если вы убьете таймер, у вас все равно может быть состояние гонки. Поверьте, проблем с встраиванием виджетов во фреймы не возникает. В вашем коде определенно есть ошибка. Доказательства, кажется, указывают на многопоточность, поскольку документально подтверждено, что Tkinter не является потокобезопасным. - person Bryan Oakley; 21.05.2012
comment
@Mayur Patel: рассмотрите такую ​​возможность: вы нажимаете на ползунок. По-прежнему в основном потоке Tk попытается обновить внешний вид кнопки перед вызовом команды. Теперь, возможно, другому потоку дается приоритет перед отменой таймера, и он также пытается обновить пользовательский интерфейс. Если вы когда-нибудь попытаетесь обновить элемент GUI из другого потока, ваш код в конечном итоге сломается. - person Bryan Oakley; 21.05.2012
comment
Во-первых, кажется, что Tkinter имеет какое-то намерение быть потокобезопасным, но я готов поверить, что это не так. bugs.python.org/issue11077. Во-вторых, при полном отключении потока таймера проблема все еще возникает. Вы считаете, что это условие возможности не выполняется; там только одна нить. Проблемы с потоками имеют тенденцию быть прерывистыми и кажутся недетерминированными. Эта проблема повторяется и возникает немедленно. Конечно, родительские виджеты во фреймах — обычная операция; и, конечно же, в моем коде есть ошибка. Вот почему я прошу помощи. :-) - person Mayur Patel; 21.05.2012

Проблема, похоже, связана с использованием ключевого слова «master» в качестве имени моего полноэкранного объекта Frame. Когда я массово переименовываю self.root.master в self.root.full_frame, все работает, как и ожидалось. Спасибо Брайану за указание на опасность многопоточности, которую я буду использовать для переоценки других аспектов моего приложения. Спасибо mgilson за проявленный интерес к воспроизводимому материалу. Я не сопоставил воспроизводимый перед публикацией, потому что думал, что у меня установлено так много странных конфигураций, что любая из них может быть задействована. В любом случае, спасибо всем. Примечание для себя: ничего не называйте «хозяином».

person Mayur Patel    schedule 21.05.2012