使用 matplotlib 和 wxpython 进行多线程

Multithreading with matplotlib and wxpython

简要说明我要实现的目标:

我正在开发使用 Python、wxPython 和 matplotlib 构建的分析软件。我正在尝试实现一个函数,程序可以在执行一些分析计算后绘制结果。目前,程序在执行计算时会冻结(计算时间最多需要 10 秒,具体取决于数据量)所以我正在尝试使用线程来创建一个非阻塞程序来改善用户体验。

我遇到了问题

我不断收到此错误: (PyAssertionError:C++ 断言 "hdcDst && hdcSrc" 在 AlphaBlt() 中失败 \src\msw\dc.cpp(2559):AlphaBlt():无效 HDC)

谷歌搜索并没有真正帮助确定原因。

我将 post 在 post 底部的完整回溯。

这是我的代码:

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()

基本上,用户将在 GUI 上单击一个图标,这将触发一个函数来执行此处由 'plotgraph()' 模拟的一些分析计算。目前,如果不使用线程,plotgraph() 将阻塞我的整个程序,因此我尝试使用线程来执行计算以释放我的 GUI。

然而,当我试图在线程中绘制我的 GUI 时,即在 plotgraph() 中有 plt.show(),绘图出现然后再次消失。当我再次单击 GUI 上的按钮以生成线程时,出现了同样的错误。

所以我尝试通过在线程结束后发送一条消息来解决这个问题,这样 plt.show() 就会在线程外发生,但我仍然遇到同样的错误。

我似乎无法在网上找到类似的错误,除了 2008 年 posted 的一个线程。如果有人能提供帮助那就太棒了!

一言以蔽之 我需要一种方法来实现某种回调函数,允许我在线程中执行分析计算,然后在计算完成后绘制图形以释放我的 GUI。如果有人可以向我解释这里出了什么问题,或者可以建议一种替代方法,那就太好了。非常感谢!!

这是完整的回溯:

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

我想你需要的是wx.PyEventBinder。 它是这样工作的:

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

当然,您需要在 __init__

中添加一行
self.Bind(EVT_CONNECTED, self.onCalculated)