2011-08-01 130 views
2
from matplotlib import pyplot as p 
from scipy import zeros 
from Queue import Queue 
import random 

w,h = 320,200 

black = zeros((h,w,3), dtype='uint8') 
red = black.copy(); red[:,:,0] = 255 
green = black.copy(); green[:,:,1] = 255 
blue = black.copy(); blue[:,:,2] = 255 

def ants(): 
    from scipy import rand, dstack 
    return dstack([(255*rand(h,w)).astype('uint8')]*3) 

fig = p.figure() 
axs = [fig.add_subplot(1,3,i) for i in xrange(3)] 
[ax.imshow(black) for ax in axs] 

q = Queue() 

def update_image(ax): 
    ## this takes some time 
    import time 
    time.sleep(3) 
    ax.images[0].set_data(random.choice([red, green, blue])) 

def hit(event): 
    if event.inaxes in axs: 
    update_axs = [event.inaxes] 
    else: 
    update_axs = axs 
    for ax in update_axs: 
    ax.images[0].set_data(ants()) 
    p.draw() 
# for ax in update_axs: 
# update_image(ax) 
# p.draw() 

cid = fig.canvas.mpl_connect('button_press_event', hit) 
p.show() 

这里是我的代码,其中所有的按预期工作。然而,当我取消注释事件处理程序中的这3行时,有一些问题我没有想到。首先GUI冻结,而update_image正在工作,其次第一个电话draw()似乎没有机会画,因为我没有看到赛车蚂蚁,而update_image正在工作。在matplotlib中设置这种类型的东西的更好的方法是什么?如何阻止matplotlib GUI线程冻结?

回答

2

为避免冻结GUI,您需要在单独的线程或进程中运行update_image。使用threading,你可能会做这样的事情:

import matplotlib 
matplotlib.use('TkAgg') 
from matplotlib import pyplot as p 
from scipy import zeros 
import random 
import threading 


w,h = 320,200 

black = zeros((h,w,3), dtype='uint8') 
red = black.copy(); red[:,:,0] = 255 
green = black.copy(); green[:,:,1] = 255 
blue = black.copy(); blue[:,:,2] = 255 

def ants(): 
    from scipy import rand, dstack 
    return dstack([(255*rand(h,w)).astype('uint8')]*3) 

fig = p.figure() 
axs = [fig.add_subplot(1,3,i) for i in xrange(3)] 
[ax.imshow(black) for ax in axs] 

def update_image(ax): 
    ## this takes some time 
    import time 
    time.sleep(3) 
    ax.images[0].set_data(random.choice([red, green, blue])) 
    ax.figure.canvas.draw() 

def hit(event): 
    if event.inaxes in axs: 
     update_axs = [event.inaxes] 
    else: 
     update_axs = axs 
    for ax in update_axs: 
     ax.images[0].set_data(ants()) 
    p.draw() 
    for ax in update_axs: 
     t=threading.Thread(target=update_image,args=(ax,)) 
     t.daemon=True 
     t.start() 

cid = fig.canvas.mpl_connect('button_press_event', hit) 
p.show() 
+1

三江源,我已经尝试类似的东西用'Queue'..so它很高兴知道我是在正确的轨道上。你的版本上面的作品,但它也有一些奇怪的行为。直到在图形窗口中有一个GUI事件,工作者线程中的'draw()'似乎没有任何效果,例如,一个鼠标移动事件。它使用'ipython -pylab'按预期进行更新,但这似乎会导致不稳定 - 大量的点击可能会导致蚂蚁持续存在,甚至数字窗口崩溃!是否有一些机制除了'p.draw()'这里需要使图形窗口重新适当地重绘? – wim

+1

@wim:这是一个很好的问题。谢谢你指出。看起来,如果你声明'matplotlib.use('TkAgg')',那么当调用'ax.figure.canvas.draw()'或'p.draw()'时,轴被更新(没有鼠标事件)。可能还有其他解决方案(如使用gtk,wx,qt或其他后端?),但我不知道完整的答案。 – unutbu

+0

再次感谢您的回答。从GTKAgg切换到TkAgg后端解决了问题。出于好奇,你知道是否严格允许从worker线程中调用draw()吗?它在我的本地机器上工作正常,但是当我尝试通过'ssh -Y'会话使用它时,我得到'RuntimeError:主线程不在主循环中'由行ax.figure.canvas.draw )'。但是,如果我让父线程在工作线程加入()后执行'draw()',那么就没有问题了。远程机器和本地机器具有完全相同的python/matplotlib /后端设置。 – wim