wxPython: Threading GUI -> Использование пользовательского обработчика событий - PullRequest
2 голосов
/ 27 февраля 2010

Я пытаюсь узнать, как запустить поток из основного приложения с графическим интерфейсом, чтобы выполнять отправку / получение моего последовательного порта при сохранении моего графического интерфейса. Мои лучшие попытки поиска Google привели меня в wxpython вики на: http://wiki.wxpython.org/LongRunningTasks, который предоставляет несколько примеров. Я остановился на изучении первого примера, включающего запуск рабочего потока при нажатии определенной кнопки.

У меня проблемы с пониманием определения пользовательского события:

def EVT_RESULT(win, func):
    """Define Result Event."""
    win.Connect(-1, -1, EVT_RESULT_ID, func)

class ResultEvent(wx.PyEvent):
    """Simple event to carry arbitrary result data."""
    def __init__(self, data):
        """Init Result Event."""
        wx.PyEvent.__init__(self)
        self.SetEventType(EVT_RESULT_ID)
        self.data = data

В первую очередь

def EVT_RESULT(win, func):
    """Define Result Event."""
    win.Connect(-1, -1, EVT_RESULT_ID, func)

Я думаю, что EVT_RESULT размещен вне классов, чтобы сделать его доступным для вызова обоими классами (делая его глобальным?)

И ... основное приложение с графическим интерфейсом отслеживает прогресс потока с помощью:

# Set up event handler for any worker thread results
EVT_RESULT(self,self.OnResult)

Я также замечаю, что во многих примерах писатель использует

from wx import *

они просто связывают вещи

EVT_SOME_NEW_EVENT(self, self.handler)

в отличие от

wx.Bind(EVT_SOME_NEW_EVENT, self.handler)

Что не помогает мне понять это быстрее. Спасибо,

Ответы [ 3 ]

4 голосов
/ 27 февраля 2010

Это старый стиль определения пользовательских событий. См. руководство по миграции для получения дополнительной информации.

Взято из руководства по миграции:

Если вы создаете свое собственное событие типы и функции EVT_ *, и вы хочу иметь возможность использовать их с Привязать метод выше, то вы должны измените свой EVT_ * на экземпляр wx.PyEventBinder вместо функция. Например, если вы привыкли есть что-то вроде этого:

myCustomEventType = wxNewEventType()
def EVT_MY_CUSTOM_EVENT(win, id, func):
    win.Connect(id, -1, myCustomEventType, func)

Измените это так:

myCustomEventType = wx.NewEventType()
EVT_MY_CUSTOM_EVENT = wx.PyEventBinder(myCustomEventType, 1)

Вот еще один пост , который я написал с несколькими примерами программ, которые делают именно то, что вы ищете.

3 голосов
/ 27 февраля 2010

Вы можете определить события следующим образом:

from wx.lib.newevent import NewEvent

ResultEvent, EVT_RESULT = NewEvent()

Вы публикуете событие так:

wx.PostEvent(handler, ResultEvent(data=data))

Свяжите это так:

def OnResult(event):
    event.data

handler.Bind(EVT_RESULT, OnResult)

Но если вам просто нужно сделать вызов из неосновного потока в основном потоке, вы можете использовать wx.CallAfter, здесь является примером.

Пользовательские события полезны, когда вы не хотите жестко кодировать, кто за что отвечает (см. шаблон проектирования наблюдателя ). Например, допустим, у вас есть главное окно и пара дочерних окон. Предположим, что некоторые дочерние окна необходимо обновить, когда в главном окне происходят определенные изменения. В этом случае главное окно может напрямую обновлять эти дочерние окна, но более элегантный подход заключается в том, чтобы определить пользовательское событие и сделать так, чтобы главное окно отправляло его себе (и не беспокоилось о том, кому нужно реагировать на него). Тогда дети, которым нужно реагировать на это событие, могут сами сделать это, связавшись с ним (и если их несколько, важно, чтобы они вызывали event.Skip(), чтобы вызывались все связанные методы).

0 голосов
/ 02 марта 2010

Возможно, вы захотите использовать потоки и очереди Python, а не пользовательские события. У меня есть программа wxPython ( OpenSTV ), которая загружает большие файлы, которые вызывали зависание графического интерфейса во время загрузки. Чтобы предотвратить зависание, я отправляю поток для загрузки файла и использую очередь для связи между графическим интерфейсом и потоком (например, для передачи исключения в графический интерфейс).

  def loadBallots(self):
    self.dirtyBallots = Ballots()
    self.dirtyBallots.exceptionQueue = Queue(1)
    loadThread = Thread(target=self.dirtyBallots.loadUnknown, args=(self.filename,))
    loadThread.start()

    # Display a progress dialog
    dlg = wx.ProgressDialog(\
      "Loading ballots",
      "Loading ballots from %s\nNumber of ballots: %d" % 
      (os.path.basename(self.filename), self.dirtyBallots.numBallots),
      parent=self.frame, style = wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME
    )
    while loadThread.isAlive():
      sleep(0.1)
      dlg.Pulse("Loading ballots from %s\nNumber of ballots: %d" %
                (os.path.basename(self.filename), self.dirtyBallots.numBallots))
    dlg.Destroy()

if not self.dirtyBallots.exceptionQueue.empty():
  raise RuntimeError(self.dirtyBallots.exceptionQueue.get())
...