Matplotlib如何重用另一个脚本创建的图形?

10
我在MacOS上使用Sublime Text和Matplotlib,Python版本为3.5,Matplotlib版本为2.0。通常情况下,我有一个脚本用于绘制数据,并使用plt.savefig()将图形保存为.pdf文件。然后我使用Skim(一种pdf阅读器),每当修改并运行脚本时,刷新文件。这样可以使我的工作布局变得简洁:一个窗口用于脚本,另一个窗口用于自动刷新的图形。
我想要保持相同的布局,但是使用Matplotlib图形(因为它们具有交互性)。我正在寻找一种方法来使用plt.show(),但始终在第一次运行脚本时创建的同一张图中
例如:

1. 第一次运行

import matplotlib.pyplot as plt
import numpy as np 

fig, ax = plt.figure()    

noise = np.random.rand(1, 100)
ax(noise)
plt.show()

2. 后续运行

import matplotlib.pyplot as plt
import numpy as np 

# This is the super command I am looking for
fig = plt.get_previous_run_figure()
ax = fig.axes

noise = np.random.rand(1, 100)
ax.plot(noise)
plt.draw()

如果是那样的话,我需要单独运行一个首次运行脚本,有人知道是否可能吗?


我理解得对吗,fig = plt.figure(0); fig.clf() 对你无效,因为你想要重复使用来自另一个Python实例的窗口? - Daan
可能是重复的问题:存储和重新加载matplotlib.pyplot对象 - tmdavison
@tom 会使用pickle保持窗口在运行之间保持打开状态吗?我认为这就是OP所问的... - MB-F
1
@kazemakase 啊,可能不行。我可能误解了问题。如果你关闭了Python会话,我怀疑你无法保持一个窗口打开,但至少通过pickling,你可以重新打开以前会话中的图形。 - tmdavison
@Daan:你理解得很好,我想从另一个Python实例中重用窗口。 - Leonard
显示剩余3条评论
3个回答

4
您想让多个连续的Python会话共享一个公共的Matplotlib窗口。我没有找到将此窗口从单独的进程中共享的方法,特别是当原始所有者可能在任何时间点终止时。
但是,您可以执行与当前工作流类似的操作,其中您有一个外部PDF查看器来查看您从多个Python实例更新的输出文件。
请参见如何pickle matplotlib图的此问题/答案: 存储和重新加载matplotlib.pyplot对象 在每个脚本中,将您的matplotlib图形输出为pickled对象,而不是调用plt.show():
import matplotlib.pyplot as plt
import numpy as np
import pickle

ax = plt.subplot(111)
x = np.linspace(0, 10)
y = np.exp(x)
plt.plot(x, y)
pickle.dump(ax, file('myplot.pickle', 'w'))

然后,启动一个专用的Python会话,加载这个pickle文件并调用plt.show()。让这个脚本在循环中运行,检查磁盘上pickle文件的更新,并在必要时重新加载:

import matplotlib.pyplot as plt
import pickle

while True:
   ax = pickle.load(file('myplot.pickle'))
   plt.show()

替代方案

我通常不会使用独立的python会话,而是在一个单独的Ipython会话中运行不同的脚本。通过选择相同的图形窗口,我最终得到了与您描述的大致相似的设置,其中同一图形窗口在整个工作日中被重复使用。

import matplotlib.pyplot as plt

fig = plt.figure(0)
fig.clf()

plt.show()

谢谢您的回答。我尝试了您的代码,但是plt.show()仍然会打开一个新的图形来显示循环中刚加载的pickle轴(即使您假设已激活plt.ion()以防止plt.show()阻塞循环)。我还尝试在循环之前先创建一个图形,并使用plt.show(block=False)显示它,但结果仍然相同。 - Leonard
如果新窗口能够在与旧窗口相同的位置打开,对您是否可行? - Daan
我从MacOSX后端转到了TkAgg,这使得你的解决方案非常有效! - Leonard
另一个快速解决方案是使用WebAgg,并设置默认端口(例如:8888)。每次脚本停止并重新启动时,端口都会更新为新数字,并且Web浏览器会自动刷新。 - Leonard

0

原则上,建立两个不同脚本之间的连接可以使用系统范围内的剪贴板来完成。(据我所知,Windows和macOS中的剪贴板都是系统范围内的。)

因此,想法是使用tk或pyqt设置一个应用程序,并实现一个通用的FigureCanvas。该应用程序可以监听剪贴板中的更改事件。

然后,另一个主要的工作流脚本将调用一些函数,将当前图形封装到pickle对象中并发送到剪贴板,从那里被GUI应用程序捕获,进行反序列化并显示在画布上。

这听起来有点麻烦,但应该能够满足问题中非常严格的要求。


0

Daan提供的替代方案对我很有用。这里是更多的代码。我在一个Tkinter交互式GUI中使用它来重用/更新一个matplotlib图形窗口:

fig1 = None
if fig1:
    #if exists, clear figure 1
    plt.figure(1).clf() 
    plt.suptitle("New Fig Title", fontsize=18, fontweight='bold')
    #reuse window of figure 1 for new figure
    fig1 = plt.scatter(points.T[0], points.T[1], color='red', **plot_kwds)      
else:
    #initialize
    fig1 = plt.figure(num=1,figsize=(7, int(7*imgRatio)), dpi=80)
    plt.tick_params(axis='both', which='major', labelsize=14)
    plt.tick_params(axis='both', which='minor', labelsize=14)           
    plt.suptitle("Title", fontsize=18, fontweight='bold')
    fig1 = plt.scatter(points.T[0], points.T[1], color='red', **plot_kwds)

该图形正在重复使用(交互式)plt窗口。为了使其正常工作,我必须在matplotlibrc文件中设置interactive:True(请参见我的评论此处)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接