matplotlib - multiple legend styles in an entry

In a current project I had to create a lot of plots. In one plot, several lines should be connected with the same text entry in the legend. To implement this I looked around the internet and stumpled upon a StackOverflow answer from 2015. It guided me to implement a solution, that worked better for my use-case.

The result can be seen in the plot below.

Plot to showcase stacked legend entry.

To paint the symbol that in front of the legend text, matplotlib uses legend handlers defined in matplotlib.legend_handler. It is possible to implement your own handler.

First how one would create legend in matplotlib.

plt.legend([(line1, line2), line3], ['text', 'more text', 'even more'])

were linen are the unpacked return values of plt.plot(…), so called artists If two artists are passed as tuple, the legend command uses the
legend_handler.HandlerTuple handler. It normally draws both symbols on top of each other.

I implemented a new handler that works similar, but draws the two symbols as a stack.

from matplotlib.legend_handler import HandlerTuple

class HandlerTupleVertical(HandlerTuple):
    """Plots all the given Lines vertical stacked."""

    def __init__(self, **kwargs):
        """Run Base Handler."""
        HandlerTuple.__init__(self, **kwargs)

    def create_artists(self, legend, orig_handle,
                       xdescent, ydescent, width, height, fontsize, trans):
        """Create artists (the symbol) for legend entry."""
        # How many lines are there.
        numlines = len(orig_handle)
        handler_map = legend.get_legend_handler_map()

        # divide the vertical space where the lines will go
        # into equal parts based on the number of lines
        height_y = (height / numlines)

        leglines = []
        for i, handle in enumerate(orig_handle):
            handler = legend.get_legend_handler(handler_map, handle)

            legline = handler.create_artists(legend, handle,
                                             xdescent,
                                             (2*i + 1)*height_y,
                                             width,
                                             2*height,
                                             fontsize, trans)
            leglines.extend(legline)

        return leglines

This new handler can now be set to be used instead of the original HandlerTuple by modifying the command shown above to

plt.legend([(line1, line2), line3], ['text', 'more text', 'even more'],
           handler_map = {tuple : HandlerTupleVertical()})

Using this handler, nicer plots are again a little bit easier to make using matplotlib.