Skip to content

Annotation legend #8292

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 13 commits into from
Closed

Conversation

jlecoeur
Copy link

@jlecoeur jlecoeur commented Mar 14, 2017

Fixes #8236

I added new legend handlers for Text, FancyArrowPatch and Annotation artists (HandlerText, HandlerFancyArrowPatch and HandlerAnnotation).

HandlerAnnotation can draw different artists in the legend:

  • if the annotation has no text and no arrow, it draws a white Rectangle
  • if the annotation has only text, it uses HandlerText to draw a text. The text is replaced by 'Aa' if it is longer than one character. (This is configurable by setting rep_str and rep_maxlen.)
  • if the annotation has only an arrow, it uses HandlerFancyArrowPatch to draw an arrow
  • If the annotation has both text and arrow, it uses HandlerTuple to draw both.

In HandlerTuple, I added the possibility of different width ratios for the artists. HandlerAnnotation uses a width_ratios=[1,4] (ie. the arrow is 4 times wider than the text).

Example code:

import matplotlib
import matplotlib.pylab as plt
from matplotlib.patches import FancyArrowPatch
from matplotlib.legend_handler import HandlerAnnotation

fig, ax = plt.subplots(1)
ax.plot([0, 1], [0, 0], label='line1')
ax.plot([0, 1], [1, 1], label='line2')

# no text, no arrow
ax.annotate("",
            xy=(0.1,0.5),
            xytext=(0.1,0.5),
            label='annotation (empty)')

# text, no arrow
my_annotation = ax.annotate("X",
            xy=(0.1,0.5),
            xytext=(0.1,0.5),
            color='C2',
            label='annotation (text, no arrow)')

# no text, arrow
ax.annotate("",
            xy=(0.3,1.0),
            xytext=(0.3,0.0),
            arrowprops={'arrowstyle':'<->', 'color':'C7' },
            label='annotation (no text, arrow)')

# text, arrow
ax.annotate("Some long string",
            xy=(0.4,1.0),
            xytext=(0.35,0.1),
            arrowprops={'arrowstyle':'->', 'color':'C2' },
            color='C1',
            label='annotation (text + arrow)')

# Fancy arrow patch
arrpatch = FancyArrowPatch([0.5, 0.8], [0.9, 0.9],
                           arrowstyle='<|-',
                           mutation_scale=20,
                           color='C3',
                           label='arrowpatch')
ax.add_patch(arrpatch)

# Long text, will not be used in legend
ax.text(x=0.1, y=0.1,
        s='Hello',
        color='C5',
        label='text')

# Short text, copied in legend
ax.text(x=0.1, y=0.2,
        s='Z',
        color='C0',
        label='short text')

ax.legend(handler_map={my_annotation: HandlerAnnotation(rep_str='Abcde', rep_maxlen=0)})
fig.show()

output:
annotate


class HandlerAnnotation(HandlerBase):
"""
Handler for FancyArrowPatch instances.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FancyArrowPatch <- Annotation?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed!

@afvincent afvincent added this to the 2.1 (next point release) milestone Mar 14, 2017
@jlecoeur
Copy link
Author

I forgot to ping you @bendichter @tacaswell

I am not sure what should be displayed when an annotation has no text and no arrow. I used a white Rectangle but this still looks awkward, would it be better (and possible?) to remove the legend entry in this case?

@afvincent
Copy link
Contributor

I wonder how useful it would be to make keyword arguments for the default replacement string for long text, and the maximal length of text entries (nice and clever names would have to be found…). The use case I could see is a user that would want to deviate from the default values (e.g. from "a" for strings longer than 1 char to "Aa" for strings longer than 2 chars) without having to override the whole create_artist method of HandlerText. To do things similar to what is shown here, i.e. (with dummy names for the kwargs)

plt.legend(handler_map={my_annotation: HandlerAnnotation(rep_str='Aa', str_maxlen=2)})

@bendichter
Copy link

This looks beautiful! About the white rectangle, I can't think of a use case where you would have an annotation with no text and no arrow in the first place. I kind of liked having the white rectangle there because now I could imagine using it as a hack to give the legend subtitles.

@jlecoeur
Copy link
Author

@bendichter Ok! I made the rectangle transparent to make sure it is not visible even if something is below the legend

@afvincent Good point, I added these arguments and updated the example in the first comment. I also prefer 'Aa', which conveys the idea of 'some text' better than 'a' so I made it the default.

@tacaswell tacaswell modified the milestones: 2.1 (next point release), 2.2 (next next feature release) Aug 29, 2017
@tacaswell
Copy link
Member

@jlecoeur Any update on this?

@jlecoeur
Copy link
Author

@tacaswell I would say it is done. Details like the relative size of character and arrow, or which character to use to represent long strings can be discussed, but the main thing is done and functional

@tacaswell tacaswell closed this Sep 24, 2017
@tacaswell tacaswell reopened this Sep 24, 2017
@tacaswell
Copy link
Member

"power cycled" to restart CI against current master.

@jlecoeur
Copy link
Author

@tacaswell Many Ci fails 👎 I do not have time to fix them at the moment, can you have a shot at it? Otherwise I will come back to it, but that would not be before a month from now.

@tacaswell
Copy link
Member

Look forward to hearing from you in a month!

@magedhennawy
Copy link

I'll take care of this for you!

magedhennawy pushed a commit to magedhennawy/matplotlib that referenced this pull request Mar 5, 2018
@jklymak
Copy link
Member

jklymak commented May 17, 2018

Closing in lieu of the active, but not merged, #10688

@jklymak jklymak closed this May 17, 2018
@story645 story645 removed this from the future releases milestone Oct 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants