J'ai un programme python qui, sur simple pression d'un bouton, veut faire une certaine tâche dans un thread séparé pour empêcher la tâche de rendre l'interface utilisateur inachevée à utiliser time.sleep() dans la tâche en cours. J'ai un problème avec le thread où lorsque le wx.CallAfter() est utilisé avec pub.sendMessage(), j'obtiens une exception. J'ai besoin du sous-pub pour envoyer des informations entre les threads. Ci-dessous est un exemple du problème que je vois, le code ne fait pas ce que je veux vraiment mais il montre l'erreur de la même manière. Ce code crée un bouton qui lorsqu'il est pressé crée un thread qui crée un tuple et envoie ensuite la partie de la chaîne du tuple à la barre d'état du cadre:Threading avec lancer pubsub AssertionError: 'callableObj n'est pas appelable' dans 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()
Quand je lance ce code et appuyez sur le bouton de l'état mises à jour bar, mais je vois aussi un retraçage comme indiqué ci-dessous:
TestGUI Showing Status Bar Update
Traceback:
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
Je suis à une complète perte de la raison pour laquelle la barre d'état se met à jour comme prévu, mais une exception est-elle déclenchée? Y at-il quelque chose d'évident que j'ai manqué ici, je n'ai pas fait de threading auparavant et c'est ma première tentative d'interface graphique avec Python et wxPython. Je saute dans la partie profonde. J'ai vu des solutions où c'était un problème d'espace de noms mais je ne peux pas voir un conflit d'espace de noms ici.
EDIT: Corrections de grammaire.