Matplotlib draw to plot within function immediately - python

Is there any way to draw to a plot within a function and then have the changes show up on the plot before the function is finished executing?
For instance, in this function I would like to get the window extent of the text, but the function gives the error Cannot get window extent w/o renderer
because at the time s.get_window_extent() is called the text has not been drawn on the plot.
import matplotlib.pyplot as plt
import time
plt.ion()
myfig, myax = plt.subplots()
def plot_now():
s = myax.annotate("foo", [0.5, 0.5])
myfig.canvas.draw_idle()
s.get_window_extent()
plot_now()
I'm using iPython, python 2.7.6, matplotlib 1.4.3, and the Qt4Agg backend

Add plt.draw() before it goes sleep.

Try plt.pause(60) instead of time.sleep(60). Someone answering this question says that time.sleep doesn't work with Qt4Agg.

Related

Make several figures from a dictionary without overwriting the previous figure [duplicate]

I have a Python program that generates graphs using matplotlib. I am trying to get the program to generate a bunch of plots in one program run (the user is asked if they want to generate another graph) all in separate windows. Any way I can do this?
To generate a new figure, you can add plt.figure() before any plotting that your program does.
import matplotlib.pyplot as plt
import numpy as np
def make_plot(slope):
x = np.arange(1,10)
y = slope*x+3
plt.figure()
plt.plot(x,y)
make_plot(2)
make_plot(3)
Using the latest matlibplot, I found the following to work for my purposes:
# create figure (will only create new window if needed)
plt.figure()
# Generate plot1
plt.plot(range(10, 20))
# Show the plot in non-blocking mode
plt.show(block=False)
# create figure (will only create new window if needed)
plt.figure()
# Generate plot2
plt.plot(range(10, 20))
# Show the plot in non-blocking mode
plt.show(block=False)
...
# Finally block main thread until all plots are closed
plt.show()
The easiest way to ensure all of your lines go to the correct figure window is something like:
from six.moves import input
import matplotlib.pyplot as plt
another = True
while another:
fig, ax = plt.subplots()
ax.plot(range(5))
fig.canvas.manager.show()
# this makes sure that the gui window gets shown
# if this is needed depends on rcparams, this is just to be safe
fig.canvas.flush_events()
# this make sure that if the event loop integration is not
# set up by the gui framework the plot will update
another = bool(input("would you like another? "))
If you want to run this with a non-gui backend you will need to drop the flush_events call or wrap it in a try: ... except NotImplementedError. Much of this complication is defensive programming because GUIs can be difficult and the behavior of this code may be dependent on many factors which are not obvious from the code shown.
Using the implicit axes of pyplot can cause problems as the 'current axes' is set by the last axes the user clicked on. You should really only use pyplot when interactively typing at the rpel and almost never (other than plt.subplots) in scripts/programs.
Use the .figure() function to create a new window, the following code makes two windows:
import matplotlib.pyplot as plt
plt.plot(range(10)) # Creates the plot. No need to save the current figure.
plt.draw() # Draws, but does not block
plt.figure() # New window, if needed. No need to save it, as pyplot uses the concept of current figure
plt.plot(range(10, 20))
plt.draw()
You can repeat this as many times as you want

Matplotlib reuse figure created by another script

I am using Matplotlib on MacOS with Sulime Text.
I use Python 3.5 and Matplotlib 2.0.
When I work on a figure, I usually have a script that plot the data, and save the figure in a .pdf file with plt.savefig(). Then I use Skim (a pdf viewer) in order to refresh the file each time I modify and run the script. This allows me to set my working layout as clean as: there is one window for the script, and one window for the figure which is automatically refreshing.
I would like to do keep the same layout, but using the Matplotlib figures (because they are interactive). I am looking for a way to use plt.show() but always in the same figure that has been created the first time I've run the script.
For instance:
1. First run
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.figure()
noise = np.random.rand(1, 100)
ax(noise)
plt.show()
2. Following runs
import matplotlib.pyplot as plt
import numpy as np
# This is the super command I am looking for
fig = plt.get_previous_run_figure()
ax = fig.axes
noise = np.random.rand(1, 100)
ax.plot(noise)
plt.draw()
In that case of course, I would have to do a first-run script separately from the main script. Does anyone know if it is possible ?
You want to have multiple consecutive python sessions share a common Matplotlib window. I see no way to share this windows from separate processes, especially when the original owner may terminate at any point in time.
However, you could do something similar to your current workflow in which you have an external pdf viewer to view a output file which you update from multiple python instances.
See this question/answer on how to pickle a matplotlib figure:
Store and reload matplotlib.pyplot object
In every script, output your matplotlib figure as a pickled object, rather than calling plt.show():
import matplotlib.pyplot as plt
import numpy as np
import pickle
ax = plt.subplot(111)
x = np.linspace(0, 10)
y = np.exp(x)
plt.plot(x, y)
pickle.dump(ax, file('myplot.pickle', 'w'))
Then, start a dedicated python session which loads this pickled object and calls plt.show(). Have this script run in a loop, checking for updates of the pickled file on disk, and reloading when necessary:
import matplotlib.pyplot as plt
import pickle
while True:
ax = pickle.load(file('myplot.pickle'))
plt.show()
Alternative
Instead of having separate python sessions, I usually have a single Ipython session in which I run different script. By selecting the same figure windows, I end up with a mostly similar setup as you describe, in which the same figure window is reused throughout the day.
import matplotlib.pyplot as plt
fig = plt.figure(0)
fig.clf()
plt.show()
In principle establishing a connection between two different scripts could be done using a system-wide clipboard. (As far as I know the clipboard in windows and macos are system-wide.)
So the idea can be to set up an application using tk or pyqt, and implement a generic FigureCanvas. This application could have an event listener for changes in the clipboard.
The other main workflow script would then call some function that wraps the current figure into a pickle object and sends it to the clipboard, from where it gets caught by the GUI application, is unpickled and shown in the canvas.
This sounds like a little bit of work, but should meet the very restrictive requirements from the question.
The alternative from Daan worked for me. Here's a bit more code. I used this in a Tkinter interactive GUI for reusing/updating a matplotlib figure window:
fig1 = None
if fig1:
#if exists, clear figure 1
plt.figure(1).clf()
plt.suptitle("New Fig Title", fontsize=18, fontweight='bold')
#reuse window of figure 1 for new figure
fig1 = plt.scatter(points.T[0], points.T[1], color='red', **plot_kwds)
else:
#initialize
fig1 = plt.figure(num=1,figsize=(7, int(7*imgRatio)), dpi=80)
plt.tick_params(axis='both', which='major', labelsize=14)
plt.tick_params(axis='both', which='minor', labelsize=14)
plt.suptitle("Title", fontsize=18, fontweight='bold')
fig1 = plt.scatter(points.T[0], points.T[1], color='red', **plot_kwds)
The figure is reusing the (interactive) plt window. For this to work, I had to set interactive : True in the matplotlibrc file (see my comment here)

Showing a few figures without stopping calculations in matplotlib

Hi I would like to show a few figures in matplotlib without stopping calculations. I would like the figure to show up right after the calculations that concern it are finished for example:
import numpy as np
import pylab as py
x=np.linspace(0,50,51)
y=x
fig, axs = plt.subplots(1, 1)
cs = axs.plot(x, y)
now i want to show the plot without blocking the possibility to make some other calculations
plt.show(block=False)
plt.pause(5)
I create the second plot
y1=2*x
fig1, axs1 = plt.subplots(1, 1)
cs1 = axs1.plot(x, y1)
plt.show()
This works however the first freezes (after 5 secound pause which I added) until I call plt.show() at the end. It is crucial that the first figure shows and works, then after calculations another figure is added to it.
The following code should do what you want. I did this in an IPython Notebook.
from IPython import display
import matplotlib.pyplot as plt
def stream_plot(iterable, plotlife=10.):
for I in iterable:
display.clear_output(wait=True)
output = do_calculations_on_i(I)
plt.plot(output)
display.display(plt.gca());
time.sleep(plotlife); #how long to show the plot for
the wait=True will wait to clear the old plot until it has something new to plot, or any other output is printed.
I put the sleep in there so I can observe each plot before it is wiped away. This was useful for having to observe distributions for several entities. You may or may not need it for what you want to do.

Updating a plot in python's matplotlib

I'm trying to plot streaming data in matplotlib. I can update the plot using interactive mode and the set_ydata function. It animates and everything looks good until the loop ends. Then the python kernel crashes and I get this message:
C:\Conda\lib\site-packages\matplotlib\backend_bases.py:2437:
MatplotlibDeprecationWarning: Using default event loop until function specific to
this GUI is implemented
warnings.warn(str, mplDeprecation)
Here's the code:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 10, 0.1)
y = np.sin(x)
plt.ion() #interactive mode on
ax = plt.gca()
line, = ax.plot(x,y)
ax.set_ylim([-5,5])
for i in np.arange(100):
line.set_ydata(y)
plt.draw()
y = y*1.01
plt.pause(0.1)
Can anyone tell me why this is crashing instead of just exiting the loop? I'm doing this in Jupyter with Python 3. And of course, if there's a better way to do this, I would love to hear about it. Thanks!
This code was adapted from How to update a plot in matplotlib?
It works well for me with mac_osx backend from Jupyter notebook in python 3.4.
Maybe you want to add plt.close() at the end to keep things tidy and prevent a hang up?

How to toggle visibility of matplotlib figures?

Is there a way that I can make a matplotlib figure disappear and reappear in response to some event? (i.e. a keypress)
I've tried using fig.set_visible(False) but that doesn't seem to do anything for me.
Simple example of code:
import matplotlib
import matplotlib.pyplot as plt
fig=matplotlib.pyplot.figure(figsize=(10, 10))
# Some other code will go here
def toggle_plot():
# This function is called by a keypress to hide/show the figure
fig.set_visible(not fig.get_visible()) # This doesn't work for me
plt.show()
The reason I'm trying to do this is because I have a bunch of plots/animations running on the figure that show the output of a running simulation, but displaying them all the time slows down my computer a lot.
Any ideas?
You have to call plt.draw() to actually instantiate any changes. This should work:
def toggle_plot():
# This function is called by a keypress to hide/show the figure
fig.set_visible(not fig.get_visible())
plt.draw()
There is a small guide to image toggling in the matplotlib gallery. I was able to use set_visible and get_visible() as shown in the example. The calls in the matplotlib gallery example are on AxesImage instances, rather than Figure instances, as in your example code. That is my guess as to why it did not work for you.
You can you use the Toplevel() widget from the tkinter library together with the matplotlib backend.
Here is a full example:
from tkinter import *
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
fig,(ax) = plt.subplots()
x = np.linspace(0, 2 * np.pi)
y = np.transpose([np.sin(x)])
ax.plot(y)
graph = Toplevel()
canvas = FigureCanvasTkAgg(fig,master=graph)
canvas.get_tk_widget().grid()
canvas.show()
import pdb; pdb.set_trace()
Calling:
graph.withdraw()
will hide the plot, and:
graph.deiconify()
will display it again.

Categories

Resources