Python Tkinter: приложение зависает при изменении заголовка окна через событие обратного вызова - PullRequest
3 голосов
/ 23 августа 2011

Я создаю простой пользовательский интерфейс Python через Tkinter, и я хотел бы использовать self.title для изменения заголовка окна при генерировании события обратного вызова.

Если я привяжу событие к кнопке или вызову обработчик события непосредственно в потоке Tk, заголовок окна изменится, как и ожидалось. Однако я намереваюсь вызвать это событие отдельным потоком, и обнаружил, что использование title в обработчике события обратного вызова приводит к зависанию приложения.

Другие задачи, которые у меня есть в обработчике событий (например, обновление метки), работают нормально, поэтому я должен предположить, что событие вызывается правильно. Я пробовал wm_title вместо title, но не увидел разницы. Я покопался и не нашел ничего странного в использовании title, просто назовите его со строкой, чтобы установить заголовок.

Вот урезанный пример, который повторяет проблему (я использую v2.7.1 на WinXP FYI); приложение работает нормально в течение 10 секунд (может переместить окно, изменить размер и т. д.), после чего Timer генерирует событие, а затем приложение останавливается.

import Tkinter
import threading

class Gui(Tkinter.Tk):

    def __init__(self, parent=None):
        Tkinter.Tk.__init__(self, parent)
        self.title('Original Title')
        self.label = Tkinter.Label(self, text='Just a Label.',
            width=30, anchor='center')
        self.label.grid()

        self.bind('<<change_title>>', self.change_title)

        timer = threading.Timer(10, self.event_generate, ['<<change_title>>'])
        timer.start()

    def change_title(self, event=None):
        self.title('New Title')

G = Gui(None)
G.mainloop()

Ответы [ 3 ]

2 голосов
/ 02 января 2014

Я столкнулся с той же проблемой, когда пользовательский интерфейс зависает при вызове self.title () из потока, отличного от основного потока. Tkinter ожидает, что все элементы пользовательского интерфейса будут выполнены в одном и том же потоке (основной поток).

Мое решение состояло в том, чтобы отдельный поток помещал функции в очередь. Очередь периодически обслуживается основным потоком, используя функцию after (ms), предоставляемую Tkinter. Вот пример с вашим кодом:

import Tkinter
import threading
from Queue import Queue, Empty

class Gui(Tkinter.Tk):

    def __init__(self, parent=None):
        Tkinter.Tk.__init__(self, parent)
        self.ui_queue = Queue()
        self._handle_ui_request()
        self.title('Original Title')
        self.label = Tkinter.Label(self, text='Just a Label.',
            width=30, anchor='center')
        self.label.grid()

        self.bind('<<change_title>>', self.change_title)

        timer = threading.Timer(1, self.event_generate, ['<<change_title>>'])
        timer.start()

    def change_title(self, event=None):
        # Separate the function name, it's args and keyword args, 
        # and put it in the queue as a tuple.
        ui_function = (self.title, ('New Title',), {})
        self.ui_queue.put(ui_function)

    def _handle_ui_request(self):
        '''
        Periodically services the UI queue to handles UI requests in the main thread.
        '''
        try:
            while True:
                f, a, k = self.ui_queue.get_nowait()
                f(*a, **k)
        except Empty:
            pass

        self.after(200, self._handle_ui_request)


G = Gui(None)
G.mainloop()
0 голосов
/ 24 августа 2011

Я тоже пробовал с 2.6.2 (Windows), но заголовок / заголовок не изменился. Никаких ошибок во время выполнения.

0 голосов
/ 23 августа 2011

Ну, ваш код на самом деле работает нормально для меня.

За исключением того, что при прерывании до десяти секунд он говорит: "RuntimeError: основной поток не в основном цикле"

I 'используя Python 2.6.6 под Ubuntu 10.10

Надеюсь, это помогло.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...