Adding an on click event to an Annotation in pyplot - matplotlib

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

Related

how to make plt plot do not show figure [duplicate]

I need to create a figure in a file without displaying it within IPython notebook. I am not clear on the interaction between IPython and matplotlib.pylab in this regard. But, when I call pylab.savefig("test.png") the current figure get's displayed in addition to being saved in test.png. When automating the creation of a large set of plot files, this is often undesirable. Or in the situation that an intermediate file for external processing by another app is desired.
Not sure if this is a matplotlib or IPython notebook question.
This is a matplotlib question, and you can get around this by using a backend that doesn't display to the user, e.g. 'Agg':
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
plt.plot([1,2,3])
plt.savefig('/tmp/test.png')
EDIT: If you don't want to lose the ability to display plots, turn off Interactive Mode, and only call plt.show() when you are ready to display the plots:
import matplotlib.pyplot as plt
# Turn interactive plotting off
plt.ioff()
# Create a new figure, plot into it, then close it so it never gets displayed
fig = plt.figure()
plt.plot([1,2,3])
plt.savefig('/tmp/test0.png')
plt.close(fig)
# Create a new figure, plot into it, then don't close it so it does get displayed
plt.figure()
plt.plot([1,3,2])
plt.savefig('/tmp/test1.png')
# Display all "open" (non-closed) figures
plt.show()
We don't need to plt.ioff() or plt.show() (if we use %matplotlib inline). You can test above code without plt.ioff(). plt.close() has the essential role. Try this one:
%matplotlib inline
import pylab as plt
# It doesn't matter you add line below. You can even replace it by 'plt.ion()', but you will see no changes.
## plt.ioff()
# Create a new figure, plot into it, then close it so it never gets displayed
fig = plt.figure()
plt.plot([1,2,3])
plt.savefig('test0.png')
plt.close(fig)
# Create a new figure, plot into it, then don't close it so it does get displayed
fig2 = plt.figure()
plt.plot([1,3,2])
plt.savefig('test1.png')
If you run this code in iPython, it will display a second plot, and if you add plt.close(fig2) to the end of it, you will see nothing.
In conclusion, if you close figure by plt.close(fig), it won't be displayed.

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.

Annotating a box outside the box, matplotlib

I want the text to appear beside the box instead of inside it:
Here is what I did:
import matplotlib as mpl
import matplotlib.pyplot as plt
from custombox import MyStyle
fig = plt.figure(figsize=(10,10))
legend_ax = plt.subplot(111)
legend_ax.annotate("Text",xy=(0.5,0.5),xycoords='data',xytext=(0.5, 0.5),textcoords= ('data'),ha="center",rotation = 180,bbox=dict(boxstyle="angled, pad=0.5", fc='white', lw=4, ec='Black'))
legend_ax.text(0.6,0.5,"Text", ha="center",size=15)
Here is what it gives me:
Note: custombox is similar to the file that is written in this link:
http://matplotlib.org/1.3.1/users/annotations_guide.html
My ultimate aim is to make it look legend like where the symbol (angled box) appears beside the text that represents it.
EDIT 1: As suggested by Ajean I have annotated text separately but I can't turn of the text within the arrow
One way to do it would be to separate the text and the bbox (which you can reproduce using an arrow). The following gives me something close to what you want, I think.
import matplotlib.pyplot as plt
from matplotlib import patches
fig = plt.figure(figsize=(5,5))
ax = fig.add_subplot(111)
ax.annotate("Text", (0.4,0.5))
bb = patches.FancyArrow(0.5,0.5,0.1,0.0,length_includes_head=True, width=0.05,
head_length=0.03, head_width=0.05, fc='white', ec='black',
lw=4)
ax.add_artist(bb)
plt.show()
You can futz with the exact placement as needed. I'm not an expert on all the kwargs, so this may not be the best solution, but it will work.

Calling pylab.savefig without display in ipython

I need to create a figure in a file without displaying it within IPython notebook. I am not clear on the interaction between IPython and matplotlib.pylab in this regard. But, when I call pylab.savefig("test.png") the current figure get's displayed in addition to being saved in test.png. When automating the creation of a large set of plot files, this is often undesirable. Or in the situation that an intermediate file for external processing by another app is desired.
Not sure if this is a matplotlib or IPython notebook question.
This is a matplotlib question, and you can get around this by using a backend that doesn't display to the user, e.g. 'Agg':
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
plt.plot([1,2,3])
plt.savefig('/tmp/test.png')
EDIT: If you don't want to lose the ability to display plots, turn off Interactive Mode, and only call plt.show() when you are ready to display the plots:
import matplotlib.pyplot as plt
# Turn interactive plotting off
plt.ioff()
# Create a new figure, plot into it, then close it so it never gets displayed
fig = plt.figure()
plt.plot([1,2,3])
plt.savefig('/tmp/test0.png')
plt.close(fig)
# Create a new figure, plot into it, then don't close it so it does get displayed
plt.figure()
plt.plot([1,3,2])
plt.savefig('/tmp/test1.png')
# Display all "open" (non-closed) figures
plt.show()
We don't need to plt.ioff() or plt.show() (if we use %matplotlib inline). You can test above code without plt.ioff(). plt.close() has the essential role. Try this one:
%matplotlib inline
import pylab as plt
# It doesn't matter you add line below. You can even replace it by 'plt.ion()', but you will see no changes.
## plt.ioff()
# Create a new figure, plot into it, then close it so it never gets displayed
fig = plt.figure()
plt.plot([1,2,3])
plt.savefig('test0.png')
plt.close(fig)
# Create a new figure, plot into it, then don't close it so it does get displayed
fig2 = plt.figure()
plt.plot([1,3,2])
plt.savefig('test1.png')
If you run this code in iPython, it will display a second plot, and if you add plt.close(fig2) to the end of it, you will see nothing.
In conclusion, if you close figure by plt.close(fig), it won't be displayed.

Obtaining the figure manager via the OO interface in 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