2015-09-08 3 views
11

Это в вилке Phoenix wxPython.wxPython thread blocking

Я пытаюсь запустить пару потоков в интересах не блокирования графического интерфейса.

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

Вот функция результата для основных вычислительных потоков:

def on_status_result(self, event): 
    if not self.panel.progress_bar.GetRange(): 
     self.panel.progress_bar.SetRange(event.data.parcel_count) 
    self.panel.progress_bar.SetValue(event.data.current_parcel) 
    self.panel.status_label.SetLabel(event.data.message) 

Вот как я их связывания:

from wx.lib.pubsub.core import Publisher 
PUB = Publisher() 

Вот как я связывание метода:

def post_event(message, data): 
    wx.CallAfter(lambda *a: Publisher().sendMessage(message, data=data)) 

И вот нити. Первые из них не работает, а вторые два сделать:

class PrepareThread(threading.Thread): 
    def __init__(self, notify_window): 
     threading.Thread.__init__(self) 
     self._notify_window = notify_window 
     self._want_abort = False 

    def run(self): 
     while not self._want_abort: 
      for status in prepare_collection(DATABASE, self._previous_id, self._current_id, self._year, self._col_type, 
              self._lock): 
       post_event('prepare.running', status) 
     post_event('prepare.complete', None) 
     return None 

    def abort(self): 
     self._want_abort = True 


class SetupThread(threading.Thread): 
    def __init__(self, notify_window): 
     threading.Thread.__init__(self) 
     self._notify_window = notify_window 
     self._want_abort = False 

    def run(self): 
     while not self._want_abort: 
      do_more_stuff_with_the_database() 
      return None 

    def abort(self): 
     self._want_abort = True 


class LatestCollectionsThread(threading.Thread): 
    def __init__(self, notify_window): 
     threading.Thread.__init__(self) 
     self._notify_window = notify_window 
     self._want_abort = False 

    def run(self): 
     while not self._want_abort: 
      do_stuff_with_my_database() 
      return None 

    def abort(self): 
     self._want_abort = True 

prepare_collection это функция, которая дает Status объектов, выглядит следующим образом:

class Status: 
    def __init__(self, parcel_count, current_parcel, total, message): 
     self.parcel_count = parcel_count 
     self.current_parcel = current_parcel 
     self.total = total 
     self.message = message 

Вот как я создаю/запуск/подписок PrepareThread:

MainForm(wx.Form): 
    prepare_thread = PrepareThread(self) 
    prepare_thread.start() 

    self.pub = Publisher() 
    self.pub.subscribe(self.on_status_result, 'prepare.running') 
    self.pub.subscribe(self.on_status_result, 'prepare.complete') 

    def on_status_result(self, event): 
     if not self.panel.progress_bar.GetRange(): 
      self.panel.progress_bar.SetRange(event.data.parcel_count) 
     self.panel.progress_bar.SetValue(event.data.current_parcel) 
     self.panel.status_label.SetLabel(event.data.message) 

Я попытался гася prepare_collection с range(10), но я до сих пор никогда не попал накануне nt обработчик.

+0

эй morgan извините ... у меня, вероятно, не будет шанса сегодня вечером пересмотреть это ... просто супер занято:/ –

+0

@joran Все хорошо. –

+0

oh dang ... извините ... я попробую и помогу вам в этот уик-энд его просто сумасшедшая неделя –

ответ

4

Это может быть весьма вовлеченным ответом, и это немного трудно работать, какие именно из ваши фрагменты, которые у вас есть в каждой части вашего кода (т.е. какие файлы они все живут). Я предполагаю, что вы хотите использовать метод pubsub (что я считаю хорошей идеей). Если структура вашего реального проекта является очень сложной, возможно, потребуется более сложное управление Publisher чем я пользуюсь здесь - дайте мне знать ...


Здесь идет - я поставлю спойлер первым - вот a one file solution за то, что вам кажется, - панель с кнопкой, чтобы начать подготовку темы, строку состояния и обработчик для завершения подготовки. Протестировано с помощью wxPython Phoenix 64-битного Python 3 и старомодного wxPython на Python 2.7. Оба в Windows - но я могу прикрутить его в Linux-окне, если это необходимо.

Резюмируя важные (не шаблонные) биты этого файла

Вам нужен единый Publisher объект, который ваши темы отправлять сообщения и ваш основной поток (MainForm в вашем примере я предполагаю) при подписке. Вы могли бы управлять Publisher для каждого потока, но я думаю, что здесь вам нужен только один для PrepareThread, поэтому я собираюсь пойти с этой моделью на данный момент.

В верхней части файла, используйте

from wx.lib.pubsub import pub 

Это позволяет pubsub управлять инстанцирование один Publisher объекта.

В вашей теме, как вы делаете, публиковать сообщения там - небольшая доплату к вашему post_event помощнику:

def post_event(message, data): 
    wx.CallAfter(lambda *a: pub.sendMessage(message, data=data)) 

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

pub.subscribe(self.on_status_result, 'prepare.running') 
pub.subscribe(self.on_status_finished, 'prepare.complete') 

Вы можете оставить on_status_result как это и определить аналогичный on_status_finished. В моем примере у меня было это позже, что позволило создать новую кнопку, позволяющую вам выполнять определенную работу.

N.B. Вам нужно быть осторожным при указании полезной нагрузки ваших сообщений - pubsub содержит довольно много информации о том, что там ожидает, и это меня поймало сначала.


P.S. Прямо в конце подготовки этого ответа - я нашел this blog post. В нем говорится что-то похожее на то, что у меня выше, поэтому я не буду воспроизводить его, но они используют другой метод создания Publisher(), как ваш первоначальный пример - подразумевая, что это тоже должно работать. Вы можете предпочесть формулировку там. Simlarly - вы можете найти this wxPython wiki страница полезная.

+0

Это похоже на то, что мне нужно. Дайте мне час, а затем я попробую! –

+0

Он работает! J, ты красивый человек. –

+0

хорошая работа .. извините, я занят ... в wx2.8 звонки издателю sendMessage подписываются для всех экземпляров (на самом деле Publisher() может возвращать тот же экземпляр ...) –

4

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

wxPython принял это во внимание, и любые методы, вызванные с wx.CallAfter, будут вызываться из основного цикла программы, который всегда работает в основном потоке. это в сочетании с модулем wx.pubsub позволяет создать свою собственную работу кадра события легко ... что-то вроде этого

def MyPostEvent(event_name,event_data): 
    #just a helper that triggers the event with wx.CallAfter 
    wx.CallAfter(lambda *a:Publisher().sendMessage(event_name,data=event_data)) 

#then to post an event 

MyPostEvent("some_event.i_made_up",{"payload":True}) 

#then in your main thread subscribe 

def OnEventHandler(evt): 
    print "EVT.data",evt.data 

pub = Publisher() 
pub.subscribe("some_event.i_made_up",OnEventHandler) 
+0

Будет ли «some_event.i_made_up» быть тем же самым, что и в MyPostEvent?Кроме того, это работает с Phoenix? –

+0

он должен работать на phoenix (может потребоваться небольшая модификация ... но в целом это понятие) –

+0

да, это то, что вы пройдете: P (см. Примеры по извлечению и испусканию события) –

Смежные вопросы