I've done a search on SO but haven't quite found the right "solution" to my problem. I am running a loop on some data that I wish to plot. At each step of the loop -- I plot the figure with plt.show(). However, since this is a blocking function, i am stuck until I manually close the window and then the loop continues and the next plot shows up.
What I would like to do is be able to bind a key press event to close the figure and continue the loop (rather than using the mouse to "X" out of the figure).
If this is not possible, I would like to set a timer to close the figure and continue the loop.
All my issues seem to deal with the fact that plt.show() is blocking everything else -- any way around this?
Some notes on my plots: They use the same axes, but contain a scatter plot, fill boxes, and annotations -- which are always changing.
Thanks!
Try using ion from matplotlib.pyplot:
import matplotlib.pyplot as pp
pp.ion()
fig = pp.figure()
More info about ion and interactive vs non-interactive usage here
Alternatively if you want to go with the button press approach assign a callback
def moveon(event):
pp.close()
cid = fig.canvas.mpl_connect('key_press_event', moveon)
pp.show()
An event timer is more tricky because the show command is blocking, so it would probably have to involve threading.
Related
In matplotlib.pyplot, what is the difference between plt.clf() and plt.close()? Will they function the same way?
I am running a loop where at the end of each iteration I am producing a figure and saving the plot. On first couple tries the plot was retaining the old figures in every subsequent plot. I'm looking for, individual plots for each iteration without the old figures, does it matter which one I use? The calculation I'm running takes a very long time and it would be very time consuming to test it out.
plt.close() will close the figure window entirely, where plt.clf() will just clear the figure - you can still paint another plot onto it.
It sounds like, for your needs, you should be preferring plt.clf(), or better yet keep a handle on the line objects themselves (they are returned in lists by plot calls) and use .set_data on those in subsequent iterations.
I think it is worth mentioning that plt.close() releases the memory, thus is preferred when generating and saving many figures in one run.
Using plt.clf() in such case will produce a warning after 20 plots (even if they are not going to be shown by plt.show()):
More than 20 figures have been opened. Figures created through the
pyplot interface (matplotlib.pyplot.figure) are retained until
explicitly closed and may consume too much memory.
plt.clf() clears the entire current figure with all its axes, but leaves the window opened, such that it may be reused for other plots.
plt.close() closes a window, which will be the current window, if not specified otherwise.
There is a slight difference between the two functions.
plt.close() - It altogether plots the graph in seperate windows,releasing
memory,retaining each window for view.
plt.clf() - We can say,it displays the graph in the same window one after other
For illustration, I have plotted two graphs with paramters year and views on X axis and Y axis each. Initially I have used closed function.it displayed the graphs in two seperate windows…
Afterwords, when I run the program with clf() it clears the graph and displays next one in same window i.e figure 1.
Here is the code snippet -
import matplotlib.pyplot as plt
year = [2001,2002,2003,2004]
Views= [12000,14000,16000,18000]
Views2 = [15000,1800,24000,84000]
plt.plot(year,Views)
plt.show()
plt.clf()
plt.plot(year,Views2)
plt.show()
plt.clf()
I've got a matplotlib plot in which I've modified the toolbar/status bar information much as in this question. This works just fine and the necessary information is displayed as I need it.
However, I often need to update the plot (which is done by calling draw() on the canvas object) with new data. What happens though is that the toolbar/status bar information won't update until the mouse is moved again. I'd like this information to update as soon as the canvas is redrawn because some of this information is pertinent to the new plot.
My attempt at making this happen was to force a mouse motion event to trigger. I've tried to trigger the event from the canvas via self.canvas.motion_notify_event(0,0) but that doesn't seem to work well. I can see that the toolbar itself has a mouse_move method, but I don't know how to trigger it (or even if it's what I want to trigger).
How can I force the toolbar/status bar information to update during a plot redraw without requiring the user to move their mouse slightly?
Did a lot of digging and figured it out. The motion_notify_event method was what I was looking for, I was just using it wrong. You can fake matplotlib out and trigger a MouseEvent by using this function, which will then make matplotlib call all functions that respond to MouseEvents, including updating the toolbar/status bar information.
The key here is that I needed to trigger the MouseEvent as if it happened within the axes object, not the entire figure. The input to the function is the (x,y) pixel position of the event with respect to the lower left corner of the figure window. By using (0,0) as I did in my question, I was saying the mouse event happened at the lower left corner of the figure window, not on the axes itself. Matplotlib does not show toolbar/status bar information unless the cursor is on the axes.
What you can do then is pick some random pixel position on the axes and use that as the position. A simple way to pick such a pixel position is using matplotlib transformations.
The following now works for me:
canvas.motion_notify_event(*ax.transAxes.transform([0,0]))
Of course in my case I'm not displaying the data coordinates of the mouse, so your use cases may vary.
I have created a little GUI with QT which set's up a single matplotlib figure and axes.
The GUI has controls to change just about everything to do with the appearance of the axes.
Basically, it does this by each widget emitting signals back up to the main frame and it calls the appropriate matplotlib setters on the axes and figure objects.
However, it is possible for the axes (and therefore the image displayed on the FigureCanvas) to change without input from the GUI (e.g. when autoscaling, or adding certain plots which adjust the axes automatically).
In this case, a widget controlling e.g. the limits of the x axis will now be displaying the wrong values.
I would like all the relevant widgets to update when the axes updates....how could I possible achieve this?
I'm thinking that this is a problem that has been solved before - how to enable a two-way communication between distinct objects?
fig.canvas.draw()
time.sleep(1e-2)
whenever anything writes to the plot? however it's hard to help with no code.
Showing an example of how your code is not working would help a lot.
EDIT:
I'll try this again then:
What about getting the state of the plot you are updating? I guess its what #Ajean means by updater method. I know that Artists in matplotlib have an Artist.properties() method that returns all of the properties and values.
I imagine Axes would have a similar method.
A quick look at the matplotlib docs yielded 2 interesting methods of axes:
ax.get_autoscale_on()
and
ax.set_autoscale_on().
ax.set_autoscale_on(False) will prevent plots from updating the state of the axes.
To give you a sense of what I'm looking for, it looks like this:
Up until now I have used matplotlib for all my plotting, and timing hasn't been critical (it's been done in postprocessing).
I'm wondering if there is a lighter weight way to plot other than shifting my data to the left and redrawing the entire plot.
Have a look at the Matplotlib Animations Examples. The main trick is to not completely redraw the graph but rather use the OO interface of matplotlib and set the x/ydata of the plot-line you created. If you've integrated your plot with some GUI, e.g., GTK, then definitely do it like proposed in the respective section of the plot, otherwise you might interfere with the event-loop of your GUI toolkit.
For reference, if the link ever dies:
from pylab import *
import time
ion()
tstart = time.time() # for profiling
x = arange(0,2*pi,0.01) # x-array
line, = plot(x,sin(x))
for i in arange(1,200):
line.set_ydata(sin(x+i/10.0)) # update the data
draw() # redraw the canvas
print 'FPS:' , 200/(time.time()-tstart)
You could try streamlit lib.
Should be the lightest one.
In the python wiki there is a list of suggestions with short descriptions: link.
I have been having some trouble with matplotlib since I started using python. When I use the interactive mode, I have to run ipython --pylab --wthread to get plots (if I don't use the --wthread command, it does not plot anything). So far, it hasn't been a problem.
Now, I want to:
Enter a loop
Plot something. This plot is a big plot with two subplots.
AFTER, show a button pannel with easygui to let the user decide depending on what he sees on the plot
close the plot
repeat for each thing to plot in my list.
I am finding several difficulties right now with this:
1) if I try to run the script in an interactive way using the run script.py command, then it does not plot annything, but directly jumps to the button pannel thing. However, if I stop the script, the plots appear. I am pretty sure that what is happening is that the "run" command does not show plots untill the script is done with the for loop. But I need the plots to appear before the buttons.
2) After some tries, I found this code to work (for some mystical reason to me...). I have tried other ways, but removing any of the show() or draw() commands just make the script not to work
fig=plt.figure(figsize=(16,8))
plt.show()
ax1=fig.add_subplot(121)
ax2.things...
ax2=fig.add_subplot(122)
ax2.things
plt.draw()
plt.show()
plt.draw()
showthebuttonsthing...
Even if this is working, matplotlib seems not to get along well with loops, and after 5 seconds of not pressing any button an just waiting, my matplotlib window gets grey. This might sound as something stupid, but colors are important for the decision I want the user the make...
3) I can't find a way to make a python script show plots if I run the python script outside ipython...
I guess there is something really wrong with my matplotlib configuration, but can't find a way to make this work...can anyone give me an hand on this?
Thanks a lot in advance!
It looks like you've almost got it, but try doing things in this order instead:
fig = plt.figure(figsize=(16,8))
ax = [fig.add_subplot(121),fig.add_subplot(122)]
ax[0].things
ax[1].things
plt.show()
#show the button panel
However, the best method may be to integrate the plot into your GUI. I am not familiar with EasyGUI, but it seems to be based on tk. This example should help you with embedding the figure into a tk window.