关于python:有没有办法分离matplotlib图,以便计算可以继续?

Is there a way to detach matplotlib plots so that the computation can continue?

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

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

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

有可能吗?有时计算时间很长,如果在检查中间结果的过程中继续进行计算,会有所帮助。


使用不会阻塞的matplotlib的调用:

使用draw()

1
2
3
4
5
6
7
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()

使用交互模式:

1
2
3
4
5
6
7
8
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()


使用关键字"block"重写阻塞行为,例如

1
2
3
4
5
6
from matplotlib.pyplot import show, plot

plot(1)  
show(block=False)

# your code

继续您的代码。


如果它以非阻塞方式支持使用,那么最好始终检查正在使用的库。

但是,如果您想要一个更通用的解决方案,或者没有其他方法,那么您可以使用Python中包含的multprocessing模块,在单独的进程中运行任何块。计算将继续:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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调用)


尝试

1
2
3
4
5
6
7
8
9
10
11
from matplotlib.pyplot import *
plot([1,2,3])
show(block=False)
# other code
# [...]

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

show()文件称:

In non-interactive mode, display all figures and block until the figures have been closed; in interactive mode it has no effect unless figures were created prior to a change from non-interactive to interactive mode (not recommended). In that case it displays the figures but does not block.

A single experimental keyword argument, block, may be set to True or False to override the blocking behavior described above.


您可能需要阅读matplotlib的文档,标题为:

在python shell中使用matplotlib


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

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

  • 在show:plt.show中使用block=false(block=false)
  • 在.py脚本的末尾使用另一个show()。
  • script.py文件示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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()


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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()

    对Matplotlib的OO接口非常有用的指南。


    嗯,我很难找出非阻塞命令…但最后,我设法重新编写了"cookbook/matplotlib/animations-animating selected plot elements"示例,因此它可以处理线程(并通过全局变量或通过Ubuntu 10.04上的python 2.6.5上的多进程Pipe在线程之间传递数据)。

    脚本可以在这里找到:设置"选定"的动画"绘图元素-thread.py"-或者粘贴在下面(注释较少)以供参考:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    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!

    希望这能帮助别人,干杯!


    如果您在控制台中工作,即IPython,您可以使用plt.show(block=False),如其他答案所示。但是如果你懒惰的话,你可以打:

    1
    plt.show(0)

    这是一样的。


    在许多情况下,将图像保存为硬盘上的.png文件会更方便。原因如下:

    优势:

    • 你可以打开它,看看它,然后在这个过程中的任何时候关闭它。这在应用程序长时间运行时特别方便时间。
    • 不会弹出任何东西,也不会强制打开窗口。这在处理许多数字时特别方便。
    • 您的图像可供以后参考,关闭图形窗口时不会丢失。
    • 小精灵

      缺点:

      • 我能想到的唯一一件事就是你必须自己去查找文件夹并打开图像。
      • 小精灵


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

        这可能有点不标准,不适合生产代码。这段代码中可能隐藏了很多"gotchas"。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        60
        61
        62
        63
        64
        65
        66
        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

        如果/当我执行正确的"保持绘图打开(即使发生错误)并允许显示新的绘图"时,如果没有用户干扰,我希望脚本正确退出(出于批处理执行目的)。

        我可能会使用类似超时问题"脚本结束!如果您希望暂停打印输出(您有5秒钟):"来自https://stackoverflow.com/questions/26704840/corner-cases-for-my-wait-for-user-input-interrupt-implementation。


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

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        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)

        在我的系统上,show()不会阻塞,尽管我希望脚本在继续之前等待用户与图形交互(并使用"pick_event"回调收集数据)。

        为了在绘图窗口关闭之前阻止执行,我使用了以下方法:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        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()生成了以下警告:

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

        尽管剧本还在运行。


        1
        2
        3
        4
        5
        6
        7
        8
        9
        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


        在我看来,这个线程中的答案提供了一些方法,这些方法不适用于每个系统,也不适用于更复杂的情况,如动画。我建议在下面的线程中查看miktex的答案,其中发现了一个健壮的方法:如何等待Matplotlib动画结束?


        如果要打开多个数字,同时保持所有数字都打开,则此代码适用于我:

        1
        2
        show(block=False)
        draw()