1
votes

Currently, I am trying to make my own custom legend handler by creating a proxy artist (?) patch using PatchCollections and then following http://matplotlib.org/users/legend_guide.html to make a custom handler.

However I am running into a roadblock in trying to implement this into the legend. The arguments for legend takes in patches, but not patchcollections.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
import matplotlib.patches as mpatches
from matplotlib.path import Path
from matplotlib.collections import PatchCollection

fig = plt.figure()
ax = fig.add_subplot(111)

verts1 = [(0.,0.),(0.,1.),(1.,1.),(0.51,0.51),(0.,0.),(0.,0.),]
codes1 = [Path.MOVETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.MOVETO,Path.CLOSEPOLY,]
path1 = Path(verts1,codes1)
patch1 = mpatches.PathPatch(path1,ls='dashed',ec='red',facecolor="none")


verts2 = [(0.49,0.49),(0.,0.),(1.,0.),(1.,1.),(0.5,0.5),(0.,0.),]
codes2 = [Path.MOVETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.MOVETO,Path.CLOSEPOLY,]
path2 = Path(verts2,codes2)
patch2 = mpatches.PathPatch(path2,ls='solid',edgecolor='red', facecolor="none")

patch = PatchCollection([patch1,patch2],match_original=True)

ax.set_xlim(-2,2)
ax.set_ylim(-2,2)

ax.add_collection(patch)

Visual

The above is the code to visualise the handler. Basically a rectangle with the upper triangle as dashed lines and the lower as solid

Using,

plt.legend([patch],["hellocello"],loc='upper right')

Recreates the error. Is there a workaround?

1

1 Answers

4
votes

From the example in this section, it looks like you need to define an object and express all coordinates in terms of the handlebox size,

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
import matplotlib.patches as mpatches
from matplotlib.path import Path
from matplotlib.collections import PatchCollection

class AnyObject(object):
    pass

class AnyObjectHandler(object):
    def legend_artist(self, legend, orig_handle, fontsize, handlebox):
        x0, y0 = handlebox.xdescent, handlebox.ydescent
        width, height = handlebox.width, handlebox.height
        hw = 0.5*width; hh = 0.5*height
        verts1 = [(x0,y0),(x0,y0+height),(x0+width,y0+height),((x0+hw)*1.01,(y0+hh)*1.01),(x0,y0),(x0,y0),]
        codes1 = [Path.MOVETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.MOVETO,Path.CLOSEPOLY,]
        path1 = Path(verts1,codes1)
        patch1 = mpatches.PathPatch(path1,ls='dashed',ec='red',facecolor="none")

        verts2 = [((x0+hw)*0.99,(y0+hh)*0.99),(x0,y0),(x0+width,y0),(x0+width,y0+height),(x0+hw,y0+hh),(x0,y0),]
        codes2 = [Path.MOVETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.MOVETO,Path.CLOSEPOLY,]
        path2 = Path(verts2,codes2)
        patch2 = mpatches.PathPatch(path2,ls='solid',edgecolor='red', facecolor="none")

        patch = PatchCollection([patch1,patch2],match_original=True)

        handlebox.add_artist(patch)
        return patch


fig = plt.figure()
ax = fig.add_subplot(111)

ax.set_xlim(-2,2)
ax.set_ylim(-2,2)

plt.legend([AnyObject()], ['hellocello'],
           handler_map={AnyObject: AnyObjectHandler()})

plt.show()

This seems to work okay with PatchCollection, at least for me on matplotlib version 1.4.3. The result looks like,

enter image description here