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

21

312

After these instructions in the Python interpreter one gets a window with a plot:

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

Unfortunately, I don't know how to continue to interactively explore the figure created by show() while the program does further calculations.

Is it possible at all? Sometimes calculations are long and it would help if they would proceed during examination of intermediate results.

Witha answered 19/1, 2009 at 16:30 Comment(4)
I can not confirm, that the selected solution from nosklo on 16:52 is working. For me draw does not open a window to display the plot, only the blocking show at the end displays the solution. However, his reply from 17:00 is correct. Switching interactive mode on via ion() fixes the problem.Aquaplane
if you are an advanced programmer, you can use os.fork() but keep in mind that using os.fork() can be tricky because you are creating a new process by copying the old process.Lefton
@TrevorBoydSmith there is no os.fork method.Spathe
@Arief docs.python.org/3/library/os.html#os.forkLefton
A
261

Use matplotlib's calls that won't block:

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

Using interactive mode:

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()
Astereognosis answered 19/1, 2009 at 16:52 Comment(15)
With matplotlib 0.98.3 the right import is from matplotlib.pyplot import plot, draw, showWitha
Thanks! but in the noninteractive mode what is the advantage of calling draw() somewhere in the code before the final show()?Witha
@meteore: it makes the screen update, if you're in a script. (In interactive python prompt interactive mode is enabled by default)Astereognosis
I didn't realize this before due to the fact that Emacs python mode does not print until you close the plot window... :(Witha
draw() doesn't work for me, it doesn't open any window. However using show(block=False) instead of draw() seems to do the trick in matplotlib 1.1.Barimah
As of March 2013, on windows, Spyder and iPython >=0.11 don't get on and the pylab-mode 'run script, look at figure while exploring interactively' workflow no longer works: spyder-ide.blogspot.co.uk/2011/08/…Fado
@nosklo, did you see? You made it into a python tutorialGormandize
Oddly enough, matplotlib starts in non-interactive mode in ipython. Also, show() doesn't clear the plot, it overlays it. Is there a way to clear the plot before printing a new one?Goodlooking
@Goodlooking I think it is time to ask another question. You can use clf() or maybe you want to close() and open another window.Astereognosis
I want to see the plot image being updated with a certain frame rate. If I use the solution above a plot window won't even appear.Thebaid
@noskolo what if I have several figures, how to plot and show Fig1 while continue the background to go on? I'd like this figure being open till the next fig being generated, so at the end I have all figs open and the code is finished. With your current solution, it keeps me waiting to close Fig1 and then the code goes on. Thanks!!Gaberlunzie
draw() didn't work for me either, only pause(0.001) did: #28269657Univocal
ion() does the trick for me! Visual Studio Code on Macos..Frauenfeld
The commands after plot([1, 2, 3]) does continue to work after the figure window(s) pop out, but the plot does not show (it is lagging) until the whole code finished. *using the plt.ion() method.Spathe
This solution does not work for me. Nothing is plotted at all.Knit
G
166

Use the keyword 'block' to override the blocking behavior, e.g.

from matplotlib.pyplot import show, plot

plot(1)  
show(block=False)

# your code

to continue your code.

Gormandize answered 13/11, 2012 at 13:40 Comment(6)
but this will close the plot window immediately, won't keep the plot open.Is
Yeah, that's true if you call your script from command line. If you are in the Ipython shell, the window won't be closed.Gormandize
check @Nico 's answer for a trick that will leave the window open in the general case.Gormandize
For me, this does not close the window immediately, only when the script is finished (so you can manually block at the end of the script if you want it to stay open).Lazos
Yes, the non-blocked windows will close when the script exits. You can either (a) allow blocking on your last plot, or (b) don't exit the script (perhaps ask for input: "press <Enter> to exit plot" or something like that).Marabou
This solution does not work for me, as the the script does not react after plotting a diagramm and on the diagramm you can't see anythingKnit
A
34

It is better to always check with the library you are using if it supports usage in a non-blocking way.

But if you want a more generic solution, or if there is no other way, you can run anything that blocks in a separated process by using the multprocessing module included in python. Computation will continue:

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

That has the overhead of launching a new process, and is sometimes harder to debug on complex scenarios, so I'd prefer the other solution (using matplotlib's nonblocking API calls)

Astereognosis answered 19/1, 2009 at 16:40 Comment(6)
Thanks! Since I don't have Python 2.6 yet on my system, I used threading.Thread as a substitute for Process. I observed that subsequent print statements become unbearably slow (third print, I issued KeyboardInterrupt after 1 min wait). Is this an effect of using threading instead of multiprocessing?Witha
@meteore: Yes, threading sucks. You can always get multiprocessing for python <2.6 from here: pyprocessing.berlios.deAstereognosis
This is absolutely excellent. Do you have an idea why the print statements are not executed when in Emacs (python mode) until the plot window is closed?Witha
In Ubuntu 8.10 (Intrepid) the package (for python <2.6) is called python-processing and you import it with 'import processing'Witha
Didn't you missed the if __name__ == '__main__':?Whistle
This solution is too complicated - and introduces too many additional complications. You should use "draw" instead unless you really are battling performance constraints.Pronunciamento
B
27

Try

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.

The show() documentation says:

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.

Buckbuckaroo answered 18/1, 2013 at 11:53 Comment(3)
why not use draw(); [.other code]; show() ? As far as I know block=False has been deprecatedKex
Why do you think it's deprecated? I see it documented here.Hove
a more current link to the docs for block are hereCaroncarotene
S
12

IMPORTANT: Just to make something clear. I assume that the commands are inside a .py script and the script is called using e.g. python script.py from the console.

A simple way that works for me is:

  1. Use the block = False inside show : plt.show(block = False)
  2. Use another show() at the end of the .py script.

Example of script.py file:

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

Senary answered 8/3, 2017 at 14:37 Comment(0)
A
10

You may want to read this document in matplotlib's documentation, titled:

Using matplotlib in a python shell

Astereognosis answered 19/1, 2009 at 17:0 Comment(0)
W
8

In my case, I wanted to have several windows pop up as they are being computed. For reference, this is the way:

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. A quite useful guide to matplotlib's OO interface.

Witha answered 21/1, 2009 at 14:5 Comment(0)
R
6

Well, I had great trouble figuring out the non-blocking commands... But finally, I managed to rework the "Cookbook/Matplotlib/Animations - Animating selected plot elements" example, so it works with threads (and passes data between threads either via global variables, or through a multiprocess Pipe) on Python 2.6.5 on Ubuntu 10.04.

The script can be found here: Animating_selected_plot_elements-thread.py - otherwise pasted below (with fewer comments) for reference:

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! 

Hope this helps someone,
Cheers!

Reeva answered 10/11, 2010 at 21:55 Comment(0)
P
6

In many cases it is more convenient til save the image as a .png file on the hard drive. Here is why:

Advantages:

  • You can open it, have a look at it and close it down any time in the process. This is particularly convenient when your application is running for a long time.
  • Nothing pops up and you are not forced to have the windows open. This is particularly convenient when you are dealing with many figures.
  • Your image is accessible for later reference and is not lost when closing the figure window.

Drawback:

  • The only thing I can think of is that you will have to go and finder the folder and open the image yourself.
Propaedeutic answered 18/12, 2013 at 18:21 Comment(2)
If you're trying to generate a lot of images, I heartily agree.Dariadarian
The draw back png's are not interactive :\Manslayer
J
5

If you are working in console, i.e. IPython you could use plt.show(block=False) as pointed out in the other answers. But if you're lazy you could just type:

plt.show(0)

Which will be the same.

Judgemade answered 18/1, 2016 at 11:11 Comment(0)
G
5

I had to also add plt.pause(0.001) to my code to really make it working inside a for loop (otherwise it would only show the first and last 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)
Garrygarson answered 9/11, 2016 at 14:16 Comment(1)
This did work for me with matplotlib3 on macOS. Great!Ungula
L
4

On my system show() does not block, although I wanted the script to wait for the user to interact with the graph (and collect data using 'pick_event' callbacks) before continuing.

In order to block execution until the plot window is closed, I used the following:

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

Note, however, that canvas.start_event_loop_default() produced the following warning:

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)

although the script still ran.

Landsknecht answered 17/3, 2011 at 5:6 Comment(1)
Thank you! Spyder imports -pylab at startup which is generally useful, but means show() won't block when ioff() - this allows you to fix this behaviour!Hollywood
A
4

The OP asks about detatching matplotlib plots. Most answers assume command execution from within a python interpreter. The use-case presented here is my preference for testing code in a terminal (e.g. bash) where a file.py is run and you want the plot(s) to come up but the python script to complete and return to a command prompt.

This stand-alone file uses multiprocessing to launch a separate process for plotting data with matplotlib. The main thread exits using the os._exit(1) mentioned in this post. The os._exit() forces main to exit but leaves the matplotlib child process alive and responsive until the plot window is closed. It's a separate process entirely.

This approach is a bit like a Matlab development session with figure windows that come up with a responsive command prompt. With this approach, you have lost all contact with the figure window process, but, that's ok for development and debugging. Just close the window and keep testing.

multiprocessing is designed for python-only code execution which makes it perhaps better suited than subprocess. multiprocessing is cross-platform so this should work well in Windows or Mac with little or no adjustment. There is no need to check the underlying operating system. This was tested on 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

Running file.py brings up a figure window, then __main__ exits but the multiprocessing + matplotlib figure window remains responsive with zoom, pan, and other buttons because it is an independent process.

Check the processes at the bash command prompt with:

ps ax|grep -v grep |grep file.py

Alienee answered 11/7, 2019 at 5:43 Comment(2)
I was trying to use your solution but it does not seem to work for me and I am trying to figure out why. I am not running the code through terminal but from Pycharm IDE if that makes any difference, though it shouldn't.Dabney
ok, what finally worked for me was setting the child process with .daemon=False as described here https://mcmap.net/q/101300/-why-child-process-daemon-true-not-exiting-when-main-process-exit-in-python However, sys.exit() did not terminated the parent process as described there until I closed the child window. On the other hand using os._exit(0) from the above example did work.Dabney
W
3

I also wanted my plots to display run the rest of the code (and then keep on displaying) even if there is an error (I sometimes use plots for debugging). I coded up this little hack so that any plots inside this with statement behave as such.

This is probably a bit too non-standard and not advisable for production code. There is probably a lot of hidden "gotchas" in this code.

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

If/when I implement a proper "keep the plots open (even if an error occurs) and allow new plots to be shown", I would want the script to properly exit if no user interference tells it otherwise (for batch execution purposes).

I may use something like a time-out-question "End of script! \nPress p if you want the plotting output to be paused (you have 5 seconds): " from https://stackoverflow.com/questions/26704840/corner-cases-for-my-wait-for-user-input-interruption-implementation.

Wenda answered 4/11, 2014 at 12:14 Comment(0)
K
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
Kisumu answered 21/2, 2017 at 6:27 Comment(1)
How would one press enter before existing?Andie
S
1

In my opinion, the answers in this thread provide methods which don't work for every systems and in more complex situations like animations. I suggest to have a look at the answer of MiKTeX in the following thread, where a robust method has been found: How to wait until matplotlib animation ends?

Southernly answered 27/12, 2015 at 6:17 Comment(0)
P
1

Here is the simplest solution I found (thread blocking code)

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
Parnassus answered 23/11, 2021 at 17:31 Comment(0)
S
0

If you want to open multiple figures, while keeping them all opened, this code worked for me:

show(block=False)
draw()
Slumlord answered 16/9, 2016 at 15:11 Comment(1)
show(block=False) has been deprecated and now is not working anymoreKex
G
0

While not directly answering OPs request, Im posting this workaround since it may help somebody in this situation:

  • Im creating an .exe with pyinstaller since I cannot install python where I need to generate the plots, so I need the python script to generate the plot, save it as .png, close it and continue with the next, implemented as several plots in a loop or using a function.

for this Im using:

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

Where "var" identifies the plot in the loop so it wont be overwritten.

Graeae answered 10/1, 2020 at 22:1 Comment(0)
T
0

What I have found as the best solution so the program does not wait for you to close the figure and have all your plots together so you can examine them side by side is to show all the plots at the end.
But this way you cannot examine the plots while program is running.

# 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()
Tying answered 27/11, 2021 at 18:27 Comment(0)
V
-2

Use plt.show(block=False), and at the end of your script call plt.show().

This will ensure that the window won't be closed when the script is finished.

Vocabulary answered 18/8, 2019 at 2:25 Comment(1)
See @nico-schlömer 's answerFumarole

© 2022 - 2024 — McMap. All rights reserved.