Python 如何阻止matplotlib GUI线程冻结?
这是我的代码,它们都按预期工作。但是,当我在事件处理程序中取消注释这3行时,出现了一些我没有预料到的问题。首先,当Python 如何阻止matplotlib GUI线程冻结?,python,multithreading,user-interface,matplotlib,Python,Multithreading,User Interface,Matplotlib,这是我的代码,它们都按预期工作。但是,当我在事件处理程序中取消注释这3行时,出现了一些我没有预料到的问题。首先,当update\u image工作时,GUI冻结,其次,对draw()的第一次调用似乎没有机会绘制,因为在update\u image工作时,我没有看到赛车蚂蚁。在matplotlib中设置此类内容以使其顺利运行的更好方法是什么?为了避免冻结GUI,您需要在单独的线程或进程中运行update\u image。使用线程,您可以执行以下操作: from matplotlib import
update\u image
工作时,GUI冻结,其次,对draw()
的第一次调用似乎没有机会绘制,因为在update\u image
工作时,我没有看到赛车蚂蚁。在matplotlib中设置此类内容以使其顺利运行的更好方法是什么?为了避免冻结GUI,您需要在单独的线程或进程中运行update\u image
。使用线程
,您可以执行以下操作:
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()
谢谢,我已经在
Queue
上做了类似的尝试。所以很高兴知道我走对了方向。上面的版本可以工作,但它也有一些奇怪的行为。工作线程中的draw()
似乎没有任何效果,直到图形窗口中出现GUI事件,例如鼠标移动事件。它按预期使用ipython-pylab更新,但这似乎会导致不稳定-大量点击可能会导致蚂蚁持续存在,甚至图形窗口崩溃!除了p.draw()
之外,这里还需要一些机制来正确地重新绘制图形窗口吗?@wim:这是一个很好的问题。谢谢你指出这一点。如果您声明matplotlib.use('TkAgg')
,则当调用ax.figure.canvas.draw()
或p.draw()
时,会更新轴(无鼠标事件)。可能还有其他解决方案(比如使用gtk、wx、qt或其他后端?),但我不知道完整的答案。再次感谢您的回答。从GTKAgg切换到TkAgg后端解决了这个问题。出于好奇,您知道是否严格允许从工作线程内调用draw()?它在我的本地机器上运行正常,但是当我试图通过ssh-Y
会话使用它时,我得到运行时错误:主线程不在主循环中,这是由线ax.figure.canvas.draw()引起的。但是,如果我让父线程在工作线程join()
之后执行draw()
,那么就没有问题了。远程计算机和本地计算机的python/matplotlib/backend设置完全相同。@wim:你的问题很好,但它把我推向了我不太了解的领域。似乎不应该从线程调用ax.figure.canvas.draw()
或p.draw()
。假设这是真的,我添加了一些代码,展示了如何使用do\u redraw
标志和gobject.idle\u add
。。。但我强烈怀疑这是解决问题的最佳方法。@wim:我终于有机会通过ssh测试脚本(有无gobject.idle\u add
)。我看到它不起作用,尽管我不知道为什么。(在我的例子中,我没有得到一个运行时错误——GUI除了窗口底部的按钮之外没有绘制任何东西…)对不起,我根本不知道答案。
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()