2017-10-20 48 views
0

我有一个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尝试。我在深层跳跃。我已经看到解决方案,它是一个名称空间问题,但我不能在这里看到名称空间冲突。

编辑:语法修复。

回答

0

WX .CallAfter接受一个函数及其参数

wx.CallAfter(pub.sendMessage,"changeStatus", msg=testResults[1]) 
0

决不WX工作,但这里的callAfter签名的样子: [wxPython]: wx.CallAfter(callableObj, *args, **kw)

你要通过函数/方法(可调用)和它的参数(位置/关键字)分别,而实际上不叫(就像在[Python]: class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}))。

我认为你要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]})