问题 在matplotlib中,有没有办法异步弹出一个数字?


在matplotlib中,有一种简单的方法可以在不中断脚本控制流的情况下绘制图形吗?

为了清晰起见使用伪代码,这是我想要实现的:

fig1 = figure()
fig1.plot_a_figure(datasets)

for dataset in datasets:
   results = analyze(dataset)    # this takes several minutes
   update(fig1)
   pop_up_another_figure(results) # would like to have a look at this one
                                  # while the next dataset is being processed

当然,我可以保存这些中间数字,但我只需要快速浏览它们,最好让它们实时弹出屏幕。

编辑:一个可运行的例子:

#!/usr/bin/python
import pylab as plb
import matplotlib.pyplot as plt

fig1=plt.figure(1)
ax = fig1.add_subplot(1,1,1)

ax.plot([1,2,3],[4,5,6],'ro-')

#fig1.show()  # this does not show a figure if uncommented
plt.show()    # until the plot window is closed, the next line is not executed

print "doing something else now"

我错过了非常基本的东西吗?


5087
2018-02-19 13:19


起源

我想 show() 那已经做到了。您可以根据需要创建任意数量的图形,show()每个图形,并在代码保持运行时保持显示。你能发布一个演示问题的可运行示例吗? - Paul
@Paul:补充一个例子。我错过了一些微不足道的东西吗? - ev-br
不,你绝对不会错过任何微不足道的事。我刚刚看到很多情节偶然同时出现,我想有一些线程正在进行,而show()不会获得锁定。阅读这篇文章: stackoverflow.com/questions/4659680/... - Paul
我可以想到三种方法:首先,如果你不关心这些图是交互式的,你可以像你建议的那样保存一个图像,但是很容易就能用这个图像启动一个观察者。 os.startfile()。如果您希望它具有交互性,并且您不介意将数据写入磁盘,则可以 subprocess.Popen() 另一个python脚本。第三个是尝试真正理解matplotlib如何管理线程.. - Paul


答案:


首先,不要忘记一个简单的替代方案就是制作新的数字窗口 plt.figure(2)plt.figure(3) 如果你真的想要更新现有的图形窗口,你最好保持你的线对象的句柄

h = ax.plot([1,2,3],[4,5,6],'ro-')

然后你会做以下事情:

h[0].set_data(some_new_results)
ax.figure.canvas.draw()

至于问题的真正含义,如果你还在努力解读这个问题......


如果需要,您需要启用交互模式 plt.show() 是非阻塞的。要修改您的可运行示例,以便 “现在做点什么” 会立即打印,而不是等待数字窗口关闭,以下情况会:

#!/usr/bin/python
import pylab as plb
import matplotlib.pyplot as plt

fig1=plt.figure(1)
ax = fig1.add_subplot(1,1,1)

ax.plot([1,2,3],[4,5,6],'ro-')

#fig1.show()  # this does not show a figure if uncommented
plt.ion()     # turns on interactive mode
plt.show()    # now this should be non-blocking

print "doing something else now"
raw_input('Press Enter to continue...')

然而,这只是表面上的事情 - 一旦你开始想要在与地块交互时做背景工作,就会出现许多复杂情况。这是使用基本上是状态机的绘画的自然结果,它在面向对象的环境中与线程和编程不能很好地协作。

  • 昂贵的计算必须进入工作线程(或者进入子进程)以避免冻结GUI。
  • Queue 应该用于传递输入数据并以线程安全的方式从worker函数中获取结果。
  • 根据我的经验,打电话是不安全的 draw() 在工作线程中,您还需要设置一种计划重绘的方法。
  • 不同的后端可能会开始做奇怪的事情 TkAgg 似乎是唯一一个100%工作的人(见 这里)。

最简单和最好的解决方案是不使用vanilla python解释器,而是使用 ipython -pylab (正如ianalis正确建议的那样),因为他们已经找到了让互动内容顺利运作所需的大部分技巧。它可以不用 ipython/pylab 但这是一项大量的额外工作。

注意: 我仍然经常喜欢在使用ipython和pyplot GUI窗口时修剪工作线程,并且为了使线程顺利工作,我还需要使用另一个命令行参数 ipython -pylab -wthread。我上线了 python 2.7.1+ 同 matplotlib v1.1.0, 你的旅费可能会改变。希望这可以帮助!

Ubuntu用户请注意:存储库现在仍然在v0.99上已经很长一段时间了,这是值得的 升级 你的 matplotlib 因为有 许多改进 来到v1.0版本,包括一个 修正马拉松,以及行为的重大变化 show()


13
2017-09-12 15:07



用plt.ion()尝试了你的例子。对我不起作用,plt.show()仍在阻止它。我在Ubuntu 10.04,python 2.6.5,matplotlib 0.99。这太老了吗? - ev-br
也许它太老了:他们改变了实施 show() 非常显着的回到v1.0(见这里 matplotlib.sourceforge.net/users/... )。如果你搬家,你会得到什么? plt.ion() 排在剧本的顶部,即在剧本之后 import matplotlib.pyplot as plt?调用函数的结果是什么? matplotlib.get_backend()? - wim
get_backend()返回TkAgg,如果plt.ion()是脚本的第一行,则没有变化。 - ev-br
是 ax.figure.canvas.draw() 阻塞/同步通话?我无法在任何地方找到这些信息。如果你知道或有一个解释这个的参考,将不胜感激。 - The Quantum Physicist


可能最简单的解决方案是使用IPython作为你的python shell。使用-pylab选项运行它。

ipython -pylab

2
2018-02-19 14:31