简体   繁体   中英

Multithreading with matplotlib and wxpython

Brief description on what I'm trying to achieve:

I'm working on a analytics software built using Python, wxPython, and matplotlib. I'm trying to implement a function where the program can plot the results after performing some analytical calculations. At the moment, the program freezes when the it's performing the calculations (and the calculation time takes up to 10 seconds depending on the amount of data) so I'm trying to use threading to create a non-blocking program to improve user experience.

Problem I'm getting

I keep getting this error : (PyAssertionError: C++ assertion "hdcDst && hdcSrc" failed at ...... \\src\\msw\\dc.cpp(2559) in AlphaBlt():AlphaBlt():invalid HDC)

and googling hasn't really help with identifying the cause.

I'll post the full traceback at the bottom of the post.

Here's my code:

import wx
import time 
import matplotlib.pyplot as plt

from wx.lib.pubsub import Publisher as pub
from threading import Thread

def plotgraph(x,y,sleeptime):
    plt.plot(x,y)
    #Simulate long process using time.sleep
    time.sleep(sleep time)
    #Send out a message once process is completed
    pub.sendMessage('PLOT','empty')

class listener():
    def __init__(self,name):
        self.name = name
        #Listens to message
        pub.subscribe(self.Plot,'PLOT')
        pass

    def Plot(self,message):
        print self.name
        plt.show()
        print 'printed'


waiting = listener('Bob')

t1 = Thread(target=plotgraph,args=([1,2,3],[1,2,3],5))
t1.start()
t2 = Thread(target=plotgraph,args=([1,2,3],[1,2,3],3))
t2.start()

Basically, the user will be clicking an icon on the GUI and that will trigger a function to perform some analytical calculation simulated by 'plotgraph()' here. At the moment, without using threads, plotgraph() will block my entire program, so I'm trying to use threads to perform the calculations to free up my GUI.

However when I tried to plot my GUI within the thread, ie have plt.show() in plotgraph(), the plot appears then disappears again. When I click the button on the GUI to spawn the thread a second time, I get the same error.

So I've tried to work around it by sending a message after the thread's ended so that the plt.show() will happen outside the thread but I'm still getting the same error.

I can't seem to be able to find a similar error online, except for one thread posted in 2008. If anyone could help that would be awesome!

In a nutshell I need a way to implement sort of a callback function that allows me to perform the analytic calculation in a thread, then plot the graph once the calculations are completed to free up my GUI. It'd be great if someone could explain to me what's wrong here, or could suggest an alternative method to do it. Thanks very much!!

Here's the full traceback:

File "C:\Users\chaishen\AppData\Local\Enthought\Canopy32\App\appdata\canopy-1.
.5.3123.win-x86\lib\threading.py", line 810, in __bootstrap_inner
   self.run()
File "C:\Users\chaishen\AppData\Local\Enthought\Canopy32\App\appdata\canopy-1.
.5.3123.win-x86\lib\threading.py", line 763, in run
   self.__target(*self.__args, **self.__kwargs)
File "<ipython-input-5-0cb01f87e97a>", line 13, in plotgraph
   pub.sendMessage('PLOT','empty')
File "C:\Users\chaishen\AppData\Local\Enthought\Canopy32\User\lib\site-package
\wx\lib\pubsub.py", line 811, in sendMessage
   self.__topicTree.sendMessage(aTopic, message, onTopicNeverCreated)
File "C:\Users\chaishen\AppData\Local\Enthought\Canopy32\User\lib\site-package
\wx\lib\pubsub.py", line 498, in sendMessage
   deliveryCount += node.sendMessage(message)
File "C:\Users\chaishen\AppData\Local\Enthought\Canopy32\User\lib\site-package
\wx\lib\pubsub.py", line 336, in sendMessage
   listener(message)
File "<ipython-input-5-0cb01f87e97a>", line 24, in Plot
   plt.show()
File "C:\Users\chaishen\AppData\Local\Enthought\Canopy32\User\lib\site-package
\matplotlib\pyplot.py", line 155, in show
   return _show(*args, **kw)
File "C:\Users\chaishen\AppData\Local\Enthought\Canopy32\User\lib\site-package
\matplotlib\backend_bases.py", line 154, in __call__
   manager.show()
File "C:\Users\chaishen\AppData\Local\Enthought\Canopy32\User\lib\site-package
\matplotlib\backends\backend_wx.py", line 1414, in show
   self.canvas.draw()
File "C:\Users\chaishen\AppData\Local\Enthought\Canopy32\User\lib\site-package
\matplotlib\backends\backend_wxagg.py", line 50, in draw
   self.gui_repaint(drawDC=drawDC)
File "C:\Users\chaishen\AppData\Local\Enthought\Canopy32\User\lib\site-package
\matplotlib\backends\backend_wx.py", line 911, in gui_repaint
   drawDC.DrawBitmap(self.bitmap, 0, 0)
File "C:\Users\chaishen\AppData\Local\Enthought\Canopy32\User\lib\site-package
\wx\_gdi.py", line 3460, in DrawBitmap
   return _gdi_.DC_DrawBitmap(*args, **kwargs)
yAssertionError: C++ assertion "hdcDst && hdcSrc" failed at ..\..\src\msw\dc.cp
(2559) in AlphaBlt(): AlphaBlt(): invalid HDC

I think what you need is the wx.PyEventBinder. It works like this:

anEVT_CALCULATED = wx.NewEventType()
EVT_CALCULATED = wx.PyEventBinder(anEVT_CALCULATED, 1)

def onCalculate(self, event): # this is your click
    calc_thread = CalculatorThread(self, params)
    calc_thread.start()
    return

def onConnected(self, event):  
    ''' this is where your thread comes back '''
    self.doSomeThingLikePlotting(event.resultdata)

class CalcEvent(wx.PyCommandEvent):
    ''' Event to signal that the thread has calculated'''
    def __init__(self, etype, eid, resultdata):
        wx.PyCommandEvent.__init__(self, etype, eid)
        self.resultdata = resultdata

class CalculatorThread(threading.Thread):
    ''' This is the thread doing your calculation and handing it back'''
    def __init__(self, listener, params):
        threading.Thread.__init__(self)
        self.listener = listener
        self.params = params

    def run(self):
        resultdata = calculate(params) # this is your calculation
        event = CalcEvent(anEVT_CALCULATED, -1, resultdata=resultdata)
        wx.PostEvent(self.listener, event)
        return

And of course you need to add one line to your __init__

self.Bind(EVT_CONNECTED, self.onCalculated)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM