是否有一种方法可以分离matplotlib图,以便计算可以继续进行?

在Python解释器中的这些指令之后,你会看到一个带有图形的窗口:

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

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

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

329358 次浏览

最好总是检查你正在使用的库是否支持以非阻塞方式使用。

但如果你想要一个更通用的解决方案,或者如果没有其他方法,你可以通过使用python中包含的multprocessing模块运行任何在分离进程中阻塞的东西。计算将继续:

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调用)

使用不会阻塞的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()

你可能想在matplotlib的文档中阅读这个文档,标题为:

在python shell中使用matplotlib

在我的例子中,我希望在计算窗口时弹出几个窗口。作为参考,方法如下:

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接口指南

嗯,我在搞清楚非阻塞命令方面遇到了很大的困难……但最后,我设法重做了“Cookbook/Matplotlib/Animations -动画选定的情节元素”示例,因此它在Ubuntu 10.04的Python 2.6.5上与线程(并通过全局变量或多进程Pipe在线程之间传递数据)一起工作。

该脚本可以在这里找到: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!

希望这能帮助到某人 干杯!< / p >

在我的系统上,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)

尽管脚本仍然在运行。

使用关键字'block'来覆盖阻塞行为,例如:

from matplotlib.pyplot import show, plot


plot(1)
show(block=False)


# your code

继续您的代码。

试一试

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以覆盖上面描述的阻塞行为。

在许多情况下,它是硬盘驱动器上的。png文件更方便,直到保存图像。原因如下:

优点:

    在这个过程中,你可以随时打开、查看和关闭它。当应用程序运行较长时间时,这尤其方便 李。< / >
  • 什么都不会弹出,你也不会被迫打开窗户。这在处理许多数字时特别方便。
  • 您的图像可用于以后的参考,并且在关闭图形窗口时不会丢失。

缺点:

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

我还希望我的图显示运行其余的代码(然后继续显示),即使出现错误(我有时使用图进行调试)。我编写了这个小hack,以便with语句中的任何plot都像这样运行。

这可能有点太非标准了,不适合用于生产代码。这段代码中可能有很多隐藏的“陷阱”。

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

如果/当我实现了一个适当的“保持图打开(即使发生错误)并允许显示新的图”,我希望脚本在没有用户干扰的情况下正确退出(用于批处理执行)。

我可能会使用超时问题“脚本结束!”\nPress p如果你想要绘图输出被暂停(你有5秒):" from https://stackoverflow.com/questions/26704840/corner-cases-for-my-wait-for-user-input-interruption-implementation

在我看来,这个线程中的答案提供的方法并不适用于每个系统和更复杂的情况,如动画。我建议在下面的帖子中看看MiKTeX的答案,在那里找到了一个健壮的方法: 如何等待matplotlib动画结束? < / p >

如果你在控制台工作,即IPython,你可以使用plt.show(block=False),正如在其他答案中指出的那样。但如果你很懒,你可以输入:

plt.show(0)

这是一样的。

如果你想打开多个图形,同时保持它们全部打开,这段代码适合我:

show(block=False)
draw()

我还必须将plt.pause(0.001)添加到我的代码中,以真正使它在for循环中工作(否则它只会显示第一个和最后一个plot):

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)
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

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

对我来说,一个简单的方法是:

  1. 使用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()

OP询问分离matplotlib块。大多数回答假设从python解释器中执行命令。这里给出的用例是我对在终端(例如bash)中测试代码的偏好,其中运行file.py,并且您希望出现绘图,但python脚本完成并返回到命令提示符。

这个独立文件使用multiprocessing启动一个单独的进程,用于用matplotlib绘制数据。主线程使用帖子中提到的os._exit(1)退出。os._exit()强制main退出,但让matplotlib子进程存活并响应,直到plot窗口关闭。这是一个完全独立的过程。

这种方法有点像带有图形窗口的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

使用plt.show(block=False),并在脚本的末尾调用plt.show()

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

虽然没有直接回答OPs的请求,但我发布了这个变通方法,因为它可能会帮助一些人在这种情况下:

  • 我用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”标识循环中的情节,因此它不会被覆盖。

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

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
我所发现的最佳解决方案是,程序不会等待您关闭图形,并将所有的图放在一起,以便您可以并排检查它们,这是在最后显示所有的图。
但是这样你就不能在程序运行时检查图了
# 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()