Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Display cursor coordinates for all axes twinned with the current one. #25556

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

Merged
merged 2 commits into from
Apr 3, 2024
Merged
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
18 changes: 14 additions & 4 deletions lib/matplotlib/axes/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3976,10 +3976,20 @@ def format_ydata(self, y):

def format_coord(self, x, y):
"""Return a format string formatting the *x*, *y* coordinates."""
return "x={} y={}".format(
"???" if x is None else self.format_xdata(x),
"???" if y is None else self.format_ydata(y),
)
twins = self._twinned_axes.get_siblings(self)
if len(twins) == 1:
return "(x, y) = ({}, {})".format(
"???" if x is None else self.format_xdata(x),
"???" if y is None else self.format_ydata(y))
screen_xy = self.transData.transform((x, y))
xy_strs = []
# Retrieve twins in the order of self.figure.axes to sort tied zorders (which is
# the common case) by the order in which they are added to the figure.
for ax in sorted(twins, key=attrgetter("zorder")):
data_x, data_y = ax.transData.inverted().transform(screen_xy)
xy_strs.append(
"({}, {})".format(ax.format_xdata(data_x), ax.format_ydata(data_y)))
return "(x, y) = {}".format(" | ".join(xy_strs))

def minorticks_on(self):
"""
Expand Down
31 changes: 23 additions & 8 deletions lib/matplotlib/cbook.py
Original file line number Diff line number Diff line change
Expand Up @@ -849,19 +849,26 @@ class Grouper:
def __init__(self, init=()):
self._mapping = weakref.WeakKeyDictionary(
{x: weakref.WeakSet([x]) for x in init})
self._ordering = weakref.WeakKeyDictionary()
for x in init:
if x not in self._ordering:
self._ordering[x] = len(self._ordering)
self._next_order = len(self._ordering) # Plain int to simplify pickling.

def __getstate__(self):
return {
**vars(self),
# Convert weak refs to strong ones.
"_mapping": {k: set(v) for k, v in self._mapping.items()},
"_ordering": {**self._ordering},
}

def __setstate__(self, state):
vars(self).update(state)
# Convert strong refs to weak ones.
self._mapping = weakref.WeakKeyDictionary(
{k: weakref.WeakSet(v) for k, v in self._mapping.items()})
self._ordering = weakref.WeakKeyDictionary(self._ordering)

def __contains__(self, item):
return item in self._mapping
Expand All @@ -875,10 +882,19 @@ def join(self, a, *args):
Join given arguments into the same set. Accepts one or more arguments.
"""
mapping = self._mapping
set_a = mapping.setdefault(a, weakref.WeakSet([a]))

try:
set_a = mapping[a]
except KeyError:
set_a = mapping[a] = weakref.WeakSet([a])
self._ordering[a] = self._next_order
self._next_order += 1
for arg in args:
set_b = mapping.get(arg, weakref.WeakSet([arg]))
try:
set_b = mapping[arg]
except KeyError:
set_b = mapping[arg] = weakref.WeakSet([arg])
self._ordering[arg] = self._next_order
self._next_order += 1
if set_b is not set_a:
if len(set_b) > len(set_a):
set_a, set_b = set_b, set_a
Expand All @@ -892,9 +908,8 @@ def joined(self, a, b):

def remove(self, a):
"""Remove *a* from the grouper, doing nothing if it is not there."""
set_a = self._mapping.pop(a, None)
if set_a:
set_a.remove(a)
self._mapping.pop(a, {a}).remove(a)
self._ordering.pop(a, None)

def __iter__(self):
"""
Expand All @@ -904,12 +919,12 @@ def __iter__(self):
"""
unique_groups = {id(group): group for group in self._mapping.values()}
for group in unique_groups.values():
yield [x for x in group]
yield sorted(group, key=self._ordering.__getitem__)

def get_siblings(self, a):
"""Return all of the items joined with *a*, including itself."""
siblings = self._mapping.get(a, [a])
return [x for x in siblings]
return sorted(siblings, key=self._ordering.get)


class GrouperView:
Expand Down
20 changes: 14 additions & 6 deletions lib/matplotlib/tests/test_backend_bases.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import re

from matplotlib import path, transforms
from matplotlib.backend_bases import (
FigureCanvasBase, KeyEvent, LocationEvent, MouseButton, MouseEvent,
Expand Down Expand Up @@ -123,11 +121,21 @@ def test_location_event_position(x, y):
assert event.y == int(y)
assert isinstance(event.y, int)
if x is not None and y is not None:
assert re.match(
f"x={ax.format_xdata(x)} +y={ax.format_ydata(y)}",
ax.format_coord(x, y))
assert (ax.format_coord(x, y)
== f"(x, y) = ({ax.format_xdata(x)}, {ax.format_ydata(y)})")
ax.fmt_xdata = ax.fmt_ydata = lambda x: "foo"
assert re.match("x=foo +y=foo", ax.format_coord(x, y))
assert ax.format_coord(x, y) == "(x, y) = (foo, foo)"


def test_location_event_position_twin():
fig, ax = plt.subplots()
ax.set(xlim=(0, 10), ylim=(0, 20))
assert ax.format_coord(5., 5.) == "(x, y) = (5.00, 5.00)"
ax.twinx().set(ylim=(0, 40))
assert ax.format_coord(5., 5.) == "(x, y) = (5.00, 5.00) | (5.00, 10.0)"
ax.twiny().set(xlim=(0, 5))
assert (ax.format_coord(5., 5.)
== "(x, y) = (5.00, 5.00) | (5.00, 10.0) | (2.50, 5.00)")


def test_pick():
Expand Down