(python) matplotlib pyplot show() .. 阻塞还是不阻塞?
我在使用 show()
的时候遇到了很多麻烦,我知道我可能做错了什么,但不太确定正确的做法是什么。
我想要的是一种方法,可以让主线程在 GUI 线程发生事件之前一直等待,类似于第一次运行时这样的效果:
from matplotlib import pyplot as p
from scipy import rand
im = (255*rand(480,640)).astype('uint8')
fig = p.figure()
ax = fig.add_subplot(111)
ax.imshow(im)
# just any mutable container for storing a click
s = [-1, -1]
def onclick(event):
if event.xdata is not None and event.ydata is not None:
s[0] = event.xdata
s[1] = event.ydata
p.close()
cid = fig.canvas.mpl_connect('button_press_event', onclick)
p.show()
print s
在这里,p.show()
会一直阻塞,直到在事件处理程序中调用 p.close()
。但是当我第二次运行同样的代码时,它却直接跳过了 p.show()
,并打印出原来的 s, [-1, -1]
。
我看到关于 p.show()
是否可以或应该在同一个程序中调用多次的信息有些矛盾。看起来它是设计成只使用一次,并且只能在脚本的最后使用。其他的使用方式似乎会破坏 pyplot
(像是状态机的问题?)。
我尝试过组合使用 p.draw()
和 p.ion()
以及 p.ioff()
,但没有得到我想要的效果(要么没有正确阻塞,要么图形没有在正确的时间出现)。
我也对事件处理程序如何能够看到 s
感到困惑,这样的方式是否不太好用来传递信息。如果我不使用像数组或列表这样的可变容器,事件处理程序想要设置的信息就会作为局部变量丢失。难道我错过了什么其他方法,让 GUI 线程能够将信号传回主线程吗?有没有办法在主线程中阻塞,而不需要定期轮询或忙等待,等待事件处理程序的信号再继续?
所以我想我主要的问题是:
有没有一个好的替代方案来替代 p.show()
,可以实现我想要的效果(和第一次调用 p.show()
时的行为一样),还是说这种代码需要完全重新思考或重写?
3 个回答
我注意到在运行代码时有一些不同之处:
直接在Python解释器中(命令行)运行
把代码放在一个Python脚本里,然后从命令行运行(用“python script.py”命令)
这两种方式都会让程序在运行时暂停,这样是可以的。
在解释器中,两个图像都会显示出来;而在命令行中,只有第一个图像会显示。
这里有几个想法,质量不一:
如果你不想让s成为一个全局变量,可以把onclick()做成一个可调用的对象,然后把它绑定到这个对象上。
你的回调函数可以获取和释放一个锁来控制程序的执行流程(这个方法有点不太干净)。
你也可以主动去检查s的状态来控制程序的流程(这个方法非常不干净)。
你可以通过fig.canvas.draw()手动控制图形的绘制。
今天我解决了我的问题。如果其他人也想知道怎么改变show()
的行为,可以继续往下看:
我在matplotlib网页的新功能部分发现了一个标题为支持多次调用show的段落:
一个长期以来的请求是支持多次调用show()。这一直很困难,因为在不同的操作系统、用户界面工具包和版本之间保持一致的行为很难。Eric Firing在统一不同后端的show功能方面做了大量工作,期望的效果是让show能够显示所有新创建的图形,并在它们关闭之前阻止程序继续执行。重复调用show时,应该显示自上次调用以来新创建的图形。Eric对他能接触到的用户界面工具包、版本和平台进行了大量测试,但不可能测试所有情况,所以请将问题反馈到邮件列表和错误追踪系统。
这段话出现在1.0.1
版本的“新功能”中,而我写这段话时,synaptic中的版本仍然是0.99.3
。我成功下载并从源代码构建了v1.0.1
。为了满足依赖关系,我还需要额外安装libfreetype6-dev tk-dev tk8.5-dev tcl8.5-dev python-gtk2-dev
这些包。
现在,使用matplotlib.__version__ == 1.0.1
,以下代码块的表现正如我所期待的那样:
import matplotlib.pyplot as p
from scipy import eye
p.imshow(eye(3))
p.show()
print 'a'
p.imshow(eye(6))
p.show()
print 'b'
p.imshow(eye(9))
p.show()
print 'c'