Skip to content

Figure legend not keeping Line2D marker #9155

Closed

Description

@anhiga

Bug report

Bug summary

When retrieving handles used in legend, marker property is not kept.

Code for reproduction

import matplotlib.pyplot as plt

point = plt.Line2D([1],[1], marker='o')
legend = plt.gcf().legend(handles=[point], labels=['point'])

# First way
handles = legend.legendHandles
handles[0].get_marker()
# Second way
lines= legend.get_lines()
lines[0].get_marker()

Actual outcome

Out[1]: ''

Expected outcome
When plotting an Axes legend, get_legend_handles_labels() can be used, which does return the right marker. There isn't such a method in Figure, so when using Legend methods or properties such as legend.get_lines() or legend.legendHandles, line2D marker isn't kept.

Is there a better way of retrieving handles from a legend?
Am I doing something wrong?

Matplotlib version

  • Operating System: Windows 10
  • Matplotlib Version: 2.0.2
  • Python Version: 3.6.1

cc @juanlu001

Activity

  1. jklymak commented on Sep 5, 2017

    @jklymak
    Member

    Not sure what the issue is with figure legends, but you can achieve the same effect with an axes legend using something like:

    ax.legend(loc='center top', bbox_to_anchor=(0.5, 0.))
    

    (note center top in this case sets the vertical and horizontal justification; the actual location is in bbox_to_anchor.)

    Maybe that is an acceptable workaround.

    I am working on a way to make this automatically resize the parent axis but that'll be 2.2 or later (see #9082).

  2. astrojuanlu commented on Sep 5, 2017

    @astrojuanlu

    @jklymak thank you for your suggestion - however it is a limited workaround, since axes only support one legend whereas figures support more of them.

    Above all, we are interested in finding out whether this is a bug or a feature :)

  3. jklymak commented on Sep 5, 2017

    @jklymak
    Member

    @Juanlu001 @anhiga

    First, there is no difference between the axes and figure hadnling of the legend artists. ax.get_legend_handles_labels() is called before legend() to gather the needed artists from the axes. You could loop through all your axes as figure.legend does:

                for ax in self.axes:
                    ax_handles, ax_labels = ax.get_legend_handles_labels()
                    for h, l in zip(ax_handles, ax_labels):
                        if not in_handles(h, l):
                            handles.append(h)
                            labels.append(l)

    The solution to your mystery, however, is contained in legend_handler.py, where it was decided that text and lines must correspond one-to-one. So if you want the handle to the marker, you can do:

    for h in leg.get_lines():
        print(h._legmarker.get_marker())
    

    This is pretty hacky, and the undrscore means it could go away.

    Its a little strange to want the marker after the legend has been created. Why don't you just make the legend refer to something you've already drawn and keep track of what you've drawn? The legend doesn't seem like a good place to store the state of your plotting.

  4. anhiga commented on Sep 6, 2017

    @anhiga
    Author

    Thank you for your answers.
    @jklymak I understand now the way get_legend_handles_labels() works.

    Why don't you just make the legend refer to something you've already drawn and keep track of what you've drawn? The legend doesn't seem like a good place to store the state of your plotting.

    You are right, we shouldn't rely on the legend to store our plotting state.

    I'm closing this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions