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
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ per-file-ignores =
examples/color/colormap_reference.py: E402
examples/color/custom_cmap.py: E402
examples/color/named_colors.py: E402
examples/event_handling/cursor_data.py: E402
examples/event_handling/data_browser.py: E501
examples/event_handling/path_editor.py: E501
examples/event_handling/pick_event_demo.py: E501
Expand Down
Binary file added doc/_static/cursor_data.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
55 changes: 55 additions & 0 deletions examples/event_handling/cursor_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"""
===========
Cursor Data
===========

This example demonstrates how to monkeypatch `.Artist.get_cursor_data` and
`.Artist.format_cursor_data` to show the elements under the cursor in the
status bar of the plot window.

.. image:: ../../_static/cursor_data.png

"""

import matplotlib.pyplot as plt
from matplotlib.lines import Line2D


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)


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)

Line2D.get_cursor_data = lines_under_cursor
Line2D.format_cursor_data = format_lines

fig, ax = plt.subplots()
ax.plot([1, 3, 2], label="Label 1", lw=3)
ax.plot([2, 1, 3], label="Label 2", lw=3)

plt.show()


#############################################################################
#
# ------------
#
# References
# """"""""""
#
# The use of the following functions, methods, classes and modules is shown
# in this example:

import matplotlib
matplotlib.artist.Artist.get_cursor_data
matplotlib.artist.Artist.format_cursor_data
4 changes: 4 additions & 0 deletions lib/matplotlib/artist.py
Original file line number Diff line number Diff line change
Expand Up @@ -1132,6 +1132,10 @@ def get_cursor_data(self, event):
The only current use case is displaying the z-value of an `.AxesImage`
in the status bar of a plot window, while moving the mouse.

Parameters
----------
event : `matplotlib.backend_bases.MouseEvent`

See Also
--------
format_cursor_data
Expand Down
0