Skip to content

Commit 0787024

Browse files
authored
Merge branch 'matplotlib:main' into issue-22374
2 parents 6a87bff + af39f1e commit 0787024

File tree

7 files changed

+85
-89
lines changed

7 files changed

+85
-89
lines changed

.github/workflows/tests.yml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,13 @@ jobs:
240240
241241
cat mplsetup.cfg
242242
243-
# All dependencies must have been pre-installed, so that the minver
244-
# constraints are held.
245-
python -m pip install --no-deps -ve .
243+
if [[ "${{ matrix.name-suffix }}" == '(Minimum Versions)' ]]; then
244+
# Minimum versions run does not use build isolation so that it
245+
# builds against the pre-installed minver dependencies.
246+
python -m pip install --no-deps --no-build-isolation -ve .
247+
else
248+
python -m pip install --no-deps -ve .
249+
fi
246250
247251
if [[ "${{ runner.os }}" != 'macOS' ]]; then
248252
unset CPPFLAGS

doc/api/animation_api.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ this hopefully minimalist example gives a sense of how ``init_func``
9797
and ``func`` are used inside of `FuncAnimation` and the theory of how
9898
'blitting' works.
9999

100+
.. note::
101+
102+
The zorder of artists is not taken into account when 'blitting'
103+
because the 'blitted' artists are always drawn on top.
104+
100105
The expected signature on ``func`` and ``init_func`` is very simple to
101106
keep `FuncAnimation` out of your book keeping and plotting logic, but
102107
this means that the callable objects you pass in must know what
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
``contour.ClabelText`` and ``ContourLabeler.set_label_props``
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
... are deprecated.
4+
5+
Use ``Text(..., transform_rotates_text=True)`` as a replacement for
6+
``contour.ClabelText(...)`` and ``text.set(text=text, color=color,
7+
fontproperties=labeler.labelFontProps, clip_box=labeler.axes.bbox)`` as a
8+
replacement for the ``ContourLabeler.set_label_props(label, text, color)``.

lib/matplotlib/axes/_secondary_axes.py

Lines changed: 23 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import numbers
2+
13
import numpy as np
24

35
from matplotlib import _api, _docstring
@@ -17,7 +19,7 @@ def __init__(self, parent, orientation, location, functions, **kwargs):
1719
While there is no need for this to be private, it should really be
1820
called by those higher level functions.
1921
"""
20-
22+
_api.check_in_list(["x", "y"], orientation=orientation)
2123
self._functions = functions
2224
self._parent = parent
2325
self._orientation = orientation
@@ -28,7 +30,7 @@ def __init__(self, parent, orientation, location, functions, **kwargs):
2830
self._axis = self.xaxis
2931
self._locstrings = ['top', 'bottom']
3032
self._otherstrings = ['left', 'right']
31-
elif self._orientation == 'y':
33+
else: # 'y'
3234
super().__init__(self._parent.figure, [0, 1., 0.0001, 1], **kwargs)
3335
self._axis = self.yaxis
3436
self._locstrings = ['right', 'left']
@@ -40,11 +42,7 @@ def __init__(self, parent, orientation, location, functions, **kwargs):
4042
self.set_functions(functions)
4143

4244
# styling:
43-
if self._orientation == 'x':
44-
otheraxis = self.yaxis
45-
else:
46-
otheraxis = self.xaxis
47-
45+
otheraxis = self.yaxis if self._orientation == 'x' else self.xaxis
4846
otheraxis.set_major_locator(mticker.NullLocator())
4947
otheraxis.set_ticks_position('none')
5048

@@ -63,8 +61,8 @@ def set_alignment(self, align):
6361
6462
Parameters
6563
----------
66-
align : str
67-
either 'top' or 'bottom' for orientation='x' or
64+
align : {'top', 'bottom', 'left', 'right'}
65+
Either 'top' or 'bottom' for orientation='x' or
6866
'left' or 'right' for orientation='y' axis.
6967
"""
7068
_api.check_in_list(self._locstrings, align=align)
@@ -92,23 +90,22 @@ def set_location(self, location):
9290

9391
# This puts the rectangle into figure-relative coordinates.
9492
if isinstance(location, str):
95-
if location in ['top', 'right']:
96-
self._pos = 1.
97-
elif location in ['bottom', 'left']:
98-
self._pos = 0.
99-
else:
100-
raise ValueError(
101-
f"location must be {self._locstrings[0]!r}, "
102-
f"{self._locstrings[1]!r}, or a float, not {location!r}")
103-
else:
93+
_api.check_in_list(self._locstrings, location=location)
94+
self._pos = 1. if location in ('top', 'right') else 0.
95+
elif isinstance(location, numbers.Real):
10496
self._pos = location
97+
else:
98+
raise ValueError(
99+
f"location must be {self._locstrings[0]!r}, "
100+
f"{self._locstrings[1]!r}, or a float, not {location!r}")
101+
105102
self._loc = location
106103

107104
if self._orientation == 'x':
108105
# An x-secondary axes is like an inset axes from x = 0 to x = 1 and
109106
# from y = pos to y = pos + eps, in the parent's transAxes coords.
110107
bounds = [0, self._pos, 1., 1e-10]
111-
else:
108+
else: # 'y'
112109
bounds = [self._pos, 0, 1e-10, 1]
113110

114111
# this locator lets the axes move in the parent axes coordinates.
@@ -161,9 +158,7 @@ def set_functions(self, functions):
161158
'and the second being the inverse')
162159
self._set_scale()
163160

164-
# Should be changed to draw(self, renderer) once the deprecation of
165-
# renderer=None and of inframe expires.
166-
def draw(self, *args, **kwargs):
161+
def draw(self, renderer):
167162
"""
168163
Draw the secondary axes.
169164
@@ -175,7 +170,7 @@ def draw(self, *args, **kwargs):
175170
self._set_lims()
176171
# this sets the scale in case the parent has set its scale.
177172
self._set_scale()
178-
super().draw(*args, **kwargs)
173+
super().draw(renderer)
179174

180175
def _set_scale(self):
181176
"""
@@ -185,22 +180,18 @@ def _set_scale(self):
185180
if self._orientation == 'x':
186181
pscale = self._parent.xaxis.get_scale()
187182
set_scale = self.set_xscale
188-
if self._orientation == 'y':
183+
else: # 'y'
189184
pscale = self._parent.yaxis.get_scale()
190185
set_scale = self.set_yscale
191186
if pscale == self._parentscale:
192187
return
193188

194-
if pscale == 'log':
195-
defscale = 'functionlog'
196-
else:
197-
defscale = 'function'
198-
199189
if self._ticks_set:
200190
ticks = self._axis.get_ticklocs()
201191

202192
# need to invert the roles here for the ticks to line up.
203-
set_scale(defscale, functions=self._functions[::-1])
193+
set_scale('functionlog' if pscale == 'log' else 'function',
194+
functions=self._functions[::-1])
204195

205196
# OK, set_scale sets the locators, but if we've called
206197
# axsecond.set_ticks, we want to keep those.
@@ -218,7 +209,7 @@ def _set_lims(self):
218209
if self._orientation == 'x':
219210
lims = self._parent.get_xlim()
220211
set_lim = self.set_xlim
221-
if self._orientation == 'y':
212+
else: # 'y'
222213
lims = self._parent.get_ylim()
223214
set_lim = self.set_ylim
224215
order = lims[0] < lims[1]
@@ -249,7 +240,7 @@ def set_color(self, color):
249240
self.spines.bottom.set_color(color)
250241
self.spines.top.set_color(color)
251242
self.xaxis.label.set_color(color)
252-
else:
243+
else: # 'y'
253244
self.tick_params(axis='y', colors=color)
254245
self.spines.left.set_color(color)
255246
self.spines.right.set_color(color)

lib/matplotlib/contour.py

Lines changed: 23 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
# per level.
3232

3333

34+
@_api.deprecated("3.7", alternative="Text.set_transform_rotates_text")
3435
class ClabelText(Text):
3536
"""
3637
Unlike the ordinary text, the get_rotation returns an updated
@@ -150,10 +151,8 @@ def clabel(self, levels=None, *,
150151
or minus 90 degrees from level.
151152
152153
use_clabeltext : bool, default: False
153-
If ``True``, `.ClabelText` class (instead of `.Text`) is used to
154-
create labels. `ClabelText` recalculates rotation angles
155-
of texts during the drawing time, therefore this can be used if
156-
aspect of the axes changes.
154+
If ``True``, use `.Text.set_transform_rotates_text` to ensure that
155+
label rotation is updated whenever the axes aspect changes.
157156
158157
zorder : float or None, default: ``(2 + contour.get_zorder())``
159158
zorder of the contour labels.
@@ -272,6 +271,7 @@ def get_label_width(self, lev, fmt, fsize):
272271
width *= 72 / fig.dpi
273272
return width
274273

274+
@_api.deprecated("3.7", alternative="Artist.set")
275275
def set_label_props(self, label, text, color):
276276
"""Set the label properties - color, fontsize, text."""
277277
label.set_text(text)
@@ -416,56 +416,32 @@ def calc_label_rot_and_inline(self, slc, ind, lw, lc=None, spacing=5):
416416

417417
return rotation, nlc
418418

419-
def _get_label_text(self, x, y, rotation):
420-
dx, dy = self.axes.transData.inverted().transform((x, y))
421-
return Text(dx, dy, rotation=rotation,
422-
horizontalalignment='center',
423-
verticalalignment='center', zorder=self._clabel_zorder)
424-
425-
def _get_label_clabeltext(self, x, y, rotation):
426-
# x, y, rotation is given in pixel coordinate. Convert them to
427-
# the data coordinate and create a label using ClabelText
428-
# class. This way, the rotation of the clabel is along the
429-
# contour line always.
430-
transDataInv = self.axes.transData.inverted()
431-
dx, dy = transDataInv.transform((x, y))
432-
drotation = transDataInv.transform_angles(np.array([rotation]),
433-
np.array([[x, y]]))
434-
t = ClabelText(dx, dy, rotation=drotation[0],
435-
horizontalalignment='center',
436-
verticalalignment='center', zorder=self._clabel_zorder)
437-
438-
return t
439-
440-
def _add_label(self, t, x, y, lev, cvalue):
441-
color = self.labelMappable.to_rgba(cvalue, alpha=self.alpha)
442-
443-
_text = self.get_text(lev, self.labelFmt)
444-
self.set_label_props(t, _text, color)
419+
def add_label(self, x, y, rotation, lev, cvalue):
420+
"""Add contour label without `.Text.set_transform_rotates_text`."""
421+
data_x, data_y = self.axes.transData.inverted().transform((x, y))
422+
t = Text(
423+
data_x, data_y,
424+
text=self.get_text(lev, self.labelFmt),
425+
rotation=rotation,
426+
horizontalalignment='center', verticalalignment='center',
427+
zorder=self._clabel_zorder,
428+
color=self.labelMappable.to_rgba(cvalue, alpha=self.alpha),
429+
fontproperties=self.labelFontProps,
430+
clip_box=self.axes.bbox)
445431
self.labelTexts.append(t)
446432
self.labelCValues.append(cvalue)
447433
self.labelXYs.append((x, y))
448-
449434
# Add label to plot here - useful for manual mode label selection
450435
self.axes.add_artist(t)
451436

452-
def add_label(self, x, y, rotation, lev, cvalue):
453-
"""
454-
Add contour label using :class:`~matplotlib.text.Text` class.
455-
"""
456-
t = self._get_label_text(x, y, rotation)
457-
self._add_label(t, x, y, lev, cvalue)
458-
459437
def add_label_clabeltext(self, x, y, rotation, lev, cvalue):
460-
"""
461-
Add contour label using :class:`ClabelText` class.
462-
"""
463-
# x, y, rotation is given in pixel coordinate. Convert them to
464-
# the data coordinate and create a label using ClabelText
465-
# class. This way, the rotation of the clabel is along the
466-
# contour line always.
467-
t = self._get_label_clabeltext(x, y, rotation)
468-
self._add_label(t, x, y, lev, cvalue)
438+
"""Add contour label with `.Text.set_transform_rotates_text`."""
439+
self.add_label(x, y, rotation, lev, cvalue)
440+
# Grab the last added text, and reconfigure its rotation.
441+
t = self.labelTexts[-1]
442+
data_rotation, = self.axes.transData.inverted().transform_angles(
443+
[rotation], [[x, y]])
444+
t.set(rotation=data_rotation, transform_rotates_text=True)
469445

470446
def add_label_near(self, x, y, inline=True, inline_spacing=5,
471447
transform=None):

pyproject.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[build-system]
2+
build-backend = "setuptools.build_meta"
3+
requires = [
4+
"certifi>=2020.06.20",
5+
"numpy>=1.19",
6+
"setuptools_scm>=7",
7+
]

setup.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434
import setuptools.command.build_py
3535
import setuptools.command.sdist
3636

37+
# sys.path modified to find setupext.py during pyproject.toml builds.
38+
sys.path.append(str(Path(__file__).resolve().parent))
39+
3740
import setupext
3841
from setupext import print_raw, print_status
3942

@@ -68,6 +71,12 @@ def has_flag(self, flagname):
6871

6972
class BuildExtraLibraries(setuptools.command.build_ext.build_ext):
7073
def finalize_options(self):
74+
# If coverage is enabled then need to keep the .o and .gcno files in a
75+
# non-temporary directory otherwise coverage info not collected.
76+
cppflags = os.getenv('CPPFLAGS')
77+
if cppflags and '--coverage' in cppflags:
78+
self.build_temp = 'build'
79+
7180
self.distribution.ext_modules[:] = [
7281
ext
7382
for package in good_packages
@@ -208,8 +217,9 @@ def update_matplotlibrc(path):
208217
class BuildPy(setuptools.command.build_py.build_py):
209218
def run(self):
210219
super().run()
211-
update_matplotlibrc(
212-
Path(self.build_lib, "matplotlib/mpl-data/matplotlibrc"))
220+
if not self.editable_mode:
221+
update_matplotlibrc(
222+
Path(self.build_lib, "matplotlib/mpl-data/matplotlibrc"))
213223

214224

215225
class Sdist(setuptools.command.sdist.sdist):
@@ -300,11 +310,6 @@ def make_release_tree(self, base_dir, files):
300310
package_data=package_data,
301311

302312
python_requires='>={}'.format('.'.join(str(n) for n in py_min_version)),
303-
setup_requires=[
304-
"certifi>=2020.06.20",
305-
"numpy>=1.19",
306-
"setuptools_scm>=7",
307-
],
308313
install_requires=[
309314
"contourpy>=1.0.1",
310315
"cycler>=0.10",

0 commit comments

Comments
 (0)