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?