-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Example for using cursor_data #12451
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
Conversation
1e68ed1
to
6da3762
Compare
return ', '.join("Line('%s')" % line.get_label() for line in data) | ||
|
||
|
||
Line2D.mouseover = True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather you monkeypatch just the line instances that get returned by ax.plot
; monkeypatching the classes themselves is a bit too global for me :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would add the trouble of binding a method to an existing object. It makes it even more complicated to understand what's going on:
import matplotlib.pyplot as plt
import types
def patch_line(line):
"""Monkeypatch a line to show it's label on mouse-over."""
def lines_under_cursor(self, event):
"""
Return a list of lines under the cursor for the given MouseMove event.
"""
if not event.inaxes:
return []
return [line for line in event.inaxes.lines if line.contains(event)[0]]
def format_lines(self, data):
"""Convert a list of lines to a comma-separted string."""
return ', '.join("Line('%s')" % line.get_label() for line in data)
line.mouseover = True
line.get_cursor_data = types.MethodType(lines_under_cursor, line)
line.format_cursor_data = types.MethodType(format_lines, line)
fig, ax = plt.subplots()
l1, = ax.plot([1, 3, 2], label="Label 1", lw=3)
l2, = ax.plot([2, 1, 3], label="Label 2", lw=3)
patch_line(l1)
patch_line(l2)
plt.show()
Of course, you could bind a pure function in this case because self
is not used, but that would lose generality.
Still not convinced that this monkey-patching is actually recommendable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, I would personally just bind free functions that don't take self as first argument. Doing it as suggested in your example is definitely overkill (as a side note, you can do line.get_cursor_data = get_cursor_data.__get__(type(line), line)
which is a bit simpler IMO for the cases where you need it, or even just bind a partial() object... (there isn't much to gain by exactly binding a bound method)).
I'm not going to lose sleep over this specific example either way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be more appropriate to subclass, such as:
class LabelShowingLine(Line2D):
def get_cursor_data(self, event):
...
def format_cursor_data(self, data):
...
ax.plot([1, 3, 2], label="Label 1", lw=3, cls=LabelShowingLine)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
l, = ax.plot(...)
l.__class__ = LabelShowingLine
hehe...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW this is how I would do it:
for line in ax.lines:
line.lines_under_cursor = lambda event: (
# Return a list of lines under the cursor for the given MouseEvent.
[line for line in event.inaxes.lines if line.contains(event)[0]]
if event.inaxes else [])
line.format_lines = lambda data: (
# Convert a list of lines into a comma-separated string.
", ".join("Line({!r})".format(line.get_label())) for line in data)
I've come to the conclusion that monkeypatching is not a reasonable way to officially extend a library. I don't want to advertise it. After all, the gained feature is quite specialized and not something we need to provide as a core extensibility of matplotlib. #12463 is a stripped down version of this to just document the parameter type. Please feel free to reopen if there is a general consensus that we want people to monkeypatch matplotlib. |
I actually think the example is fine, but am not going to lose sleep over whether to document it. |
PR Summary
Continues #12427, in particular #12427 (comment).