Python 如何阻止matplotlib GUI线程冻结?

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

这是我的代码,它们都按预期工作。但是,当我在事件处理程序中取消注释这3行时,出现了一些我没有预料到的问题。首先,当
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()