Research on wxpython multithreading includes (import Publisher and other error studies) and wxpythonpublisher
As an automated tester, developing basic application desktop programs is required! Recently, I have been studying wxpython related knowledge. I have seen multiple threads. I found that the official document says, "You cannot modify window attributes in a thread! ", But the actual situation is: a simple fq app was recently created. I have opened two threads. One thread is used to display the setting progress (a third-party host is used, so the download host is required to overwrite the local host). One thread processes the download task, dynamic settings found in the first threadSelf. Gauge (value) can take effect, and wx. CallAfter is not used !! Note that wxpython can only process one event at a time, so that wx. CallAfter is not enabled in the thread at the same time, so that wxpython will execute CallAfter to pass functions one by one. That is: Enable N threads, The N-1 is used to do things (do not use CallAfter in the thread type), the N threads call CallAfter to notify the main thread to update the interface. As mentioned above, I tried to directly change the main window control in the thread (I don't know if there is a problem ). Yes! However, it is recommended that wx. CallAfter be used according to the official statement to avoid abnormal crash.
Common scenarios of wxpython multi-threaded applications: for processing complex tasks (such as downloading several files), if you put all the code in the main thread, the application will become stuck after the event is triggered, it cannot trigger other times, and is almost in a suspended state (although he is still alive ...), such a program will crash if it is used by others ...., therefore, multithreading is used. After the download event is triggered, the task is completed in the thread. What is the main thread doing? But a prompt should be given to the window program, such as when the Download button is triggered, the main program is "under download... ", after the download is complete, the thread notifies the window program to update the state to" download completed... ", such interactions are at least friendly.
How to Use wxpython multithreading: wxpython developers suggest using wx. CallAfter + PubSub. The CallAfter owner pushes the time to the main program. PubSub enables the wxPython application to communicate with other threads.
In fact, it is easier to directly pass a main thread method in wx. CallAfter! But since the official website says so, we can use it like this !! Let's take an official example to see how wxpython is used ~
import time import wx from threading import Thread from wx.lib.pubsub import Publisher ######################################################################## class TestThread(Thread): """Test Worker Thread Class.""" #---------------------------------------------------------------------- def __init__(self): """Init Worker Thread Class.""" Thread.__init__(self) self.start() # start the thread #---------------------------------------------------------------------- def run(self): """Run Worker Thread.""" # This is the code executing in the new thread. for i in range(6): time.sleep(10) wx.CallAfter(self.postTime, i) time.sleep(5) wx.CallAfter(Publisher().sendMessage, "update", "Thread finished!") #---------------------------------------------------------------------- def postTime(self, amt): """ Send time to GUI """ amtOfTime = (amt + 1) * 10 Publisher().sendMessage("update", amtOfTime) ######################################################################## class MyForm(wx.Frame): #---------------------------------------------------------------------- def __init__(self): wx.Frame.__init__(self, None, wx.ID_ANY, "Tutorial") # Add a panel so it looks the correct on all platforms panel = wx.Panel(self, wx.ID_ANY) self.displayLbl = wx.StaticText(panel, label="Amount of time since thread started goes here") self.btn = btn = wx.Button(panel, label="Start Thread") btn.Bind(wx.EVT_BUTTON, self.onButton) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.displayLbl, 0, wx.ALL|wx.CENTER, 5) sizer.Add(btn, 0, wx.ALL|wx.CENTER, 5) panel.SetSizer(sizer) # create a pubsub receiver Publisher().subscribe(self.updateDisplay, "update") #---------------------------------------------------------------------- def onButton(self, event): """ Runs the thread """ TestThread() self.displayLbl.SetLabel("Thread started!") btn = event.GetEventObject() btn.Disable() #---------------------------------------------------------------------- def updateDisplay(self, msg): """ Receives data from thread and updates the display """ t = msg.data if isinstance(t, int): self.displayLbl.SetLabel("Time since thread started: %s seconds" % t) else: self.displayLbl.SetLabel("%s" % t) self.btn.Enable() #---------------------------------------------------------------------- # Run the program if __name__ == "__main__": app = wx.PySimpleApp() frame = MyForm().Show() app.MainLoop()
The above code we casually search wxpython multi-threaded applications will take the official example, may be the version of the problem (I use a wx-2.8), the first sentence to give an error, it is common to search the Publisher moudle on the Internet, but there is no corresponding saying. Now let's take a look at this classic wxpython program. This code is simple, and the following problems may occur due to version issues.
1. ImportError: cannot import name Publisher
This error is normal. We enter wx. lib. the pubsub module does not find the Publisher class, but we have. lib. the pub module under the pubsub module finds the Publisher shadow. It was already private in version 2.8. For details, see row 126, _ publisher = _ Publisher (), at the same time, the subscribe and sendMessage of Publisher are copied to the subscribe and sendMessage variables as shown in (128,131. So we will introduce the header: from wx. lib. pubsub import pub, and change all Publisher () to pub.
2. TypeError: sendMessage () takes exactly 2 arguments (3 given)
After 1 modification, I thought it was done, and the running still reported an error and crashed. If the problem is hard to find the cause, most people may find it hard to find the cause. We debug the problem in and out and find that it instantiates "C: \ Python27 \ Lib \ site-packages \ wx-2.8-msw-unicode \ wx \ lib \ pubsub \ core \ kwargs \ publisher. py "Publisher class under this module. Let's take a closer look at the sendMessage method. The second parameter should be passed to all dictionary types. We will change the lines 24, 32, and 71 of this program to wx. callAfter (pub. sendMessage, "update", msg = "Thread finished! "), Pub. sendMessage (" update ", msg = amtOfTime), t = msg. Run the command ~~ Note that the key value of sendMessage (msg here) must be the same as the receiving parameter of the subscribe listening method (msg here). The source code is actually the dictionary parameter msgKwargs to pass the value.
In fact,This code is correct. We noticed that "C: \ Python27 \ Lib \ site-packages \ wx-2.8-msw-unicode \ wx \ lib \ pubsub \ core \ __init __. in fact, the core module dynamically loads the Publisher class. We enter policies. py is the module's 10th rows, and msgDataProtocol = 'kwargs 'is found. It turns out so... we know that the core module has two packages: arg1 and kwargs. We observe that the sendMessage method of the publisher class is different in the Publisher module. We used the Publisher class under arg1 in this code. Okay, we changed row 10th of policies. py to msgDataProtocol = 'arg1'. The restored code is running correctly !! The difference between kwargs and arg1 is that kwargs can pass multiple parameters to the main program (** kwargs), while arg1 can only pass one parameter.
After a simple discussion above, we know that wxpython pushes events to the main program through wx. CallAfter and transmits data through PubSub and the main program. This is a simple understanding of wxpython multithreading. I hope to help you.