without - python plot




Is there a way to detach matplotlib plots so that the computation can continue? (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

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

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.


Here is an update (python 3.6.5 on Windows 10).

I tried all sorts of combinations - the simplest I've found is just to use pause(0.01) after each plot - no need for a show() for the intermediate plots - then a single show() at the end ensures you can look at the final plot before termination.

As an example, here is a bit of code I use to check speed for various array sizes - higher plotted values are higher speeds... there are 10 overlaid plots...

from pylab import *
import matplotlib.pyplot as plt
from time import *
ttot=clock();
mmax=6;npts=20;nplts=10;
x=[int(a+0.5) for a in 10**linspace(0,mmax,npts)]
for nrun in range(nplts):
    j=0;aa=1;bb=1;b=1;
    tim=zeros(npts)
    for n in x:
        aa=rand(n);bb=aa;b=aa;
        if n<100:m=10000
        elif n<5000:m=1000
        elif n<20000:m=100
        else:m=100
        tt=clock()
        for ii in range(1,m+1):
          b=aa*bb+aa
        tt1=clock()-tt
        tim[j]=tt1/n/m
        j=j+1
    print(n,2/(tt1/n/m)/1e6);
    plt.semilogx(x,2/tim/1e6)
    pause(0.01)
print(clock()-ttot)
show()

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)

If I understand the question properly, using Ipython (or Ipython QT or Ipython notebook) would allow you to work interactively with the chart while calculations go one in the background. http://ipython.org/


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

show(block=False)
draw()

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.

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 MikeTex in the following thread, where a robust method has been found: How to wait until matplotlib animation ends?


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)


Try

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.

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.


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

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!






plot