in matplotlib, is there a way to pop up a figure asynchronously?
In matplotlib, is there a simple way of plotting a figure without interrupting the control flow of the script?
Using pseudocode for clarity, here's 开发者_StackOverflow社区what I'm trying to achieve:
fig1 = figure()
fig1.plot_a_figure(datasets)
for dataset in datasets:
results = analyze(dataset) # this takes several minutes
update(fig1)
pop_up_another_figure(results) # would like to have a look at this one
# while the next dataset is being processed
Of course, I can just savefig() these intermediate figures, but I only need a quick glance at a them and it would be the best to have them just pop up on the screen in real time.
EDIT: A runnable example:
#!/usr/bin/python
import pylab as plb
import matplotlib.pyplot as plt
fig1=plt.figure(1)
ax = fig1.add_subplot(1,1,1)
ax.plot([1,2,3],[4,5,6],'ro-')
#fig1.show() # this does not show a figure if uncommented
plt.show() # until the plot window is closed, the next line is not executed
print "doing something else now"
Am I missing something very very basic?
First things first, don't forget a simple alternative is to just make new figure windows with plt.figure(2)
, plt.figure(3)
etc. If you really want to update the existing figure window, you had better keep a handle on your lines object with
h = ax.plot([1,2,3],[4,5,6],'ro-')
And then later you would be doing something like:
h[0].set_data(some_new_results)
ax.figure.canvas.draw()
As for the real meat of the question, if you're still battling with this read on..
You need to enable interactive mode if you want plt.show()
to be non-blocking. To modify your runnable example so that "doing something else now" would print immediately, as opposed to waiting for the figure window to be closed, the following would do:
#!/usr/bin/python
import pylab as plb
import matplotlib.pyplot as plt
fig1=plt.figure(1)
ax = fig1.add_subplot(1,1,1)
ax.plot([1,2,3],[4,5,6],'ro-')
#fig1.show() # this does not show a figure if uncommented
plt.ion() # turns on interactive mode
plt.show() # now this should be non-blocking
print "doing something else now"
raw_input('Press Enter to continue...')
However, this is just scratching the surface of things - there are many complications once you start wanting to do background work while interacting with the plots. This is a natural consequence of painting with what's essentially a state machine, it doesn't rub well with threading and programming in an object-oriented environment.
- Expensive calculations will have to go into worker threads (or alternatively into subprocesses) to avoid freezing the GUI.
Queue
should be used to pass input data and get results out of the worker functions in a thread-safe way.- In my experience, it is not safe to call
draw()
in the worker thread so you also need to set up a way to schedule a repaint. - Different backends may start to do strange things and
TkAgg
seems to be the only one which works 100% (see here).
The easiest and best solution is not to use the vanilla python interpreter, but to use ipython -pylab
(as ianalis has rightly suggested), because they have already figured out most of the tricks needed to get interactive stuff working smoothly. It can be done without ipython
/pylab
but it's a significant amount of extra work.
Note: I still often like to farm off worker threads whilst using ipython and pyplot GUI windows, and to get threading working smoothly I also need to use another commandline argument ipython -pylab -wthread
. I'm on python 2.7.1+
with matplotlib v1.1.0
, your mileage may vary. Hope this helps!
Note for Ubuntu users: The repositories are still back on v0.99 for quite some time now, it is worth upgrading your matplotlib
because there were many improvements coming up to the v1.0 release including a Bugfix marathon, and major changes to the behaviour of show()
.
Probably the easiest solution is to use IPython as your python shell. Run it with the -pylab option.
ipython -pylab
精彩评论