我有一个python程序,在按下按钮时我想在一个单独的线程中执行某个任务来停止任务使GUI无法响应在任务中使用time.sleep()。我有一个线程问题,当wx.CallAfter()与pub.sendMessage()一起使用时,我得到一个异常。我需要pub sub在线程之间发送信息。线程与pubsub投掷AssertionError:'callableObj不可调用'在wxPython
下面是我看到的问题的一个例子,代码没有做我真正想要的,但它以相同的方式显示错误。此代码创建一个按钮,在按下时创建创建一个元组的螺纹,然后将元组的串部分发送到框架上的状态栏:
#!/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()
当运行该代码,并按下该按钮的状态酒吧更新,但是我也看到了回溯,如下图所示:
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尝试。我在深层跳跃。我已经看到解决方案,它是一个名称空间问题,但我不能在这里看到名称空间冲突。
编辑:语法修复。