我有一個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嘗試。我在深層跳躍。我已經看到解決方案,它是一個名稱空間問題,但我不能在這裏看到名稱空間衝突。
編輯:語法修復。