2011-08-01 50 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行のコメントを外すと、私が期待していないいくつかの問題があります。最初にupdate_imageが動作している間にGUIがフリーズし、第2に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 'ですでに似た何かをしようとしていました。上記のあなたのバージョンは動作しますが、それはまたいくつかの奇妙な動作をします。 FigureウィンドウにGUIイベントがあるまで、ワーカースレッドの 'draw()'は何の効果もないようです。マウス移動イベント。それは 'ipython -pylab'で期待どおりに更新されますが、これは不安定になります - たくさんのクリックがアリを継続させ、Figureウィンドウもクラッシュする可能性があります! Figureウィンドウを適切に再描画させるためにここに必要な 'p.draw()'以外のメカニズムがありますか? – wim

+1

@wim:それはすばらしい質問です。それを指摘していただきありがとうございます。 'matplotlib.use( 'TkAgg')'を宣言すると、 'ax.figure.canvas.draw()'や 'p.draw()'が呼び出されたときに軸が更新されます(マウスイベントなし)。おそらく他のソリューション(gtk、wx、qt、または他のバックエンドを使用しているようなもの)がありますが、完全な答えはわかりません。 – unutbu

+0

答えにもう一度感謝します。 GTKAggからTkAggバックエンドに切り替えることで問題は解決しました。奇妙なことから、ワーカースレッド内からdraw()を厳密に呼び出すことが許可されているかどうかは分かりますか?私のローカルマシンでは問題なく動作しますが、 'ssh -Y'セッションで使用しようとすると、' RuntimeError:メインスレッドがメインループにはない 'という行の' ax.figure.canvas.draw( ) '。しかし、親スレッドが 'draw()'をワーカースレッド 'join()'の後に実行させる場合、問題はありません。リモートマシンとローカルマシンはまったく同じpython/matplotlib/backend設定を持っています。 – wim

関連する問題