I'm drawing a legend on an axes object in matplotlib but the default positioning which claims to place it in a smart place doesn't seem to work. Ideally, I'd like to have the legend be draggable by the user. How can this be done?
Note: This is now built into matplotlib
leg = plt.legend() if leg: leg.draggable()
will work as expected
Well, I found bits and pieces of the solution scattered among mailing lists. I've come up with a nice modular chunk of code that you can drop in and use... here it is:
class DraggableLegend: def __init__(self, legend): self.legend = legend self.gotLegend = False legend.figure.canvas.mpl_connect('motion_notify_event', self.on_motion) legend.figure.canvas.mpl_connect('pick_event', self.on_pick) legend.figure.canvas.mpl_connect('button_release_event', self.on_release) legend.set_picker(self.my_legend_picker) def on_motion(self, evt): if self.gotLegend: dx = evt.x - self.mouse_x dy = evt.y - self.mouse_y loc_in_canvas = self.legend_x + dx, self.legend_y + dy loc_in_norm_axes = self.legend.parent.transAxes.inverted().transform_point(loc_in_canvas) self.legend._loc = tuple(loc_in_norm_axes) self.legend.figure.canvas.draw() def my_legend_picker(self, legend, evt): return self.legend.legendPatch.contains(evt) def on_pick(self, evt): if evt.artist == self.legend: bbox = self.legend.get_window_extent() self.mouse_x = evt.mouseevent.x self.mouse_y = evt.mouseevent.y self.legend_x = bbox.xmin self.legend_y = bbox.ymin self.gotLegend = 1 def on_release(self, event): if self.gotLegend: self.gotLegend = False
...and in your code...
def draw(self): ax = self.figure.add_subplot(111) scatter = ax.scatter(np.random.randn(100), np.random.randn(100)) legend = DraggableLegend(ax.legend())
I emailed the Matplotlib-users group and John Hunter was kind enough to add my solution it to SVN HEAD.
On Thu, Jan 28, 2010 at 3:02 PM, Adam Fraser wrote:
I thought I'd share a solution to the draggable legend problem since it took me forever to assimilate all the scattered knowledge on the mailing lists...
Cool -- nice example. I added the code to legend.py. Now you can do
leg = ax.legend()
to enable draggable mode. You can repeatedly call this func to toggle the draggable state.
I hope this is helpful to people working with matplotlib.
In newer versions of Matplotlib (v1.0.1), this is built-in.
def draw(self): ax = self.figure.add_subplot(111) scatter = ax.scatter(np.random.randn(100), np.random.randn(100)) legend = ax.legend() legend.draggable(state=True)
If you are using matplotlib interactively (for example, in IPython's pylab mode).
plot(range(10), range(10), label="test label") plot(range(10), [5 for x in range(10)], label="another test") l = legend() l.draggable(True)