有没有一种方法可以分离matplotlib图,以便继续计算?


258

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

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

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

有可能吗?有时计算很长,如果可以在检查中间结果时进行计算,则将有所帮助。


5
我无法确认,从16:52的nosklo选定的解决方案正在工作。对我来说,绘制不会打开显示图的窗口,只有最后的阻塞显示会显示解决方案。但是,他从17:00开始的答复是正确的。通过开启交互模式ion()可解决此问题。
H. Brandsmeier 2011年

如果您是高级程序员,则可以使用,os.fork()但是请记住,使用os.fork()可能会很棘手,因为您是通过复制旧过程来创建新过程的。
Trevor Boyd Smith,

Answers:


214

使用matplotlib不会阻塞的呼叫:

使用draw()

from matplotlib.pyplot import plot, draw, show
plot([1,2,3])
draw()
print 'continue computation'

# at the end call show to ensure window won't close.
show()

使用交互模式:

from matplotlib.pyplot import plot, ion, show
ion() # enables interactive mode
plot([1,2,3]) # result shows immediatelly (implicit draw())

print 'continue computation'

# at the end call show to ensure window won't close.
show()

28
使用matplotlib 0.98.3时,正确的导入来自matplotlib.pyplot导入图,绘图,显示
流星2009年

112
draw()对我不起作用,它不会打开任何窗口。但是show(block=False)draw()在matplotlib 1.1中,使用代替似乎可以解决问题。
伦佩尔

4
@nosklo,你看到了吗?您已将其制作为python 教程
1

4
@noskolo如果我有几个图,在继续背景进行的同时如何绘制和显示Fig1怎么办?我希望打开这个图直到生成下一个图,所以最后我打开了所有图,并完成了代码。使用您当前的解决方案,它使我等待关闭图1,然后代码继续进行。谢谢!!
PHYKER

9
draw()也不对我pause(0.001)
有用

133

使用关键字“ block”来覆盖阻止行为,例如

from matplotlib.pyplot import show, plot

plot(1)  
show(block=False)

# your code

继续您的代码。


17
但这会立即关闭绘图窗口,不会使绘图保持打开状态。
LWZ

8
是的,如果您是从命令行调用脚本的,那是对的。如果您在Ipython shell中,则不会关闭该窗口。
1

1
在一般情况下,请检查@Nico的答案是否有技巧可以使窗口保持打开状态。
1

2
对我来说,这不会立即关闭窗口,只有在脚本完成后才关闭(因此,如果希望脚本保持打开状态,可以在脚本末尾手动阻止)。
Luator

是的,脚本退出后,非阻塞窗口将关闭。您可以(a)允许在您的最后一个绘图上进行阻止,或者(b)不退出脚本(也许要求输入:“按<Enter>退出绘图”或类似方法)。
Daniel Goldfarb

29

最好始终检查您使用的库是否以非阻塞方式支持使用。

但是,如果您需要更通用的解决方案,或者没有其他方法,则可以使用multprocessingpython中包含的模块运行在单独的进程中阻塞的任何内容。计算将继续:

from multiprocessing import Process
from matplotlib.pyplot import plot, show

def plot_graph(*args):
    for data in args:
        plot(data)
    show()

p = Process(target=plot_graph, args=([1, 2, 3],))
p.start()

print 'yay'
print 'computation continues...'
print 'that rocks.'

print 'Now lets wait for the graph be closed to continue...:'
p.join()

这会带来启动新进程的开销,有时在复杂的场景下很难调试,因此我更喜欢其他解决方案(使用matplotlib非阻塞API调用


谢谢!由于我的系统上还没有Python 2.6,因此我使用threading.Thread代替了Process。我观察到随后的打印语句变得异常缓慢(第三次打印,等待1分钟后我发出了KeyboardInterrupt)。使用线程而不是多处理是否有效果?
流星

@meteore:是的,线程很烂。您始终可以从此处获取python <2.6的多处理程序:pyprocessing.berlios.de
nosklo

这绝对是极好的。您是否知道为什么在Emacs(Python模式)下直到关闭绘图窗口时才执行打印语句?
流星

在Ubuntu 8.10(Intrepid)中,(针对python <2.6的)软件包称为python-processing,您可以通过“导入处理”将其导入
meteore 2009年

1
您不是错过了if __name__ == '__main__':吗?
2013年

25

尝试

import matplotlib.pyplot as plt
plt.plot([1,2,3])
plt.show(block=False)
# other code
# [...]

# Put
plt.show()
# at the very end of your script to make sure Python doesn't bail out
# before you finished examining.

show()文档说:

在非交互模式下,显示所有图形并阻止直到图形被关闭;在交互模式下,除非在从非交互模式更改为交互模式之前创建图形(不推荐),否则它无效。在这种情况下,它会显示数字,但不会阻止。

可以将单个实验性关键字参数block设置为True或False,以覆盖上述阻止行为。


为什么不使用draw(); [.other代码]; 表演() ?据我所知,block = False已被弃用
Bogdan

您为什么认为它已被弃用?我看到它记录在这里
NicoSchlömer,2016年

11

重要提示:只是为了澄清一些内容。我假设命令在.py脚本中,并且使用例如python script.py从控制台调用脚本。

一个对我有用的简单方法是:

  1. 在show中使用block = False:plt.show(block = False)
  2. 在.py脚本的末尾使用另一个 show()。

script.py文件示例

plt.imshow(*something*)                                                               
plt.colorbar()                                                                             
plt.xlabel("true ")                                                                   
plt.ylabel("predicted ")                                                              
plt.title(" the matrix")  

# Add block = False                                           
plt.show(block = False)

################################
# OTHER CALCULATIONS AND CODE HERE ! ! !
################################

# the next command is the last line of my script
plt.show()



8

就我而言,我想在计算窗口时弹出几个窗口。供参考,方法如下:

from matplotlib.pyplot import draw, figure, show
f1, f2 = figure(), figure()
af1 = f1.add_subplot(111)
af2 = f2.add_subplot(111)
af1.plot([1,2,3])
af2.plot([6,5,4])
draw() 
print 'continuing computation'
show()

PS。关于matplotlib的OO接口的非常有用的指南


6

好吧,我很难弄清非阻塞命令.​​..但是最后,我设法重做了“ Cookbook / Matplotlib / Animations-动画选定的绘图元素 ”示例,因此它可以与线程一起使用(并在线程之间传递数据通过全局变量或通过multiprocessPipe)(在Ubuntu 10.04上的Python 2.6.5上)。

可以在以下位置找到该脚本:Animating_selected_plot_elements - thread.py-否则粘贴在下面(带有较少的注释)以供参考:

import sys
import gtk, gobject
import matplotlib
matplotlib.use('GTKAgg')
import pylab as p
import numpy as nx 
import time

import threading 



ax = p.subplot(111)
canvas = ax.figure.canvas

# for profiling
tstart = time.time()

# create the initial line
x = nx.arange(0,2*nx.pi,0.01)
line, = ax.plot(x, nx.sin(x), animated=True)

# save the clean slate background -- everything but the animated line
# is drawn and saved in the pixel buffer background
background = canvas.copy_from_bbox(ax.bbox)


# just a plain global var to pass data (from main, to plot update thread)
global mypass

# http://docs.python.org/library/multiprocessing.html#pipes-and-queues
from multiprocessing import Pipe
global pipe1main, pipe1upd
pipe1main, pipe1upd = Pipe()


# the kind of processing we might want to do in a main() function,
# will now be done in a "main thread" - so it can run in
# parallel with gobject.idle_add(update_line)
def threadMainTest():
    global mypass
    global runthread
    global pipe1main

    print "tt"

    interncount = 1

    while runthread: 
        mypass += 1
        if mypass > 100: # start "speeding up" animation, only after 100 counts have passed
            interncount *= 1.03
        pipe1main.send(interncount)
        time.sleep(0.01)
    return


# main plot / GUI update
def update_line(*args):
    global mypass
    global t0
    global runthread
    global pipe1upd

    if not runthread:
        return False 

    if pipe1upd.poll(): # check first if there is anything to receive
        myinterncount = pipe1upd.recv()

    update_line.cnt = mypass

    # restore the clean slate background
    canvas.restore_region(background)
    # update the data
    line.set_ydata(nx.sin(x+(update_line.cnt+myinterncount)/10.0))
    # just draw the animated artist
    ax.draw_artist(line)
    # just redraw the axes rectangle
    canvas.blit(ax.bbox)

    if update_line.cnt>=500:
        # print the timing info and quit
        print 'FPS:' , update_line.cnt/(time.time()-tstart)

        runthread=0
        t0.join(1)   
        print "exiting"
        sys.exit(0)

    return True



global runthread

update_line.cnt = 0
mypass = 0

runthread=1

gobject.idle_add(update_line)

global t0
t0 = threading.Thread(target=threadMainTest)
t0.start() 

# start the graphics update thread
p.show()

print "out" # will never print - show() blocks indefinitely! 

希望这对某人有帮助,
干杯!


5

在许多情况下,将图像另存为.png文件到硬盘驱动器上更为方便。原因如下:

优点:

  • 您可以在过程中随时打开,查看和关闭它。当您的应用程序长时间运行时,这特别方便。
  • 什么都不会弹出,也不用强迫您打开窗户。当您处理许多数字时,这特别方便。
  • 您的图像可供访问以供以后参考,并且在关闭图形窗口时不会丢失。

退税:

  • 我唯一能想到的是,您将必须去查找文件夹并自己打开图像。

如果您要生成大量图像,我衷心同意。
惊人的2014年

6
退回png不可交互:\


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()不会阻塞-这使您可以解决此问题!
丢失

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

如果/当我实施适当的“使绘图保持打开状态(即使发生错误)并允许显示新绘图”时,我希望脚本在没有用户干预的情况下正确退出(出于批处理目的)。

我可能会使用类似超时问题的“脚本结尾!\ n如果您想中止绘图输出(您有5秒钟),请按 p:“,来自/programming/26704840/corner我的情况下等待用户输入中断的实现


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

16
一按将如何进入?
grovina

2

OP询问有关拆除matplotlib地块的问题。大多数答案都假定命令是从python解释器中执行的。此处提供的用例是我偏爱在运行a的终端(例如bash)中测试代码,file.py并且您希望绘制图但python脚本完成并返回命令提示符。

此独立文件用于multiprocessing启动一个单独的过程,以使用绘制数据matplotlib。主线程退出使用os._exit(1)中提到的这个职位。的os._exit()主要退出,但叶势力matplotlib子进程还活着,直到响应关闭绘图窗口。完全是一个单独的过程。

这种方法有点像Matlab开发会话,其中包含带有响应命令提示符的图形窗口。使用这种方法,您已经失去了与图形窗口过程的所有联系,但是,可以进行开发和调试。只需关闭窗口并继续测试即可。

multiprocessing专为仅python代码执行而设计,这使其可能比更加适合subprocessmultiprocessing是跨平台的,因此几乎不需要调整就可以在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


我试图使用您的解决方案,但它似乎对我不起作用,并且我试图找出原因。我不是通过终端运行代码,而是从Pycharm IDE运行代码,尽管这样做没有任何区别,但是应该不会。
ttsesm

1
好的,最终对我有用的是.daemon=False按照此处所述设置子进程stackoverflow.com/a/49607287/1476932但是,sys.exit()直到我关闭子窗口,才如此处所述终止父进程。另一方面,使用os._exit(0)上述示例确实有效。
ttsesm

1

我认为,该线程中的答案提供的方法不适用于每个系统,并且无法在更复杂的情况下(例如动画)使用。我建议在以下线程中查看MiKTeX的答案,该线程中找到了一种可靠的方法: 如何等待matplotlib动画结束?


0

如果您想打开多个图形,同时将它们全部打开,则此代码对我有用:

show(block=False)
draw()

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

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”标识循环中的图,因此不会被覆盖。


-1

使用plt.show(block=False)和在脚本调用的结尾plt.show()

这将确保脚本完成后不会关闭窗口。


参见@nico-schlömer的答案
Josh Wolff
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.