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

Skip to content

Commit a515fc5

Browse files
authored
Merge pull request #25556 from anntzer/twincursor
Display cursor coordinates for all axes twinned with the current one.
2 parents d0faf22 + c5b2158 commit a515fc5

File tree

3 files changed

+51
-18
lines changed

3 files changed

+51
-18
lines changed

lib/matplotlib/axes/_base.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3976,10 +3976,20 @@ def format_ydata(self, y):
39763976

39773977
def format_coord(self, x, y):
39783978
"""Return a format string formatting the *x*, *y* coordinates."""
3979-
return "x={} y={}".format(
3980-
"???" if x is None else self.format_xdata(x),
3981-
"???" if y is None else self.format_ydata(y),
3982-
)
3979+
twins = self._twinned_axes.get_siblings(self)
3980+
if len(twins) == 1:
3981+
return "(x, y) = ({}, {})".format(
3982+
"???" if x is None else self.format_xdata(x),
3983+
"???" if y is None else self.format_ydata(y))
3984+
screen_xy = self.transData.transform((x, y))
3985+
xy_strs = []
3986+
# Retrieve twins in the order of self.figure.axes to sort tied zorders (which is
3987+
# the common case) by the order in which they are added to the figure.
3988+
for ax in sorted(twins, key=attrgetter("zorder")):
3989+
data_x, data_y = ax.transData.inverted().transform(screen_xy)
3990+
xy_strs.append(
3991+
"({}, {})".format(ax.format_xdata(data_x), ax.format_ydata(data_y)))
3992+
return "(x, y) = {}".format(" | ".join(xy_strs))
39833993

39843994
def minorticks_on(self):
39853995
"""

lib/matplotlib/cbook.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -849,19 +849,26 @@ class Grouper:
849849
def __init__(self, init=()):
850850
self._mapping = weakref.WeakKeyDictionary(
851851
{x: weakref.WeakSet([x]) for x in init})
852+
self._ordering = weakref.WeakKeyDictionary()
853+
for x in init:
854+
if x not in self._ordering:
855+
self._ordering[x] = len(self._ordering)
856+
self._next_order = len(self._ordering) # Plain int to simplify pickling.
852857

853858
def __getstate__(self):
854859
return {
855860
**vars(self),
856861
# Convert weak refs to strong ones.
857862
"_mapping": {k: set(v) for k, v in self._mapping.items()},
863+
"_ordering": {**self._ordering},
858864
}
859865

860866
def __setstate__(self, state):
861867
vars(self).update(state)
862868
# Convert strong refs to weak ones.
863869
self._mapping = weakref.WeakKeyDictionary(
864870
{k: weakref.WeakSet(v) for k, v in self._mapping.items()})
871+
self._ordering = weakref.WeakKeyDictionary(self._ordering)
865872

866873
def __contains__(self, item):
867874
return item in self._mapping
@@ -875,10 +882,19 @@ def join(self, a, *args):
875882
Join given arguments into the same set. Accepts one or more arguments.
876883
"""
877884
mapping = self._mapping
878-
set_a = mapping.setdefault(a, weakref.WeakSet([a]))
879-
885+
try:
886+
set_a = mapping[a]
887+
except KeyError:
888+
set_a = mapping[a] = weakref.WeakSet([a])
889+
self._ordering[a] = self._next_order
890+
self._next_order += 1
880891
for arg in args:
881-
set_b = mapping.get(arg, weakref.WeakSet([arg]))
892+
try:
893+
set_b = mapping[arg]
894+
except KeyError:
895+
set_b = mapping[arg] = weakref.WeakSet([arg])
896+
self._ordering[arg] = self._next_order
897+
self._next_order += 1
882898
if set_b is not set_a:
883899
if len(set_b) > len(set_a):
884900
set_a, set_b = set_b, set_a
@@ -892,9 +908,8 @@ def joined(self, a, b):
892908

893909
def remove(self, a):
894910
"""Remove *a* from the grouper, doing nothing if it is not there."""
895-
set_a = self._mapping.pop(a, None)
896-
if set_a:
897-
set_a.remove(a)
911+
self._mapping.pop(a, {a}).remove(a)
912+
self._ordering.pop(a, None)
898913

899914
def __iter__(self):
900915
"""
@@ -904,12 +919,12 @@ def __iter__(self):
904919
"""
905920
unique_groups = {id(group): group for group in self._mapping.values()}
906921
for group in unique_groups.values():
907-
yield [x for x in group]
922+
yield sorted(group, key=self._ordering.__getitem__)
908923

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

914929

915930
class GrouperView:

lib/matplotlib/tests/test_backend_bases.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import re
2-
31
from matplotlib import path, transforms
42
from matplotlib.backend_bases import (
53
FigureCanvasBase, KeyEvent, LocationEvent, MouseButton, MouseEvent,
@@ -123,11 +121,21 @@ def test_location_event_position(x, y):
123121
assert event.y == int(y)
124122
assert isinstance(event.y, int)
125123
if x is not None and y is not None:
126-
assert re.match(
127-
f"x={ax.format_xdata(x)} +y={ax.format_ydata(y)}",
128-
ax.format_coord(x, y))
124+
assert (ax.format_coord(x, y)
125+
== f"(x, y) = ({ax.format_xdata(x)}, {ax.format_ydata(y)})")
129126
ax.fmt_xdata = ax.fmt_ydata = lambda x: "foo"
130-
assert re.match("x=foo +y=foo", ax.format_coord(x, y))
127+
assert ax.format_coord(x, y) == "(x, y) = (foo, foo)"
128+
129+
130+
def test_location_event_position_twin():
131+
fig, ax = plt.subplots()
132+
ax.set(xlim=(0, 10), ylim=(0, 20))
133+
assert ax.format_coord(5., 5.) == "(x, y) = (5.00, 5.00)"
134+
ax.twinx().set(ylim=(0, 40))
135+
assert ax.format_coord(5., 5.) == "(x, y) = (5.00, 5.00) | (5.00, 10.0)"
136+
ax.twiny().set(xlim=(0, 5))
137+
assert (ax.format_coord(5., 5.)
138+
== "(x, y) = (5.00, 5.00) | (5.00, 10.0) | (2.50, 5.00)")
131139

132140

133141
def test_pick():

0 commit comments

Comments
 (0)