2011-08-01 47 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