有没有一种方法可以分离Matplotlib图表,以便计算可以继续进行?

311

在Python解释器中执行这些指令后,就会得到一个带有绘图的窗口:

from matplotlib.pyplot import *
plot([1,2,3])
show()
# other code

不幸的是,我不知道如何在程序继续计算时继续交互式地探索show()创建的图形。

这种情况有可能吗?有时候计算需要很长时间,如果在检查中间结果的同时能够继续进行计算将会很有帮助。


5
我无法确认,nosklo在16:52选择的解决方案是否有效。对我而言,绘图函数没有打开窗口来显示图形,只有最后的阻塞(show)函数显示了解决方案。然而,他在17:00的回答是正确的。通过使用ion()打开交互模式可以解决问题。 - H. Brandsmeier
如果您是高级程序员,可以使用 os.fork(),但请记住,使用 os.fork() 可能会很棘手,因为您正在通过复制旧进程来创建新进程。 - Trevor Boyd Smith
@TrevorBoydSmith,没有os.fork方法。 - Redsbefall
@Arief https://docs.python.org/3/library/os.html#os.fork - Trevor Boyd Smith
21个回答

5
我还需要在代码中添加 plt.pause(0.001),以便在for循环中正常工作(否则它只会显示第一个和最后一个图):
import matplotlib.pyplot as plt

plt.scatter([0], [1])
plt.draw()
plt.show(block=False)

for i in range(10):
    plt.scatter([i], [i+1])
    plt.draw()
    plt.pause(0.001)

这对我在macOS上使用matplotlib3确实有效。太棒了! - Jerry Ma

4

在我的系统上,show()函数不会阻塞程序执行,虽然我希望脚本在用户与图形进行交互并使用'pick_event'回调收集数据之前等待。

为了阻塞执行直到绘图窗口关闭,我使用了以下方法:

fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.plot(x,y)

# set processing to continue when window closed
def onclose(event):
    fig.canvas.stop_event_loop()
fig.canvas.mpl_connect('close_event', onclose)

fig.show() # this call does not block on my system
fig.canvas.start_event_loop_default() # block here until window closed

# continue with further processing, perhaps using result from callbacks

请注意,canvas.start_event_loop_default() 产生了以下警告:


C:\Python26\lib\site-packages\matplotlib\backend_bases.py:2051: DeprecationWarning: Using default event loop until function specific to this GUI is implemented
  warnings.warn(str,DeprecationWarning)

虽然脚本仍在运行。

谢谢!Spyder在启动时导入-pylab通常很有用,但这意味着当ioff()时show()不会阻塞-这允许您修复此行为! - lost

4
本文讨论如何分离matplotlib图。大多数答案都假设在Python解释器中执行命令。这里介绍的用例是我更喜欢在终端(例如bash)中测试代码,其中运行file.py并希望图形出现,但Python脚本完成并返回到命令提示符。
该独立文件使用multiprocessing启动一个单独的进程来绘制matplotlib数据。主线程使用this帖子中提到的os._exit(1)退出。 os._exit()强制主线程退出,但保留matplotlib子进程活动并响应,直到关闭绘图窗口。它完全是一个单独的进程。
这种方法有点像Matlab开发会话,具有具有响应式命令提示符的图形窗口。使用此方法,您已经失去了与图形窗口进程的所有联系,但对于开发和调试来说这没关系。只需关闭窗口并继续测试即可。
"multiprocessing" 是专为 Python 代码执行而设计的,因此它可能比 "subprocess" 更适合。 "multiprocessing" 是跨平台的,因此在 Windows 或 Mac 上使用时,很少或根本不需要进行调整。无需检查底层操作系统。这在 Linux、Ubuntu 18.04LTS 上进行了测试。
#!/usr/bin/python3

import time
import multiprocessing
import os

def plot_graph(data):
    from matplotlib.pyplot import plot, draw, show
    print("entered plot_graph()")
    plot(data)
    show() # this will block and remain a viable process as long as the figure window is open
    print("exiting plot_graph() process")

if __name__ == "__main__":
    print("starting __main__")
    multiprocessing.Process(target=plot_graph, args=([1, 2, 3],)).start()
    time.sleep(5)
    print("exiting main")
    os._exit(0) # this exits immediately with no cleanup or buffer flushing

运行 file.py 会打开一个图形窗口,然后 __main__ 退出,但是 multiprocessing + matplotlib 的图形窗口仍然保持响应,具有缩放、平移和其他按钮,因为它是一个独立的进程。
在 bash 命令提示符下可以使用以下命令检查进程: ps ax|grep -v grep |grep file.py

1
我正在尝试使用您的解决方案,但似乎对我无效,我正在努力找出原因。我并不是通过终端而是从Pycharm IDE运行代码,但这不应该有任何区别。 - ttsesm
1
好的,最终对我有用的是按照这里https://dev59.com/qqrka4cB1Zd3GeqPbEe6#49607287所述将子进程设置为`.daemon = False。但是,正如那里描述的那样,sys.exit()并没有像预期的那样终止父进程,直到我关闭了子窗口。另一方面,使用上面示例中的 os._exit(0)` 确实起作用了。 - ttsesm

3
我希望我的图表能够在代码的其余部分运行时显示(即使出现错误,我有时使用图表进行调试)。我编写了这个小技巧,以便该with语句内的任何图表都能够实现此功能。
这可能有点过于非标准化,并且不建议用于生产代码。这段代码中可能隐藏着许多“陷阱”。
from contextlib import contextmanager

@contextmanager
def keep_plots_open(keep_show_open_on_exit=True, even_when_error=True):
    '''
    To continue excecuting code when plt.show() is called
    and keep the plot on displaying before this contex manager exits
    (even if an error caused the exit).
    '''
    import matplotlib.pyplot
    show_original = matplotlib.pyplot.show
    def show_replacement(*args, **kwargs):
        kwargs['block'] = False
        show_original(*args, **kwargs)
    matplotlib.pyplot.show = show_replacement

    pylab_exists = True
    try:
        import pylab
    except ImportError: 
        pylab_exists = False
    if pylab_exists:
        pylab.show = show_replacement

    try:
        yield
    except Exception, err:
        if keep_show_open_on_exit and even_when_error:
            print "*********************************************"
            print "Error early edition while waiting for show():" 
            print "*********************************************"
            import traceback
            print traceback.format_exc()
            show_original()
            print "*********************************************"
            raise
    finally:
        matplotlib.pyplot.show = show_original
        if pylab_exists:
            pylab.show = show_original
    if keep_show_open_on_exit:
        show_original()

# ***********************
# Running example
# ***********************
import pylab as pl
import time
if __name__ == '__main__':
    with keep_plots_open():
        pl.figure('a')
        pl.plot([1,2,3], [4,5,6])     
        pl.plot([3,2,1], [4,5,6])
        pl.show()

        pl.figure('b')
        pl.plot([1,2,3], [4,5,6])
        pl.show()

        time.sleep(1)
        print '...'
        time.sleep(1)
        print '...'
        time.sleep(1)
        print '...'
        this_will_surely_cause_an_error

如果我实现了一个合适的“保持图表打开(即使出现错误)并允许显示新图表”的功能,我希望脚本能够在没有用户干预的情况下正确退出(以进行批处理执行)。
我可能会使用类似于这样的超时询问“Yes! \n如果您希望绘图输出暂停,请按p键(您有5秒钟时间):”来自https://stackoverflow.com/questions/26704840/corner-cases-for-my-wait-for-user-input-interruption-implementation。请注意,HTML标签将被保留。

2
plt.figure(1)
plt.imshow(your_first_image)

plt.figure(2)
plt.imshow(your_second_image)

plt.show(block=False) # That's important 

raw_input("Press ENTER to exist") # Useful when you run your Python script from the terminal and you want to hold the running to see your figures until you press Enter

17
在退出前如何按下回车键? - grovina

1

这是我找到的最简单的解决方案(线程阻塞代码)

plt.show(block=False) # this avoids blocking your thread

plt.pause(1) # comment this if you do not want a time delay

# do more stuff

plt.show(block=True) # this prevents the window from closing on you

1
在我看来,这个帖子中提供的答案并不适用于所有系统和更复杂的情况,比如动画。我建议看一下MiKTeX在下面的帖子中的回答,那里找到了一种强大的方法: 如何等待matplotlib动画结束?

0
如果您想打开多个图形,并将它们全部保持打开状态,下面的代码对我行之有效:
show(block=False)
draw()

show(block=False)已被弃用,现在不再起作用。 - Bogdan

0
我发现的最佳解决方案是让程序不等待您关闭图形并将所有绘图放在一起,以便您可以并排检查它们,但这样做会导致您无法在程序运行时检查绘图。
# stuff

numFig = 1

plt.figure(numFig)
numFig += 1
plt.plot(x1, y1)

# other stuff

plt.figure(numFig)
numFig += 1
plt.plot(x2, y2)

# more stuff

plt.show()

0

虽然没有直接回答OP的问题,但我发布这个解决方法,因为它可能会帮助到处于这种情况下的某些人:

  • 我正在使用pyinstaller创建一个.exe文件,因为我无法在需要生成图形的地方安装Python,所以我需要使用Python脚本生成图形,将其保存为.png格式,关闭它并继续下一个,实现为循环中的多个图形或使用函数。

为此,我使用:

import matplotlib.pyplot as plt
#code generating the plot in a loop or function
#saving the plot
plt.savefig(var+'_plot.png',bbox_inches='tight', dpi=250) 
#you can allways reopen the plot using
os.system(var+'_plot.png') # unfortunately .png allows no interaction.
#the following avoids plot blocking the execution while in non-interactive mode
plt.show(block=False) 
#and the following closes the plot while next iteration will generate new instance.
plt.close() 

在循环中,"var"用于标识绘图,以防止其被覆盖。


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