我有一个python程序,只需按一下按钮,我就可以在一个单独的线程中执行某项任务,以阻止任务因为在手头的任务中使用time.sleep()而使GUI无响应。我有一个线程的问题,当wx.CallAfter()与pub.sendMessage()一起使用时,我得到一个异常。我需要pub子在线程之间发送信息。
下面是我看到的问题的一个例子,代码并没有做我真正想要的事情,但它以同样的方式显示错误。此代码创建一个按钮,按下该按钮创建一个创建元组的线程,然后将元组的字符串部分发送到框架上的状态栏:
#!/usr/bin/env python2.7
import wx
from wx.lib.pubsub import pub
from threading import Thread
#====================================
# Main Application Frame Class
#====================================
class MainFrame(wx.Frame):
"""The main frame class for the application."""
# MainFrame Constructor Method
def __init__(self, *args, **kwargs):
"""Initialise the main application frame and bind events to event handlers."""
wx.Frame.__init__(self, style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER, *args, **kwargs)
self.appStatusBar = self.CreateStatusBar()
self.panel = MainPanel(self)
# Set up the file menu
filemenu = wx.Menu()
menuAbout = filemenu.Append(wx.ID_ABOUT, "&About", " Testing Publisher with Threading")
menuExit = filemenu.Append(wx.ID_EXIT, "E&xit", " Terminate Program")
# Set up a menu bar for placing the file menu
menuBar = wx.MenuBar()
menuBar.Append(filemenu, "&File")
self.SetMenuBar(menuBar)
# Set the events that will trigger from the users interaction
self.Bind(wx.EVT_MENU, self.onAbout, menuAbout)
self.Bind(wx.EVT_MENU, self.onExit, menuExit)
# Use some sizers to see layout options
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.panel, proportion=1, flag=wx.EXPAND)
# Layout the sizers
self.SetSizer(self.sizer)
self.SetAutoLayout(1)
self.sizer.Fit(self)
# Set up listeners for the status bar and error dialog so that they can be implemented from other classes
pub.subscribe(self.changeStatusBar, "changeStatus")
pub.subscribe(self.errorMsgDisp, "errorDisplay")
self.Centre()
self.Show(True)
# End of MainFrame Constructor Method
# onAbout Method Functionality
def onAbout(self, e):
"""Open Program About Dialog.
:param e: The event that triggered the method
"""
dlg = wx.MessageDialog(self, "Testing Publisher with Threading", "About Program", wx.OK)
dlg.ShowModal()
dlg.Destroy()
# End of onAbout() Method
# onExit Method Functionality
def onExit(self, e):
"""Close the GUI down.
:param e: The event that triggered the method
"""
self.Close()
# End of onExit() Method
# Update the Status Bar Message Method
def changeStatusBar(self, msg):
"""Change the message displayed on the status bar.
:param msg: Message to be displayed in the status bar
"""
self.appStatusBar.SetStatusText(msg)
self.appStatusBar.Refresh()
# End of changeStatusBar() Method
# Display Error Messages Method
def errorMsgDisp(self, msg):
"""Display the error message sent to the function.
:param msg: The string with the error message to be displayed
"""
dlg = wx.MessageDialog(None, msg, "Error", wx.OK | wx.ICON_ERROR)
dlg.ShowModal()
dlg.Destroy()
# End of errorMsgDisp() Method
# End of MainFrame class
#====================================
# Main Panel Class
#====================================
class MainPanel(wx.Panel):
"""The main panel class for the application.
The main panel for holding all the widgets for the tool.
"""
# MainPanel Constructor Method
def __init__(self, parent, *args, **kwargs):
"""Set up the main panel that all the widgets are tied to.
Defines all the widgets and events that are to occur when the widgets
are used.
:param parent: The parent frame/panel that the MainPanel belongs to
"""
wx.Panel.__init__(self, parent, *args, **kwargs)
self.mainVBox = wx.BoxSizer(wx.VERTICAL)
self.testingButton = wx.Button(self, label="Testing Button")
self.Bind(wx.EVT_BUTTON, self.onTestButton, self.testingButton)
# Add the COMs Panel to the main panel Vertical box sizer
self.mainVBox.Add(self.testingButton, proportion=1, flag=wx.EXPAND)
self.SetSizer(self.mainVBox)
# Event for doing something with the button
def onTestButton(self, e):
testBtn = e.GetEventObject()
testBtn.Disable()
testingThread = WorkerThread()
testingThread.start()
# End of MainPanel class
#====================================
# Activity Thread Class
#====================================
class WorkerThread(Thread):
"""Worker thread class for doing all time consuming tasks."""
# WorkerThread Constructor Method
def __init__(self):
"""Initialises the worker thread ready to run tasks."""
Thread.__init__(self)
# End of WorkerThread Constructor Method
# Worker Run Method
def run(self):
"""When the thread is started the tasks in this method are executed."""
self.testButton()
# End of run() Method
# Test the button
def testButton(self):
"""Create tuple and publish the string to the status bar"""
testResults = (0, "Status Bar Updated")
wx.CallAfter(pub.sendMessage("changeStatus", msg=testResults[1]))
# End of testButton() Method
# End of WorkerThread Class
#====================================
# Main Code that Runs the GUI
#====================================
if __name__ == '__main__':
app = wx.App(False)
frame = MainFrame(None, title="Threading Test")
app.MainLoop()
当我运行此代码并按下按钮时,状态栏会更新,但我也会看到一个Traceback,如下所示:
TestGUI Showing Status Bar Update
回溯:
Exception in thread Thread-1:
Traceback (most recent call last):
File "C:\Python27\lib\threading.py", line 801, in __bootstrap_inner
self.run()
File "C:\Users\Mhaines\Documents\threading_pubsub_test.py", line 150, in run
self.testButton()
File "C:\Users\Mhaines\Documents\threading_pubsub_test.py", line 157, in testButton
wx.CallAfter(pub.sendMessage("changeStatus", msg=testResults[1]))
File "C:\Python27\lib\site-packages\wx-3.0-msw\wx\_core.py", line 16759, in CallAfter
assert callable(callableObj), "callableObj is not callable"
AssertionError: callableObj is not callable
我完全不知道为什么状态栏会按预期更新,但我收到异常?有没有明显我错过的东西,我以前没有做过线程,这是我第一次使用Python和wxPython进行GUI尝试。我在深处跳进去。我已经看到了解决方案,这是一个命名空间问题,但我在这里看不到命名空间冲突。
编辑:语法修复。
答案 0 :(得分:0)
从未使用 wx ,但这里是callAfter
签名的样子:
[wxPython]: wx.CallAfter(callableObj, *args, **kw)
您必须传递函数/方法(可调用)及其参数(位置/关键字)单独,实际上不调用(就像在{{3}中一样}})。
我认为您希望callAfter
像您在代码段中指定的一样调用sendMessage
:
pub.sendMessage("changeStatus", msg=testResults[1])
然后,你的行:
wx.CallAfter(pub.sendMessage("changeStatus", msg=testResults[1]))
应该是:
wx.CallAfter(pub.sendMessage, args=("changeStatus",), kw={"msg": testResults[1]})
答案 1 :(得分:0)
wx.CallAfter接受一个函数及其参数
wx.CallAfter(pub.sendMessage,"changeStatus", msg=testResults[1])