0

Ich habe eine App mit einem langen Hintergrundprozess (Daemon) und einer Wx-basierten Benutzeroberfläche. Der Hintergrundprozess platziert Nachrichten in eine multiprocessing.JoinableQueue(), von der die UI lesen sollte, um Statusaktualisierungen während der Ausführung des Daemon-Prozesses zu erhalten. Das Problem, das ich habe, ist, während die AwaitStatusReportThreadClass-Instanz die Nachricht an die Benutzeroberfläche übergibt, die Benutzeroberfläche reagiert nicht mehr. Wenn Sie den folgenden Code ausführen und dann versuchen, die Größe des Fensters zu ändern, wird die App als nicht reagierend angezeigt.Wie kann ich einen Thread auf eine verbindungsfähige Warteschlangennachricht reagieren lassen, um meine wx Phoenix UI zu aktualisieren?

Ich laufe dies auf Windows 7 Python27 32bit, WxPython 4.0.0a3 (Phoenix)

Meine App mit verschiedenen Python-Dateien mit strukturiert sind, aber ich konnte den Fehler mit dem folgenden Code reproduzieren. Es ist ziemlich einfach. Ich habe eine Klasse namens AwaitStatusReportThreadClass erstellt, um die Pub/Sub-Methoden here zu verwenden, um die Benutzeroberfläche zu aktualisieren. Die init-Methode dieser Klasse hat eine Endlosschleife, die nach Statusmeldungen sucht. Wenn einer gefunden wird, wird die StatusBar-Nachricht aktualisiert.

import multiprocessing 
import threading 
import wx 
import sys 
from wx.lib.pubsub import pub 


class AwaitStatusReportThreadClass(threading.Thread): 
    """This class should pass messages to the UI class""" 
    def __init__(self, messageQueue): 
     """Init worker Thread Class""" 
     self.messageQueue = messageQueue 

     threading.Thread.__init__(self) 
     self.start() 

    def run(self): 
     """This code executes when the thread is run""" 

     KeepRunningStatusThread = True 
     while KeepRunningStatusThread: 
      sys.stdout.flush() 

      try: 
       msg = self.messageQueue.get() 
       self.messageQueue.task_done() 
      except: 
       pass 

      if msg == "Shutdown": 
       # Kill this thread 
       KeepRunningStatusThread = False 

      else: 
       pub.sendMessage("UI", msg=msg) 


class UI2(wx.Frame): 
    """This class is the UI""" 
    def __init__(self, taskQueue, messageQueue, stopQueue, parent=None): 

     self.taskQueue = taskQueue 
     self.messageQueue = messageQueue 
     self.stopQueue = stopQueue 

     wx.Frame.__init__(self, parent, title="TestApp") 

     #Main panel 
     sizerMain = wx.BoxSizer(wx.VERTICAL) 

     # Add the status bar 
     panelStatusBar = wx.Panel(self) 
     sizerStatusBar = wx.BoxSizer(wx.HORIZONTAL) 
     panelStatusBar.SetSizer(sizerStatusBar) 

     self.StatusBar_Main = wx.StatusBar(panelStatusBar, wx.NewId()) 
     sizerStatusBar.Add(self.StatusBar_Main, 1, wx.EXPAND | wx.LEFT | wx.RIGHT, 2) 

     #Add the status bar sizer to the main sizer 
     sizerMain.Add(panelStatusBar, 0, wx.EXPAND) 

     #Add the progress bar 
     panelProgressBar = wx.Panel(self) 
     sizerProgressBar = wx.BoxSizer(wx.HORIZONTAL) 
     panelProgressBar.SetSizer(sizerProgressBar) 
     self.Gauge_ProgressBar = wx.Gauge(panelProgressBar, wx.NewId()) 
     sizerProgressBar.Add(self.Gauge_ProgressBar, 1, wx.EXPAND) 
     sizerMain.Add(panelProgressBar,0,wx.EXPAND) 

     #Layout the frame 
     self.SetSizer(sizerMain) 
     self.SetAutoLayout(1) 
     sizerMain.Fit(self) 
     self.Layout() 
     self.Show(show=True) 

     #Subscribe to messages from the messageQueue 
     pub.subscribe(self.HandleStatusUpdate, "UI") 
     AwaitStatusReportThreadClass(self.messageQueue) 

    def HandleStatusUpdate(self, msg): 
     """ 
     This def updates the UI from a pubsub subscription 

     """ 
     StatusBar = self.StatusBar_Main 
     StatusBar.PushStatusText(msg) 


if __name__ == "__main__": 

    #Make multiprocessing work when app is frozen 
    multiprocessing.freeze_support() 

    taskQueue = multiprocessing.JoinableQueue() #Specifies tasks to be completed by the GenInst process 
    messageQueue = multiprocessing.JoinableQueue() #Holds status messages/progress messages to update the message zone and progress bar in the UI 
    stopQueue = multiprocessing.JoinableQueue() #Allows cancel operation button to function 

    messageQueue.put("Please wait while the GenInst background process starts...") 

    #Start the UI 
    app = wx.App(False) 
    frame = UI2(taskQueue=taskQueue, messageQueue=messageQueue, stopQueue=stopQueue) 
    app.MainLoop() 

Irgendwelche Ratschläge?

Antwort

0

Nun, die Lösung war einfach. Ich werde es hier lassen, für den Fall, dass es jemandem in der Zukunft hilft.

Alles, was benötigt wurde, um die AwaitStatusReportThreadClass zu strukturieren, um wx.CallAfter zu verwenden, um die Nachricht zu veröffentlichen. Ich fügte der Klasse eine postMessage-Funktion hinzu und rief sie unter Verwendung von wx.CallAfter(self.postMessage, msg)

class AwaitStatusReportThreadClass(threading.Thread): 
    """This class should pass messages to the UI class""" 
    def __init__(self, messageQueue): 
     """Init worker Thread Class""" 
     self.messageQueue = messageQueue 

     threading.Thread.__init__(self) 
     self.start() 

    def run(self): 
     """This code executes when the thread is run""" 

     KeepRunningStatusThread = True 
     while KeepRunningStatusThread: 
      sys.stdout.flush() 

      try: 
       msg = self.messageQueue.get() 
       self.messageQueue.task_done() 
       wx.CallAfter(self.postMessage, msg) 

      except: 
       pub.sendMessage("Failed")   

    def postMessage(self, msg): 
     pub.sendMessage("UI", msg=msg) 
an
Verwandte Themen