Начало потока Python по требованию / замораживание соединения из графического интерфейса wxPython - PullRequest
0 голосов
/ 17 мая 2010

Я пытаюсь создать очень простой графический интерфейс wxPython, который отслеживает и отображает внешние данные. Есть кнопка включения / выключения мониторинга. Когда мониторинг включен, графический интерфейс обновляет пару wx StaticLabels с данными в реальном времени. Когда мониторинг отключен, графический интерфейс бездействует.

Способ, которым я пытался его построить, был с довольно простой версией Python Thread. При нажатии кнопки «Начать мониторинг» программа порождает поток, который обновляет метки информацией в реальном времени. При нажатии кнопки «Остановить мониторинг» вызывается thread.join (), и он должен остановиться.

Функция запуска работает, и обновление данных в режиме реального времени работает отлично, но когда я нажимаю «Стоп», вся программа зависает. Я запускаю это на Windows 7 64-битной, так что я получаю обычный диалог Windows «Эта программа перестала отвечать».

Вот соответствующий код:

class MonGUI(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        ...
        ... other code for the GUI here ...
        ...
        # Create the thread that will update the VFO information
        self.monThread = Thread(None, target=self.monThreadWork)
        self.monThread.daemon = True
        self.runThread = False

    def monThreadWork(self):
        while self.runThread:
            ...
            ... Update the StaticLabels with info
            ... (This part working)
            ...

    # Turn monitoring on/off when the button is pressed.
    def OnClick(self, event):
        if self.isMonitoring:
            self.button.SetLabel("Start Monitoring")
            self.isMonitoring = False
            self.runThread = False
            self.monThread.join()
        else:
            self.button.SetLabel("Stop Monitoring")
            self.isMonitoring = True

            # Start the monitor thread!
            self.runThread = True
            self.monThread.start()

Я уверен, что есть лучший способ сделать это, но я довольно плохо знаком с программированием GUI и потоками Python, и это было первое, что я придумал.

Итак, почему нажатие кнопки, чтобы остановить поток, приводит к зависанию всего этого?

Ответы [ 3 ]

2 голосов
/ 18 мая 2010

В wxPython операции с графическим интерфейсом должны выполняться в основном потоке . В некоторых местах вашего кода вы вызываете GUI из другого потока.

Самое простое решение - использовать wx.CallAfter(). Строка кода будет выглядеть как

wx.CallAfter(self.button.SetLabel, “Start Monitoring”)

, который после завершения функции вызовет self.button.SetLabel («Начать мониторинг») из основного потока.

Есть и другие способы решения этой проблемы, такие как использование очереди потоков Python или wx.PostEvent, но начните с CallAfter, потому что это проще всего.

Другие проблемы также актуальны, например, вы не можете перезапустить тот же поток, но с помощью CallAfter остановит сбой .

1 голос
/ 18 мая 2010

Tom10 имеет правильную идею, избегая обновлений пользовательского интерфейса из потока монитора.

Кроме того, вероятно, не очень хорошая идея блокировать вызов self.monThread.join() в вашем потоке пользовательского интерфейса. Если вы хотите, чтобы пользовательский интерфейс дал некоторую обратную связь о том, что поток монитора фактически закончился, сделайте так, чтобы monThreadWorker выполнил wx.CallAfter () или wx.PostEvent () непосредственно перед его закрытием.

Избегайте всего, что блокирует ваш поток пользовательского интерфейса, и вы избежите взаимоблокировки пользовательского интерфейса

1 голос
/ 18 мая 2010

Скорее всего, он висит на join([timeout]), который блокирует вызывающий поток до тех пор, пока поток, чей метод join() вызывается, не завершится - либо в обычном режиме, либо через необработанное исключение - либо до наступления необязательного тайм-аута.

У вас есть какой-то внутренний цикл в потоке или блокирующий вызов, который ждет какого-то источника данных, который может никогда не прийти? Когда я писал основную последовательную программу, которая собирала данные COM-порта, она иногда зависала, потому что функция чтения в моем потоке блокировалась, пока не получила что-то.

Я бы добавил несколько отладочных операторов print, чтобы увидеть, что происходит.

Изменить:

Я бы также использовал threading.Event() вместо логического флага, например ::

# in the init code...
self.runThread = threading.Event()

# when starting thread...
self.runThread.set()
self.monThread.start()

# in the thread...
while self.runThread.isSet():
    pass # do stuff

# killing the thread...
self.runThread.clear()
self.monThread.join()

Это не должно заставить работать по-другому, но это немного более безопасный способ сделать это.

...