8000 Example for using cursor_data by timhoffm · Pull Request #12451 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

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

Closed
wants to merge 1 commit into from

Conversation

timhoffm
Copy link
Member
@timhoffm timhoffm commented Oct 8, 2018

PR Summary

Continues #12427, in particular #12427 (comment).

@timhoffm timhoffm force-pushed the doc-cursor-data-event branch from 1e68ed1 to 6da3762 Compare October 8, 2018 23:05
return ', '.join("Line('%s')" % line.get_label() for line in data)


Line2D.mouseover = True
Copy link
Contributor

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 :)

Copy link
Member Author

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.

Copy link
Contributor

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.

Copy link
Member Author

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)

Copy link
Contributor

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...

Copy link
Contributor

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)

@timhoffm
Copy link
Member Author
timhoffm commented Oct 9, 2018

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.

@anntzer
8794 Copy link
Contributor
anntzer commented Oct 9, 2018

I actually think the example is fine, but am not going to lose sleep over whether to document it.

@timhoffm timhoffm deleted the doc-cursor-data-event branch June 10, 2022 21:16
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.

2 participants
0