Obtaining the figure manager via the OO interface in Matplotlib - matplotlib

I want to be able to get the figure_manager of a created figure:
e.g. I can do it with the pyplot interface using:
from pylab import*
figure()
plot(arange(100))
mngr = get_current_fig_manager()
However what if I have a few figures:
from pylab import *
fig0 = figure()
fig1 = figure()
plot(arange(100))
mngr = fig0.get_manager() #DOES NOT WORK - no such method as Figure.get_manager()
however, searching carefully through the figure API, http://matplotlib.org/api/figure_api.html, was not useful. Neither was auto-complete in my IDE on an instance of a figure, none of the methods / members seemed to give me a 'manager'.
So how do I do this and in general, where should I look if there is a pyplot method whose analogue I need in the OO interface?
PS: what kind of an object is returned by get_current_fig_manager() anyway?
In the debugger i get:
type(get_current_fig_manager())
<type 'instance'>
which sounds pretty mysterious...

Good question. Your right, the docs don't say anything about being able to get the manager or the canvas. From experience of the code the answer to your question is:
>>> import matplotlib.pyplot as plt
>>> a = plt.figure()
>>> b = plt.figure()
>>> a.canvas.manager
<matplotlib.backends.backend_tkagg.FigureManagerTkAgg instance at 0x1c3e170>
>>> b.canvas.manager
<matplotlib.backends.backend_tkagg.FigureManagerTkAgg instance at 0x1c42ef0>
The best place to find out about this stuff is by reading the code. In this case, I knew I wanted to get the canvas so that I could get hold of the figure manager, so I looked at the set_canvas method in figure.py and found the following code:
def set_canvas(self, canvas):
"""
Set the canvas the contains the figure
ACCEPTS: a FigureCanvas instance
"""
self.canvas = canvas
From there, (as there was no get_canvas method), I knew where the canvas was being stored and could access it directly.
HTH

Related

matplotlib chart construction to suit use flask? (working example uses matplotlib.figure FigureCanvasAgg) can I construct errorbars?

I have a matplotlib chart working nicely as a python script. I need to create this chart style in flask. Can't use this method within flask as flask thread management doesn't play with matplotlib.
Oddly, the current method will run once successfully, subsequent runs will produce this error.
RuntimeError: main thread is not in main loop
So this is my desired chart format to produce in flask.
the code I'm using currently.
fig, ax = plt.subplots()
fig.subplots_adjust(bottom=0.29, top=.91)
ax.set_title(title)
ax.set_ylabel("y label text")
ax.set_xlabel('x label text')
ax.tick_params(axis='x', labelrotation = -80)
l = ax.plot(df_output['column1'])
y_error = df_output['column2']
plt.errorbar(list(df_output.index), \
list(df_output['column1']), \
yerr = y_error,fmt='o',ecolor = 'blue',color='blue')
fig.legend(l, loc=8, labels=labels)
#loc=2 = top left corner, loc=8 = 'lower center'
#plt.show()
plt.savefig(output_path+"/"+title+'_errorbars.png')
I found this example that works with flask
https://gist.github.com/illume/1f19a2cf9f26425b1761b63d9506331f
it uses this matplotlib charting syntax. Need to convert my old matplotlib format to suit the flask compatible format. Is this chart format possible via FigureCanvasAgg?
fig = Figure()
axis = fig.add_subplot(1, 1, 1)
print("type(axis):", type(axis))
x_points = data.iloc[:, 0]
y_points = data['mean minus sterility control mean']
axis.plot(x_points, y_points)
output = io.BytesIO()
FigureCanvasAgg(fig).print_png(output)
return Response(output.getvalue(), mimetype="image/png")
I'll admit to not being strong in building matpotlib charts. changing between chart building methods throws me.
I'm digging around the docs at moment.
https://matplotlib.org/stable/gallery/user_interfaces/canvasagg.html
I did find this Q&A (RuntimeError: main thread is not in main loop with Matplotlib and Flask)
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
while this appears to run for me. I want to move away from creating charts as files on the server, too much potential for file mismanagement, creating the chart as a io.BytesIO() output (or some format within the flask http response to user) is a much better solution.
(I'd like to keep at an image output, rather than change architecture to (say) a json output and constructing chart in client using javascript libraries)

How to prevent 1e9 from being shown to exponential form in Python matplotlib figure

I've seen this. How to prevent numbers being changed to exponential form in Python matplotlib figure
However, I've got some custom annotations to put in, and I'd just like matplotlib to just not show the 1e9 marker. Example code below
import matplotlib.pyplot as plt
import seaborn as sns
sns.set() # not necessary, but just to reproduce the photo below
f, a = plt.subplots() # I use the oop interface
pd.DataFrame({'y': [1e9, 2e9, 3e9], 'x': [1, 2, 3]}).set_index('x').plot(ax=a)
Yields:
How do I just not show the 1e9? I have a custom annotation there which says 'billions' and it overlaps.
My thanks to ImportanceOfBeingErnest above, as
a.yaxis.offsetText.set_visible(False)
solves.

Adding an on click event to an Annotation in pyplot

I'm looking for a way to add an "on click" event to an annotation in matplotlib.pyplot to destroy it. The relevant code:
import matplotlib.pyplot as plt
plt.ion()
plt.plot()
plt.annotate("Kill me",xy=(0,0))
Now we need to find the annotation, one way is to iterate over:
plt.gca().texts
Though there may be a better way. So far I have not found how to obtain the widget/add an event with this. This may be possible using mpl_connect of the plt figure canvas but I'm not sure, and that would require going by the bounding box, which I would like to avoid, but if no other is solution is available is fine.
You can indeed use mpl_connect to connect a picker event to an object in the canvas. In this case the call to annotate can be given a picker argument, which specifies the radius around the object that should trigger the event.
You can then directly operate on the object that triggered the event, which is available in the event slot as event.artist.
import matplotlib.pyplot as plt
fig = plt.figure()
ax=fig.add_subplot(111)
plt.plot([0,5],[0,6], alpha=0)
plt.xlim([-1,6])
plt.ylim([-1,6])
for i in range(6):
for j in range(6):
an = plt.annotate("Kill me",xy=(j,i), picker=5)
def onclick(event):
event.artist.set_text("I'm killed")
event.artist.set_color("g")
event.artist.set_rotation(20)
# really kill the text (but too boring for this example;-) )
#event.artist.set_visible(False)
# or really REALLY kill it with:
#event.artist.remove()
fig.canvas.draw()
cid = fig.canvas.mpl_connect('pick_event', onclick)
plt.show()

how to close a show() window but keep the figure alive?

if I create a figure then do plt.close() :
from matplotlib import pyplot as plt
fig1 = plt.figure()
fig2 = plt.figure()
fig1.show()
plt.close()
fig1.show()
fig2.show()
the fig1 will only show once, because the plt.close() will destroy the figure object referred by fig1. How can I only close the window without destroy the figure?
So far nothing really works.
after each plt.figure(), a new figure_manager is going to be generated. And will be put into a list in plt instance.
>>> print plt.get_fignums()
[1, 2]
however, after plt.close(), the figure_manager of the specific figure will be pop out.
>>> print plt.get_fignums()
[2]
As #John Sharp mentioned plt._backend_mod.new_figure_manager_given_figure(plt.get_fignums()[-1]+1,fig1) will create a new figure_manager for the fig1. However, it has not been added to the plt. So it is impossible to control those figure_manager while plt:
>>> plt._backend_mod.new_figure_manager_given_figure(plt.get_fignums()[-1]+1,fig1)
<matplotlib.backends.backend_tkagg.FigureManagerTkAgg instance at 0x2b0f680>
>>> print plt.get_fignums()
[2]
>>> plt.new_figure_manager(1)
<matplotlib.backends.backend_tkagg.FigureManagerTkAgg instance at 0x2b1e3f8>
>>> plt.get_fignums()
[2]
So it cannot be closed by plt.close(), except directly call figure_manager.destroy()
The suggestion to set current fm directly will be worse:
fm = plt.get_current_fig_manager()
fm.canvas.figure = fig1
fig1.canvas = fm.canvas
at the first glance, this seems work. However, it will directly change the fig2's fm to point to fig1, which will cause lots of trouble.
If there is any way, we can make the pyplot to register manually generated fm, that may work. So far have no luck there.
Since the Figure is still referenced by the name fig1 it is not destroyed. You just need to create a new figure manager for the figure. One way to do this is to get a new figure manager by generating a new blank figure and manually set the canvas figure to be fig1:
plt.figure()
fm = plt.get_current_fig_manager()
fm.canvas.figure = fig1
fig1.canvas = fm.canvas
Once you've done this you can show and then close the figure with:
fig1.show()
plt.close()
Alternatively, if you were showing two figures at once and only wanted to close a particular one, instead of using plt.close() you can call the fm.destroy() method to close the window showing only the particular figure referenced by that frame manager.

Interactive image plotting with matplotlib

I am transitioning from Matlab to NumPy/matplotlib. A feature in matplotlib that seems to be lacking is interactive plotting. Zooming and panning is useful, but a frequent use case for me is this:
I plot a grayscale image using imshow() (both Matlab and matplotlib do well at this). In the figure that comes up, I'd like to pinpoint a certain pixel (its x and y coordinates) and get its value.
This is easy to do in the Matlab figure, but is there a way to do this in matplotlib?
This appears to be close, but doesn't seem to be meant for images.
custom event handlers are what you are going to need for this. It's not hard, but it's not "it just works" either.
This question seems pretty close to what you are after. If you need any clarification, I'd be happy to add more info.
I'm sure you have managed to do this already. Slightly(!) modifying the link, I've written the following code that gives you the x and y coordinates once clicked within the drawing area.
from pylab import *
import sys
from numpy import *
from matplotlib import pyplot
class Test:
def __init__(self, x, y):
self.x = x
self.y = y
def __call__(self,event):
if event.inaxes:
print("Inside drawing area!")
print("x: ", event.x)
print("y: ", event.y)
else:
print("Outside drawing area!")
if __name__ == '__main__':
x = range(10)
y = range(10)
fig = pyplot.figure("Test Interactive")
pyplot.scatter(x,y)
test = Test(x,y)
connect('button_press_event',test)
pyplot.show()
Additionally, this should make it easier to understand the basics of interactive plotting than the one provided in the cookbook link.
P.S.: This program would provide the exact pixel location. The value at that location should give us the grayscale value of respective pixel.
Also the following could help:
http://matplotlib.sourceforge.net/users/image_tutorial.html