1

I was looking at this code from another StackOverflow post that showed you how to create a legend entry with multiple colors. I tried adapting the code a little to display hatches (//, xx, etc.) in the legend but couldn't get it to work. Here is my attempt at adding a hatch.

Note: This is just a minimal example, the idea is to have the first rectangle with a hatch and the second color rectangle without a hatch or viceversa. This example is supposed to help me figure out how to add it (when needed) to the patch.

import matplotlib.pyplot as plt
from matplotlib.collections import PatchCollection

# define an object that will be used by the legend
class MulticolorPatch(object):
    def __init__(self, colors):
        self.colors = colors
        
# define a handler for the MulticolorPatch object
class MulticolorPatchHandler(object):
    def legend_artist(self, legend, orig_handle, fontsize, handlebox):
        width, height = handlebox.width, handlebox.height
        patches = []
        for i, c in enumerate(orig_handle.colors):
          patches.append(
              plt.Rectangle(
                  [
                      width/len(orig_handle.colors) * i - handlebox.xdescent, 
                      -handlebox.ydescent
                  ],
                  width / len(orig_handle.colors),
                  height, 
                  facecolor=c, 
                  edgecolor="black",
                  linewidth=0.5,
                  hatch='//'
              )
          )

        patch = PatchCollection(patches, match_original=True)
        handlebox.add_artist(patch)

        return patch


# ------ choose some colors
colors1 = ['r', 'g']
colors2 = ['b', 'y']

# ------ create a dummy-plot (just to show that it works)
f, ax = plt.subplots()
ax.plot([1,2,3,4,5], [1,4.5,2,5.5,3], c='g', lw=0.5, ls='--',
        label='... just a line')
ax.scatter(range(len(colors1)), range(len(colors1)), c=colors1)
ax.scatter([range(len(colors2))], [.5]*len(colors2), c=colors2, s=50)

# ------ get the legend-entries that are already attached to the axis
h, l = ax.get_legend_handles_labels()

# ------ append the multicolor legend patches
h.append(MulticolorPatch(colors1))
l.append("a nice multicolor legend patch")

h.append(MulticolorPatch(colors2))
l.append("and another one")

# ------ create the legend
f.legend(h, l, loc='upper left', 
         handler_map={MulticolorPatch: MulticolorPatchHandler()}, 
         bbox_to_anchor=(.125,.875))

This produces the following even though my intention was to have hatches:

Plot with MultiColor Legend Entry

How can I modify this to add the hatches?

Thanks in advance!

Xavier Merino
  • 679
  • 1
  • 5
  • 19

1 Answers1

0

Currently it's not possible to set individual hatches for a collection, see docs:

Exceptions are the zorder, hatch, pickradius, capstyle and joinstyle properties, which can only be set globally for the whole collection.

The solution would be to not use a collection:

class MulticolorPatchHandler(object):
    def legend_artist(self, legend, orig_handle, fontsize, handlebox):
        width, height = handlebox.width, handlebox.height
        for i, c in enumerate(orig_handle.colors):
          handlebox.add_artist(
              plt.Rectangle(
                  [
                      width/len(orig_handle.colors) * i - handlebox.xdescent, 
                      -handlebox.ydescent
                  ],
                  width / len(orig_handle.colors),
                  height, 
                  facecolor=c, 
                  edgecolor="black",
                  linewidth=0.5,
                  hatch='//' if i==0 else None
              )
          )
        return handlebox

Here's the output using the sample data in the question:

Plot with hatches

Xavier Merino
  • 679
  • 1
  • 5
  • 19
Stef
  • 28,728
  • 2
  • 24
  • 52