有没有一种方法可以分离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个回答

258

使用不会阻塞的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导入plot、draw和show函数。 - meteore
123
draw() 对我没用,它没有打开任何窗口。然而,在 matplotlib 1.1 中使用 show(block=False) 替代 draw() 似乎能解决问题。 - rumpel
4
@nosklo,你看到了吗?你被列入了一篇Python教程中(http://www.scribd.com/doc/47168316/45/Nonblocking-Plots-with-Matplotlib)。 - Jan
5
如果我有多个图,如何绘制并显示Fig1并使背景继续进行?我希望这个图一直打开,直到生成下一个图,在代码结束时所有图都打开且代码已完成。使用您目前的解决方案,它让我等待关闭Fig1,然后代码才继续执行。谢谢!! - physiker
12
draw() 对我也没有用,只有 pause(0.001) 有用:https://dev59.com/n14c5IYBdhLWcg3wJnU- - NumesSanguis
显示剩余10条评论

165
使用关键字 'block' 来覆盖阻塞行为,例如:
from matplotlib.pyplot import show, plot

plot(1)  
show(block=False)

# your code

继续编写您的代码。


24
但是这将立即关闭绘图窗口,不会保持绘图开放。 - LWZ
8
如果您从命令行调用脚本,那么这是正确的。如果您在Ipython shell中,则窗口不会关闭。 - Jan
2
请查看@Nico的答案,其中包含一个在一般情况下保持窗口打开的技巧。 - Jan
2
对我来说,这不会立即关闭窗口,只有当脚本完成后才会关闭(因此在脚本末尾手动阻塞,如果您希望它保持打开状态)。 - luator
2
是的,非阻塞窗口将在脚本退出时关闭。您可以选择(a)在最后一个图中允许阻塞,或者(b)不要退出脚本(也许询问输入:“按<Enter>键退出绘图”或类似的内容)。 - Daniel Goldfarb
这个解决方案对我不起作用,因为脚本在绘制图表后没有反应,在图表上也看不到任何东西。 - PeterBe

34

如果你想在非阻塞的情况下使用某个库,最好始终与你正在使用的库进行确认。

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

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分钟后中断了程序)。这是使用线程而非多进程的影响吗? - meteore
@meteore:是的,线程很烦人。你可以从这里获取 python <2.6 的多进程库:http://pyprocessing.berlios.de/ - nosklo
这绝对是非常棒的。你有没有想法为什么在Emacs(Python模式)中,直到绘图窗口关闭后,打印语句才会被执行? - meteore
在Ubuntu 8.10(Intrepid)中,针对Python <2.6的软件包称为python-processing,并且您可以使用'import processing'进行导入。 - meteore
1
你没有忘记加上 if __name__ == '__main__': 了吗? - Wernight
这个解决方案太复杂了,而且引入了太多的额外问题。除非你真的在处理性能限制,否则应该使用“draw”。 - Peter

27

尝试

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(); [.其他代码]; show()?据我所知,block=False已被弃用。 - Bogdan
1
你为什么认为它已经被弃用了?我在这里看到了文档 [http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.show]。 - Nico Schlömer
block 的更多当前文档链接在这里 - ofloveandhate

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


10
你可能想阅读 matplotlib 文档中的这篇文章,标题为:“在 Python Shell 中使用 matplotlib”。

请点击以下链接查看:Using matplotlib in a python shell

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面向对象接口指南


6
在许多情况下,将图片保存为硬盘上的.png文件更加方便。以下是原因:
优点: - 您可以在任何时候打开、查看并关闭它。当您的应用程序运行了很长时间时,这特别方便。 - 没有弹出窗口会出现,您也不必一直保持窗口打开。当您处理许多图形时,这特别方便。 - 您的图片可以供以后参考,并且在关闭图形窗口时不会丢失。
缺点: - 我唯一想到的就是您需要去找到文件夹并自己打开图片。

如果你正在尝试生成大量的图像,我完全赞同。 - fantabolous
6
PNG文件的缺点是不具有互动性。 - Inverse

6

我曾经在非阻塞命令方面遇到了很大的困难......但最终,我成功地重构了Cookbook/Matplotlib/Animations - Animating selected plot elements示例,使其能够在Python 2.6.5和Ubuntu 10.04上使用线程(并通过全局变量或通过多进程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! 

希望这能帮助到某些人,祝好!

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

将是相同的。


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