diff --git a/.github/workflows/cibuildwheel.yml b/.github/workflows/cibuildwheel.yml
index 41d28d864dbe..a4c0c0781813 100644
--- a/.github/workflows/cibuildwheel.yml
+++ b/.github/workflows/cibuildwheel.yml
@@ -105,6 +105,7 @@ jobs:
CIBW_SKIP: "*-musllinux_aarch64"
CIBW_TEST_COMMAND: >-
python {package}/ci/check_version_number.py
+ MACOSX_DEPLOYMENT_TARGET: "10.12"
MPL_DISABLE_FH4: "yes"
strategy:
matrix:
@@ -115,16 +116,10 @@ jobs:
cibw_archs: "aarch64"
- os: windows-latest
cibw_archs: "auto64"
- - os: macos-11
+ - os: macos-12
cibw_archs: "x86_64"
- # NOTE: macos_target can be moved back into global environment after
- # meson-python 0.16.0 is released.
- macos_target: "10.12"
- os: macos-14
cibw_archs: "arm64"
- # NOTE: macos_target can be moved back into global environment after
- # meson-python 0.16.0 is released.
- macos_target: "11.0"
steps:
- name: Set up QEMU
@@ -140,49 +135,44 @@ jobs:
path: dist/
- name: Build wheels for CPython 3.12
- uses: pypa/cibuildwheel@8d945475ac4b1aac4ae08b2fd27db9917158b6ce # v2.17.0
+ uses: pypa/cibuildwheel@932529cab190fafca8c735a551657247fa8f8eaf # v2.19.1
with:
package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }}
env:
CIBW_BUILD: "cp312-*"
CIBW_ARCHS: ${{ matrix.cibw_archs }}
- MACOSX_DEPLOYMENT_TARGET: "${{ matrix.macos_target }}"
- name: Build wheels for CPython 3.11
- uses: pypa/cibuildwheel@8d945475ac4b1aac4ae08b2fd27db9917158b6ce # v2.17.0
+ uses: pypa/cibuildwheel@932529cab190fafca8c735a551657247fa8f8eaf # v2.19.1
with:
package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }}
env:
CIBW_BUILD: "cp311-*"
CIBW_ARCHS: ${{ matrix.cibw_archs }}
- MACOSX_DEPLOYMENT_TARGET: "${{ matrix.macos_target }}"
- name: Build wheels for CPython 3.10
- uses: pypa/cibuildwheel@8d945475ac4b1aac4ae08b2fd27db9917158b6ce # v2.17.0
+ uses: pypa/cibuildwheel@932529cab190fafca8c735a551657247fa8f8eaf # v2.19.1
with:
package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }}
env:
CIBW_BUILD: "cp310-*"
CIBW_ARCHS: ${{ matrix.cibw_archs }}
- MACOSX_DEPLOYMENT_TARGET: "${{ matrix.macos_target }}"
- name: Build wheels for CPython 3.9
- uses: pypa/cibuildwheel@8d945475ac4b1aac4ae08b2fd27db9917158b6ce # v2.17.0
+ uses: pypa/cibuildwheel@932529cab190fafca8c735a551657247fa8f8eaf # v2.19.1
with:
package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }}
env:
CIBW_BUILD: "cp39-*"
CIBW_ARCHS: ${{ matrix.cibw_archs }}
- MACOSX_DEPLOYMENT_TARGET: "${{ matrix.macos_target }}"
- name: Build wheels for PyPy
- uses: pypa/cibuildwheel@8d945475ac4b1aac4ae08b2fd27db9917158b6ce # v2.17.0
+ uses: pypa/cibuildwheel@932529cab190fafca8c735a551657247fa8f8eaf # v2.19.1
with:
package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }}
env:
CIBW_BUILD: "pp39-*"
CIBW_ARCHS: ${{ matrix.cibw_archs }}
- MACOSX_DEPLOYMENT_TARGET: "${{ matrix.macos_target }}"
if: matrix.cibw_archs != 'aarch64'
- uses: actions/upload-artifact@v4
@@ -199,6 +189,8 @@ jobs:
environment: release
permissions:
id-token: write
+ attestations: write
+ contents: read
steps:
- name: Download packages
uses: actions/download-artifact@v4
@@ -210,5 +202,10 @@ jobs:
- name: Print out packages
run: ls dist
+ - name: Generate artifact attestation for sdist and wheel
+ uses: actions/attest-build-provenance@bdd51370e0416ac948727f861e03c2f05d32d78e # v1.3.2
+ with:
+ subject-path: dist/matplotlib-*
+
- name: Publish package distributions to PyPI
- uses: pypa/gh-action-pypi-publish@81e9d935c883d0b210363ab89cf05f3894778450 # v1.8.14
+ uses: pypa/gh-action-pypi-publish@ec4db0b4ddc65acdf4bff5fa45ac92d78b56bdf0 # v1.9.0
diff --git a/.github/workflows/circleci.yml b/.github/workflows/circleci.yml
index 8f9e3190c5e2..3aead720cf20 100644
--- a/.github/workflows/circleci.yml
+++ b/.github/workflows/circleci.yml
@@ -10,7 +10,8 @@ jobs:
name: Run CircleCI artifacts redirector
steps:
- name: GitHub Action step
- uses: larsoner/circleci-artifacts-redirector-action@master
+ uses:
+ scientific-python/circleci-artifacts-redirector-action@4e13a10d89177f4bfc8007a7064bdbeda848d8d1 # v1.0.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
api-token: ${{ secrets.CIRCLECI_TOKEN }}
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 29d2859999bd..203b0eee9ca4 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -32,7 +32,6 @@ jobs:
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
- setup-python-dependencies: false
- name: Build compiled code
if: matrix.language == 'c-cpp'
diff --git a/.github/workflows/conflictcheck.yml b/.github/workflows/conflictcheck.yml
index 3eb384fa6585..3110839e5150 100644
--- a/.github/workflows/conflictcheck.yml
+++ b/.github/workflows/conflictcheck.yml
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check if PRs have merge conflicts
- uses: eps1lon/actions-label-merge-conflict@e62d7a53ff8be8b97684bffb6cfbbf3fc1115e2e # v3.0.0
+ uses: eps1lon/actions-label-merge-conflict@1b1b1fcde06a9b3d089f3464c96417961dde1168 # v3.0.2
with:
dirtyLabel: "status: needs rebase"
repoToken: "${{ secrets.GITHUB_TOKEN }}"
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 126693beafa7..daa07e62b2e5 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -160,10 +160,9 @@ jobs:
fi
;;
macOS)
- brew install ccache
- brew tap homebrew/cask-fonts
- brew install font-noto-sans-cjk ghostscript gobject-introspection gtk4 ninja
- brew install --cask inkscape
+ brew update
+ brew install ccache ghostscript gobject-introspection gtk4 ninja
+ brew install --cask font-noto-sans-cjk inkscape
;;
esac
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 2dc1ca5352c0..14817e95929f 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -79,7 +79,7 @@ repos:
- id: yamllint
args: ["--strict", "--config-file=.yamllint.yml"]
- repo: https://github.com/python-jsonschema/check-jsonschema
- rev: 0.28.1
+ rev: 0.28.4
hooks:
# TODO: Re-enable this when https://github.com/microsoft/azure-pipelines-vscode/issues/567 is fixed.
# - id: check-azure-pipelines
diff --git a/.yamllint.yml b/.yamllint.yml
index 3b30533ececa..2be81b28c7fb 100644
--- a/.yamllint.yml
+++ b/.yamllint.yml
@@ -3,7 +3,7 @@ extends: default
rules:
line-length:
- max: 111
+ max: 120
allow-non-breakable-words: true
truthy:
check-keys: false
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index bf055d0eaa16..4c50c543846a 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -119,10 +119,10 @@ stages:
texlive-xetex
;;
Darwin)
+ brew update
brew install --cask xquartz
brew install ccache ffmpeg imagemagick mplayer ninja pkg-config
- brew tap homebrew/cask-fonts
- brew install font-noto-sans-cjk-sc
+ brew install --cask font-noto-sans-cjk-sc
;;
Windows_NT)
choco install ninja
diff --git a/doc/_static/switcher.json b/doc/_static/switcher.json
index 6996d79bc22a..3d712e4ff8e9 100644
--- a/doc/_static/switcher.json
+++ b/doc/_static/switcher.json
@@ -2,7 +2,8 @@
{
"name": "3.9 (stable)",
"version": "stable",
- "url": "https://matplotlib.org/stable/"
+ "url": "https://matplotlib.org/stable/",
+ "preferred": true
},
{
"name": "3.10 (dev)",
diff --git a/doc/_static/zenodo_cache/11201097.svg b/doc/_static/zenodo_cache/11201097.svg
new file mode 100644
index 000000000000..70f35a7a659f
--- /dev/null
+++ b/doc/_static/zenodo_cache/11201097.svg
@@ -0,0 +1,35 @@
+
\ No newline at end of file
diff --git a/doc/api/index.rst b/doc/api/index.rst
index e55a0ed3c5b2..70c3b5343e7a 100644
--- a/doc/api/index.rst
+++ b/doc/api/index.rst
@@ -126,6 +126,7 @@ Alphabetical list of modules:
sphinxext_mathmpl_api.rst
sphinxext_plot_directive_api.rst
sphinxext_figmpl_directive_api.rst
+ sphinxext_roles.rst
spines_api.rst
style_api.rst
table_api.rst
diff --git a/doc/api/prev_api_changes/api_changes_3.5.0/development.rst b/doc/api/prev_api_changes/api_changes_3.5.0/development.rst
index 2db21237a699..b42e6eff3423 100644
--- a/doc/api/prev_api_changes/api_changes_3.5.0/development.rst
+++ b/doc/api/prev_api_changes/api_changes_3.5.0/development.rst
@@ -77,6 +77,6 @@ In order to avoid conflicting with the use of :file:`setup.cfg` by
``setup.cfg`` to ``mplsetup.cfg``. The :file:`setup.cfg.template` has been
correspondingly been renamed to :file:`mplsetup.cfg.template`.
-Note that the path to this configuration file can still be set via the
-:envvar:`MPLSETUPCFG` environment variable, which allows one to keep using the
-same file before and after this change.
+Note that the path to this configuration file can still be set via the ``MPLSETUPCFG``
+environment variable, which allows one to keep using the same file before and after this
+change.
diff --git a/doc/api/prev_api_changes/api_changes_3.9.1.rst b/doc/api/prev_api_changes/api_changes_3.9.1.rst
new file mode 100644
index 000000000000..4a9a1fc6669c
--- /dev/null
+++ b/doc/api/prev_api_changes/api_changes_3.9.1.rst
@@ -0,0 +1,13 @@
+API Changes for 3.9.1
+=====================
+
+Development
+-----------
+
+Documentation-specific custom Sphinx roles are now semi-public
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+For third-party packages that derive types from Matplotlib, our use of custom roles may
+prevent Sphinx from building their docs. These custom Sphinx roles are now public solely
+for the purposes of use within projects that derive from Matplotlib types. See
+:mod:`matplotlib.sphinxext.roles` for details.
diff --git a/doc/api/sphinxext_roles.rst b/doc/api/sphinxext_roles.rst
new file mode 100644
index 000000000000..99959ff05d14
--- /dev/null
+++ b/doc/api/sphinxext_roles.rst
@@ -0,0 +1,7 @@
+==============================
+``matplotlib.sphinxext.roles``
+==============================
+
+.. automodule:: matplotlib.sphinxext.roles
+ :no-undoc-members:
+ :private-members: _rcparam_role, _mpltype_role
diff --git a/doc/conf.py b/doc/conf.py
index c9a475aecf9c..f43806a8b4c0 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -116,9 +116,9 @@ def _parse_skip_subdirs_file():
'sphinx_gallery.gen_gallery',
'matplotlib.sphinxext.mathmpl',
'matplotlib.sphinxext.plot_directive',
+ 'matplotlib.sphinxext.roles',
'matplotlib.sphinxext.figmpl_directive',
'sphinxcontrib.inkscapeconverter',
- 'sphinxext.custom_roles',
'sphinxext.github',
'sphinxext.math_symbol_table',
'sphinxext.missing_references',
@@ -508,6 +508,7 @@ def js_tag_with_cache_busting(js):
# this special value indicates the use of the unreleased banner. If we need
# an actual announcement, then just place the text here as usual.
"announcement": "unreleased" if not is_release_build else "",
+ "show_version_warning_banner": True,
}
include_analytics = is_release_build
if include_analytics:
diff --git a/doc/install/environment_variables_faq.rst b/doc/install/environment_variables_faq.rst
index ba384343cc5a..38e0d0ef0c63 100644
--- a/doc/install/environment_variables_faq.rst
+++ b/doc/install/environment_variables_faq.rst
@@ -29,13 +29,6 @@ Environment variables
used to find a base directory in which the :file:`matplotlib` subdirectory is
created.
-.. envvar:: MPLSETUPCFG
-
- This optional variable can be set to the full path of a :file:`mplsetup.cfg`
- configuration file used to customize the Matplotlib build. By default, a
- :file:`mplsetup.cfg` file in the root of the Matplotlib source tree will be
- read. Supported build options are listed in :file:`mplsetup.cfg.template`.
-
.. envvar:: PATH
The list of directories searched to find executable programs.
diff --git a/doc/install/index.rst b/doc/install/index.rst
index ea8e29d71565..867e4600a77e 100644
--- a/doc/install/index.rst
+++ b/doc/install/index.rst
@@ -121,22 +121,20 @@ Before trying to install Matplotlib, please install the :ref:`dependencies`.
To build from a tarball, download the latest *tar.gz* release
file from `the PyPI files page `_.
-We provide a `mplsetup.cfg`_ file which you can use to customize the build
-process. For example, which default backend to use, whether some of the
-optional libraries that Matplotlib ships with are installed, and so on. This
-file will be particularly useful to those packaging Matplotlib.
-
-.. _mplsetup.cfg: https://raw.githubusercontent.com/matplotlib/matplotlib/main/mplsetup.cfg.template
-
If you are building your own Matplotlib wheels (or sdists) on Windows, note
that any DLLs that you copy into the source tree will be packaged too.
-
Configure build and behavior defaults
=====================================
-Aspects of the build and install process and some behaviorial defaults of the
-library can be configured via:
+We provide a `meson.options`_ file containing options with which you can use to
+customize the build process. For example, which default backend to use, whether some of
+the optional libraries that Matplotlib ships with are installed, and so on. These
+options will be particularly useful to those packaging Matplotlib.
+
+.. _meson.options: https://github.com/matplotlib/matplotlib/blob/main/meson.options
+
+Aspects of some behaviorial defaults of the library can be configured via:
.. toctree::
:maxdepth: 2
diff --git a/doc/project/citing.rst b/doc/project/citing.rst
index 008ee794a41b..fd013231b6c5 100644
--- a/doc/project/citing.rst
+++ b/doc/project/citing.rst
@@ -32,6 +32,9 @@ By version
.. START OF AUTOGENERATED
+v3.9.0
+ .. image:: ../_static/zenodo_cache/11201097.svg
+ :target: https://doi.org/10.5281/zenodo.11201097
v3.8.4
.. image:: ../_static/zenodo_cache/10916799.svg
:target: https://doi.org/10.5281/zenodo.10916799
diff --git a/doc/sphinxext/custom_roles.py b/doc/sphinxext/custom_roles.py
deleted file mode 100644
index d76c92709865..000000000000
--- a/doc/sphinxext/custom_roles.py
+++ /dev/null
@@ -1,89 +0,0 @@
-from urllib.parse import urlsplit, urlunsplit
-
-from docutils import nodes
-
-from matplotlib import rcParamsDefault
-
-
-class QueryReference(nodes.Inline, nodes.TextElement):
- """
- Wraps a reference or pending reference to add a query string.
-
- The query string is generated from the attributes added to this node.
-
- Also equivalent to a `~docutils.nodes.literal` node.
- """
-
- def to_query_string(self):
- """Generate query string from node attributes."""
- return '&'.join(f'{name}={value}' for name, value in self.attlist())
-
-
-def visit_query_reference_node(self, node):
- """
- Resolve *node* into query strings on its ``reference`` children.
-
- Then act as if this is a `~docutils.nodes.literal`.
- """
- query = node.to_query_string()
- for refnode in node.findall(nodes.reference):
- uri = urlsplit(refnode['refuri'])._replace(query=query)
- refnode['refuri'] = urlunsplit(uri)
-
- self.visit_literal(node)
-
-
-def depart_query_reference_node(self, node):
- """
- Act as if this is a `~docutils.nodes.literal`.
- """
- self.depart_literal(node)
-
-
-def rcparam_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
- # Generate a pending cross-reference so that Sphinx will ensure this link
- # isn't broken at some point in the future.
- title = f'rcParams["{text}"]'
- target = 'matplotlibrc-sample'
- ref_nodes, messages = inliner.interpreted(title, f'{title} <{target}>',
- 'ref', lineno)
-
- qr = QueryReference(rawtext, highlight=text)
- qr += ref_nodes
- node_list = [qr]
-
- # The default backend would be printed as "agg", but that's not correct (as
- # the default is actually determined by fallback).
- if text in rcParamsDefault and text != "backend":
- node_list.extend([
- nodes.Text(' (default: '),
- nodes.literal('', repr(rcParamsDefault[text])),
- nodes.Text(')'),
- ])
-
- return node_list, messages
-
-
-def mpltype_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
- mpltype = text
- type_to_link_target = {
- 'color': 'colors_def',
- }
- if mpltype not in type_to_link_target:
- raise ValueError(f"Unknown mpltype: {mpltype!r}")
-
- node_list, messages = inliner.interpreted(
- mpltype, f'{mpltype} <{type_to_link_target[mpltype]}>', 'ref', lineno)
- return node_list, messages
-
-
-def setup(app):
- app.add_role("rc", rcparam_role)
- app.add_role("mpltype", mpltype_role)
- app.add_node(
- QueryReference,
- html=(visit_query_reference_node, depart_query_reference_node),
- latex=(visit_query_reference_node, depart_query_reference_node),
- text=(visit_query_reference_node, depart_query_reference_node),
- )
- return {"parallel_read_safe": True, "parallel_write_safe": True}
diff --git a/doc/users/github_stats.rst b/doc/users/github_stats.rst
index a7f909b30cf5..0c8f29687afb 100644
--- a/doc/users/github_stats.rst
+++ b/doc/users/github_stats.rst
@@ -1,747 +1,195 @@
.. _github-stats:
-GitHub statistics for 3.9.0 (May 15, 2024)
+GitHub statistics for 3.9.1 (Jul 04, 2024)
==========================================
-GitHub statistics for 2023/09/15 (tag: v3.8.0) - 2024/05/15
+GitHub statistics for 2024/05/15 (tag: v3.9.0) - 2024/07/04
These lists are automatically generated, and may be incomplete or contain duplicates.
-We closed 97 issues and merged 450 pull requests.
-The full list can be seen `on GitHub `__
+We closed 30 issues and merged 111 pull requests.
+The full list can be seen `on GitHub `__
-The following 175 authors contributed 2584 commits.
+The following 29 authors contributed 184 commits.
-* 0taj
-* Abdul Razak Taha
-* Adam J. Stewart
-* Adam Turner
-* Aditi Gautam
-* agautam478
-* Alan Lau
-* Albert Y. Shih
-* Alec Vercruysse
-* Alexander Volkov
-* Alice Descoeudres
-* Allan Haldane
-* Amirreza Aflakparast
-* Ananya Devarakonda
-* ananya314
-* Anja Beck
-* Anjini2004
-* Ant Lockyer
* Antony Lee
-* Anvi Verma
-* Artyom Romanov
-* Augusto Borges
-* avramid9
-* Ben Root
-* bersbersbers
-* Binaya Sharma
-* Cameron
-* Chaoyi Hu
-* chaoyihu
-* Chiraag Balu
-* Christoph Hasse
-* ConstableCatnip
-* CozyFrog
-* Cyril Gadal
-* Dale Dai
-* Daniel Bergman
-* Daniel Hitchcock
-* danielcobej
-* David Gilbertson
-* David Stansby
-* ddale1128@gmail.com
+* Brigitta Sipőcz
+* Christian Mattsson
+* dale
* dependabot[bot]
-* Devilsaint
-* dohyun
-* Drew Kinneer
-* DWesl
-* Elisa Heckelmann
-* ElisaHeck
* Elliott Sales de Andrade
-* Eric Firing
-* Eric Prestat
-* esibinga
-* Eva Sibinga
-* Evgenii Radchenko
-* Faisal Fawad
-* Felipe Cybis Pereira
-* Garrett Sward
-* Gaurav-Kumar-Soni
-* Gauri Chaudhari
-* Gautam Sagar
+* Eytan Adler
* Greg Lucas
-* Gurudatta Shanbhag
+* haaris
* hannah
-* Haoying Zhang
-* Hugues Hoppe
-* i-jey
-* iamfaham
-* Ian Hunt-Isaak
* Ian Thomas
-* ifEricReturnTrue
* Illviljan
-* Issam
-* Issam Arabi
-* Jacob Stevens-Haas
-* Jacob Tomlinson
-* Jake
-* Jake Stevens-Haas
-* James Salsman
-* Jaroza727
-* Jeremy Farrell
-* Jirka
-* Jody Klymak
-* Jorge Moraleda
-* Joshua Stevenson
-* jovianw
-* João Andrade
-* jpgianfaldoni
-* jsdodge
-* jsjeelshah
-* judfs
-* Juhan Oskar Hennoste
-* Junpei Ota
-* Katherine Turk
-* katotaisei
-* KheshavKumar
-* Koustav Ghosh
-* Kritika Verma
+* K900
* Kyle Sunden
-* Linyi Li
-* linyilily
-* lkkmpn
-* Lucia Korpas
-* madisonwong210
-* Maggie Liu
-* Marc Bresson
+* Lumberbot (aka Jack)
+* malhar2460
* Matthew Feickert
-* Matthew Morrison
-* Matthias Bussonnier
* Melissa Weber Mendonça
-* melissawm
-* mliu08
-* Mostafa Noah
-* MostafaNouh0011
-* n-aswin
-* Nabil
-* nbarlowATI
-* Nidaa Rabah
-* Nivedita Chaudhari
+* MischaMegens2
* Oscar Gustafsson
-* patel-zeel
-* Pavel Liavonau
-* Pedro
-* Pedro Peçanha
-* Peter Talley
-* Pradeep Reddy Raamana
-* Prajwal Agrawal
-* Pranav Raghu
-* prateetishah
-* pre-commit-ci[bot]
-* QuadroTec
-* Rafael Tsuha
-* Raghuram Sirigiri
-* Raphael
-* Raphael Quast
-* Ratnabali Dutta
-* rawwash
-* rsp2210
-* Ruoyi
-* Ruoyi Xie
-* Rushikesh Pandya
* Ruth Comer
-* samGreer
-* Samuel Diebolt
-* saranti
* Scott Shambaugh
-* Sebastian Berg
-* Seohyeon Lee
-* Sheepfan0828
-* ShivamPathak99
-* Shriya Kalakata
-* shriyakalakata
-* Stefan
-* Steffen Rehberg
-* stevezhang1999
-* Sudhanshu Pandey
-* Talha Irfan
-* thehappycheese
+* simond07
+* SjoerdB93
+* Takumasa N
+* Takumasa N.
+* Takumasa Nakamura
* Thomas A Caswell
-* Tiago Lubiana
* Tim Hoffmann
-* tobias
-* Tom Sarantis
-* trananso
-* turnipseason
-* tusharkulkarni008
-* UFEddy
-* Vashesh08
-* vicky6
-* vigneshvetrivel8
-* wemi3
-* yangyangdotcom
-* YiLun Fan
-* Zach Champion
-* zachjweiner
-* zoehcycy
GitHub issues and pull requests:
-Pull Requests (450):
+Pull Requests (111):
-* :ghpull:`28206`: Backport PR #28205 on branch v3.9.x (TST: Fix tests with older versions of ipython)
-* :ghpull:`28207`: TST: Followup corrections to #28205
-* :ghpull:`28205`: TST: Fix tests with older versions of ipython
-* :ghpull:`28203`: Backport PR #28164 on branch v3.9.x (CI: Ensure code coverage is always uploaded)
-* :ghpull:`28204`: Backport PR #28195 on branch v3.9.x (TST: Prepare for pytest 9)
-* :ghpull:`28191`: DOC: Use released mpl-sphinx-theme on v3.9.x
-* :ghpull:`28195`: TST: Prepare for pytest 9
-* :ghpull:`28193`: Backport PR #28185 on branch v3.9.x (DOC: Bump mpl-sphinx-theme to 3.9)
-* :ghpull:`28190`: Backport PR #28103 on branch v3.9.x ([DOC]: Fix compatibility with sphinx-gallery 0.16)
-* :ghpull:`28164`: CI: Ensure code coverage is always uploaded
-* :ghpull:`28194`: Backport PR #28188 on branch v3.9.x ([TST] Bump some tolerances for Macos ARM)
-* :ghpull:`28188`: [TST] Bump some tolerances for Macos ARM
-* :ghpull:`28185`: DOC: Bump mpl-sphinx-theme to 3.9
-* :ghpull:`28189`: Backport PR #28181 on branch v3.9.x (DOC: Prepare release notes for 3.9)
-* :ghpull:`28103`: [DOC]: Fix compatibility with sphinx-gallery 0.16
-* :ghpull:`28181`: DOC: Prepare release notes for 3.9
-* :ghpull:`28184`: Backport PR #28182 on branch v3.9.x (Bump custom hatch deprecation expiration)
-* :ghpull:`28182`: Bump custom hatch deprecation expiration
-* :ghpull:`28178`: Backport PR #28171 on branch v3.9.x (Support removing absent tools from ToolContainerBase.)
-* :ghpull:`28171`: Support removing absent tools from ToolContainerBase.
-* :ghpull:`28174`: Backport PR #28169 on branch v3.9.x (Clarify public-ness of some ToolContainerBase APIs.)
-* :ghpull:`28169`: Clarify public-ness of some ToolContainerBase APIs.
-* :ghpull:`28160`: Backport PR #28039 on branch v3.9.x (Respect vertical_axis when rotating plot interactively)
-* :ghpull:`28159`: Backport PR #28157 on branch v3.9.x (Remove call to non-existent method _default_contains in Artist)
-* :ghpull:`28162`: Backport PR #27948 on branch v3.9.x (Move IPython backend mapping to Matplotlib and support entry points)
-* :ghpull:`28163`: Backport PR #28144 on branch v3.9.x (DOC: Refactor code in the fishbone diagram example)
-* :ghpull:`28144`: DOC: Refactor code in the fishbone diagram example
-* :ghpull:`27948`: Move IPython backend mapping to Matplotlib and support entry points
-* :ghpull:`28039`: Respect vertical_axis when rotating plot interactively
-* :ghpull:`28157`: Remove call to non-existent method _default_contains in Artist
-* :ghpull:`28141`: Backport PR #27960 on branch v3.9.x (Update AppVeyor config)
-* :ghpull:`28138`: Backport PR #28068 on branch v3.9.x ([TYP] Add possible type hint to ``colors`` argument in ``LinearSegmentedColormap.from_list``)
-* :ghpull:`28140`: Backport PR #28136 on branch v3.9.x (Appease pycodestyle.)
-* :ghpull:`27960`: Update AppVeyor config
-* :ghpull:`28068`: [TYP] Add possible type hint to ``colors`` argument in ``LinearSegmentedColormap.from_list``
-* :ghpull:`28136`: Appease pycodestyle.
-* :ghpull:`28135`: Backport PR #28134 on branch v3.9.x (DOC: Minor improvements on quickstart)
-* :ghpull:`28134`: DOC: Minor improvements on quickstart
-* :ghpull:`28121`: Backport PR #28085 on branch v3.9.x (Clarify that the pgf backend is never actually used interactively.)
-* :ghpull:`28120`: Backport PR #28102 on branch v3.9.x (Fix typo in color mapping documentation in quick_start.py)
-* :ghpull:`28109`: Backport PR #28100 on branch v3.9.x (TST: wxcairo sometimes raises OSError on missing cairo libraries)
-* :ghpull:`28100`: TST: wxcairo sometimes raises OSError on missing cairo libraries
-* :ghpull:`28108`: Backport PR #28107 on branch v3.9.x ([DOC] Fix description in CapStyle example)
-* :ghpull:`28107`: [DOC] Fix description in CapStyle example
-* :ghpull:`28102`: Fix typo in color mapping documentation in quick_start.py
-* :ghpull:`28095`: Backport PR #28094 on branch v3.9.x (DOC: exclude sphinx 7.3.*)
-* :ghpull:`28081`: Backport PR #28078 on branch v3.9.x (Clarify that findfont & _find_fonts_by_props return paths.)
-* :ghpull:`28080`: Backport PR #28077 on branch v3.9.x (Parent tk StringVar to the canvas widget, not to the toolbar.)
-* :ghpull:`28092`: Backport PR #28032 on branch v3.9.x (FIX: ensure images are C order before passing to pillow)
-* :ghpull:`28032`: FIX: ensure images are C order before passing to pillow
-* :ghpull:`28088`: Backport PR #28087 on branch v3.9.x (Document Qt5 minimal version.)
-* :ghpull:`28085`: Clarify that the pgf backend is never actually used interactively.
-* :ghpull:`28078`: Clarify that findfont & _find_fonts_by_props return paths.
-* :ghpull:`28077`: Parent tk StringVar to the canvas widget, not to the toolbar.
-* :ghpull:`28062`: Backport PR #28056 on branch v3.9.x (Strip trailing spaces from log-formatter cursor output.)
-* :ghpull:`28063`: Backport PR #28055 on branch v3.9.x (DOC: Improve inverted axis example)
-* :ghpull:`28056`: Strip trailing spaces from log-formatter cursor output.
-* :ghpull:`28049`: Backport PR #28036 on branch v3.9.x (BLD: Fetch version from setuptools_scm at build time)
-* :ghpull:`28036`: BLD: Fetch version from setuptools_scm at build time
-* :ghpull:`28038`: Backport PR #28023 on branch v3.9.x (ci: Update merge conflict labeler)
-* :ghpull:`28023`: ci: Update merge conflict labeler
-* :ghpull:`28035`: Backport PR #28026 on branch v3.9.x ([DOC] reshuffle of contributing)
-* :ghpull:`28026`: [DOC] reshuffle of contributing
-* :ghpull:`28024`: DOC: Rewrite "Work on an issue" section
-* :ghpull:`28011`: DOC: Move bug reports and feature requests to top of contributing index
-* :ghpull:`27747`: Move doc/users/installing/ to doc/install/
-* :ghpull:`27952`: ENH: Align titles
-* :ghpull:`28017`: Merge up v3.8.4
-* :ghpull:`28014`: Improve timeline example.
-* :ghpull:`28019`: DOC: correct path to mpl_toolkits reference images
-* :ghpull:`26981`: Fixes Issue #26377 - Auto-escape % Symbol in Latex in pie labels
-* :ghpull:`28007`: wx: Fix file extension for toolmanager-style toolbar
-* :ghpull:`25556`: Display cursor coordinates for all axes twinned with the current one.
-* :ghpull:`23597`: Always use PyQT/PySide6 for GitHub CI
-* :ghpull:`28013`: Avoid plt.xticks/plt.yticks in gallery examples.
-* :ghpull:`28006`: Fix deprecation warnings in ft2font extension
-* :ghpull:`27723`: ci: Enable testing on M1 macOS
-* :ghpull:`26375`: Add ``widths``, ``heights`` and ``angles`` setter to ``EllipseCollection``
-* :ghpull:`27999`: Remove documentation that some backends don't support hatching.
-* :ghpull:`26710`: Add support for High DPI displays to wxAgg backend
-* :ghpull:`27148`: Correctly treat pan/zoom events of overlapping axes.
-* :ghpull:`27981`: DOC: Fix label type specification in parameter descriptions
-* :ghpull:`27979`: Clarify error message for bad-dimensionality in pcolorfast().
-* :ghpull:`27962`: DOC: Document axes_grid1.Grid attributes
-* :ghpull:`27968`: MNT: Remove remaining 3.7 deprecations
-* :ghpull:`27965`: DOC: Rewrite the example illustrating bxp()
-* :ghpull:`26453`: add documentation for reloading font cache
-* :ghpull:`26131`: Tst/restore old tests
-* :ghpull:`27730`: Add an rcparam for image.interpolation_stage.
-* :ghpull:`27956`: Use PyOS_setsig in macos backend
-* :ghpull:`27829`: Simplify color/marker disambiguation logic in _process_plot_format.
-* :ghpull:`27840`: Add legend support for boxplots
-* :ghpull:`27943`: Support Cn, n>9 in plot() shorthand format.
-* :ghpull:`27950`: ci: Fix condition for publishing wheels
-* :ghpull:`27909`: Add a note to pyplot docstrings referencing the corresponding object methods
-* :ghpull:`27929`: DOC: Add summary lines to plot types
-* :ghpull:`27915`: [BUG] Fix redirect-from Sphinx extension
-* :ghpull:`27945`: DOC: Explain leading dot in object references
-* :ghpull:`27947`: Update docs for ``FancyArrowPatch`` & ``Annotation`` to make it clear that ShrinkA/B parameters are in points and not fractional.
-* :ghpull:`27944`: Bump the actions group with 2 updates
-* :ghpull:`27932`: Fix pickling of make_axes_area_auto_adjustable'd axes.
-* :ghpull:`26500`: closes #26477 ENH: Add interpolation_stage in qt figureoptions
-* :ghpull:`27927`: Update docs
-* :ghpull:`27916`: Revert renaming labels to tick_labels in boxplot_stats()
-* :ghpull:`27931`: Highlight development_setup code snippets as bash, not python.
-* :ghpull:`27856`: Support hatching in cairo backends.
-* :ghpull:`27922`: Fix cbook style
-* :ghpull:`27668`: MNT: prevent merging using labels + branch protection rules
-* :ghpull:`27857`: Documentation edit for matshow function
-* :ghpull:`27928`: DOC: Fix syntax for ToolBase.image docstring
-* :ghpull:`27873`: Simplify the LineCollection example
-* :ghpull:`27492`: Fix semantics of MEP22 image names.
-* :ghpull:`27918`: Fix new flake8 errors from old merge
-* :ghpull:`27874`: Modernize macosx backend a bit
-* :ghpull:`25887`: Update ``_unpack_to_numpy`` function to convert JAX and PyTorch arrays to NumPy
-* :ghpull:`27685`: Work around pyparsing diagnostic warnings
-* :ghpull:`26594`: Added optional props argument to Lasso Widget __init__ to customize Lasso line
-* :ghpull:`22761`: Add minor ticks on and off in Axis
-* :ghpull:`22407`: Add ``set_XY`` and ``set_data`` to ``Quiver``
-* :ghpull:`27901`: Rename boxplot's tick label parameter
-* :ghpull:`27883`: Fix build on older macOS deployment targets
-* :ghpull:`27900`: Remove empty user guide tutorials page
-* :ghpull:`27885`: Clean up headers in extensions
-* :ghpull:`27910`: DOC: Fix dead link in README
-* :ghpull:`26567`: Use SVG inheritance diagrams now that linking has been fixed
-* :ghpull:`27899`: Merge up 3.8.x into main
-* :ghpull:`27905`: Improved error message for malformed colors
-* :ghpull:`27906`: Override open_group, close_group methods in PathEffectRenderer
-* :ghpull:`27904`: FIX: Restore D213 in flake8
-* :ghpull:`27895`: Remove versions from sidebar in docs
-* :ghpull:`27894`: Mark triangulation classes as final
-* :ghpull:`27557`: Use :mpltype:``color`` for color types
-* :ghpull:`27845`: Make sure custom alpha param does not change 'none' colors in a list of colors
-* :ghpull:`27719`: Add BackendRegistry singleton class
-* :ghpull:`27890`: DOC: State approximate documentation build time
-* :ghpull:`27887`: BLD: Add a fallback URL for FreeType
-* :ghpull:`25224`: Allow passing a transformation to secondary_xaxis/_yaxis
-* :ghpull:`27886`: Fix devdocs version switcher
-* :ghpull:`27884`: FIX: don't copy twice on RGB input
-* :ghpull:`27087`: Convert path extension to pybind11
-* :ghpull:`27867`: DOC: Update some animation related topics
-* :ghpull:`27848`: FIX: handle nans in RGBA input with ScalarMappables
-* :ghpull:`27821`: BLD,Cygwin: Include Python.h first in various C++ files
-* :ghpull:`27457`: TST: adding tests of current clear behavior on ticks
-* :ghpull:`27872`: doc: add description of ``**kwargs`` usage to collections
-* :ghpull:`27868`: Use pybind11 string formatter for exception messages
-* :ghpull:`27862`: Add dtype/copy args to internal testing class
-* :ghpull:`27658`: Bump pydata-sphinx-theme
-* :ghpull:`27303`: FIX: also exclude np.nan in RGB(A) in color mapping
-* :ghpull:`27860`: Bump the actions group with 2 updates
-* :ghpull:`27869`: Correctly set temporary pdf/pgf backends
-* :ghpull:`27850`: Deprecate ``plot_date``
-* :ghpull:`27815`: Add side option to violinplot
-* :ghpull:`27836`: DOC: use ... for continuation prompt in docstrings
-* :ghpull:`27819`: MNT: remove draw method args and kwargs
-* :ghpull:`27813`: DOC: Update violinplot() docs
-* :ghpull:`27698`: Add linting and validation of all YAML files
-* :ghpull:`27811`: Fix Annulus width check
-* :ghpull:`27667`: Change return type of ``ion`` and ``ioff`` to fix unbound variable errors with Pyright
-* :ghpull:`27807`: Expand CI pytest reporting config to ignore xfails
-* :ghpull:`27806`: Remove self._renderer from AnnotationBbox and ConnectionPatch
-* :ghpull:`27799`: Clarify that set_ticks() affects major/minor ticks independently
-* :ghpull:`27787`: Improve documentation on boxplot and violinplot
-* :ghpull:`27800`: Deactivate sidebar for release notes
-* :ghpull:`27798`: Fix sphinx-gallery CSS
-* :ghpull:`27462`: DOC: clarify the default value of *radius* in Patch.contains_point
-* :ghpull:`27565`: MNT: arghandling subplotspec
-* :ghpull:`27796`: Make mypy a bit stricter
-* :ghpull:`27767`: Update handling of sequence labels for plot
-* :ghpull:`27795`: Add EffVer badge
-* :ghpull:`27780`: Partly revert #27711
-* :ghpull:`27768`: MNT: deprecate draw method args and kwargs
-* :ghpull:`27783`: Update README.md to fix citation link
-* :ghpull:`27726`: TST: always set a (long) timeout for subprocess and always use our wrapper
-* :ghpull:`27781`: Simplify example: Box plots with custom fill colors
-* :ghpull:`27750`: Bump the actions group with 2 updates
-* :ghpull:`27771`: Add marker-only and line+marker visuals to the plot() plot types
-* :ghpull:`27764`: Increase size of legend in Legend guide example
-* :ghpull:`26800`: Bump minimum NumPy version to 1.23
-* :ghpull:`27752`: Update some Meson internals
-* :ghpull:`27702`: GOV: adopt EffVer
-* :ghpull:`26965`: Removal of deprecated API cm
-* :ghpull:`27758`: [Doc] Remove special casing for removed method
-* :ghpull:`25815`: [TST] Make jpl units instantiated with datetimes consistent with mpl converters
-* :ghpull:`27729`: DOC: Improve colormap normalization example
-* :ghpull:`27732`: TST: Remove memory leak test
-* :ghpull:`27733`: ci: Simplify CodeQL setup
-* :ghpull:`27692`: Add method to update position of arrow patch
-* :ghpull:`27736`: Fix incorrect API reference in docs
-* :ghpull:`27731`: DOC: Create explicit rename legend entry section in guide
-* :ghpull:`27560`: Moved /users/project to /doc/project
-* :ghpull:`27728`: Simplify Figure._suplabels.
-* :ghpull:`27715`: Bump the actions group with 3 updates
-* :ghpull:`27711`: Fix boxplot legend entries part 2
-* :ghpull:`27696`: DOC: clean up automated tests section of workflow docs
-* :ghpull:`27686`: Improve Locator docstrings
-* :ghpull:`27704`: ci: Remove prerelease conditions from Azure Pipelines
-* :ghpull:`27568`: Fix boxplot legend entries
-* :ghpull:`27694`: MNT: fix labeller
-* :ghpull:`26953`: MNT: test that table doesn't try to convert unitized data
-* :ghpull:`27690`: Remove "Past versions" section from release notes
-* :ghpull:`26926`: Closes #22011: Changes to SubFigures so it behaves like a regular artist
-* :ghpull:`27469`: Fixed legend with legend location "best" when legend overlaps shaded area and text
-* :ghpull:`27684`: Bump the actions group with 1 update
-* :ghpull:`27665`: Axes.inset_axes - warning message removed
-* :ghpull:`27688`: CI: skip code coverage upload on scheduled tests
-* :ghpull:`27689`: ci: Don't include API/what's new notes in general doc labels
-* :ghpull:`27640`: Add ``get_cursor_data`` to ``NonUniformImage``
-* :ghpull:`27676`: BLD: Downgrade FreeType to 2.6.1 on Windows ARM
-* :ghpull:`27619`: Use GH action to install reviewdog
-* :ghpull:`27552`: TST: Use importlib for importing in pytest
-* :ghpull:`27650`: DOC: Added call out to API guidelines to contribute + small API guidelines reorg
-* :ghpull:`27618`: Add option of running stubtest using tox
-* :ghpull:`27656`: Bump the actions group with 1 update
-* :ghpull:`27415`: Use class form of data classes
-* :ghpull:`27649`: Check for latex binary before building docs
-* :ghpull:`27641`: MNT: fix api changes link in PR template
-* :ghpull:`27644`: ci: Fix mpl_toolkits label
-* :ghpull:`27230`: Query macOS for available system fonts.
-* :ghpull:`27643`: ci: Update nightly upload for artifacts v4
-* :ghpull:`27642`: Fix auto-labeler configuration
-* :ghpull:`27639`: Doc: typo fix for #22699
-* :ghpull:`26978`: [pre-commit.ci] pre-commit autoupdate
-* :ghpull:`27563`: Enable PyPI publishing from GitHub Actions
-* :ghpull:`22699`: Proof of concept for adding kwdoc content to properties using a decorator
-* :ghpull:`27633`: Auto-label PRs based on changed files
-* :ghpull:`27607`: Error on bad input to hexbin extents
-* :ghpull:`27629`: Don't run CI twice on dependabot branches
-* :ghpull:`27562`: Avoid an extra copy/resample if imshow input has no alpha
-* :ghpull:`27628`: Bump the actions group with 2 updates
-* :ghpull:`27626`: CI: Group dependabot updates
-* :ghpull:`27589`: Don't clip PowerNorm inputs < vmin
-* :ghpull:`27613`: Fix marker validator with cycler (allow mix of classes)
-* :ghpull:`27615`: MNT: add spaces to PR template
-* :ghpull:`27614`: DOC: Updated link in annotation API docs to point to annotation user guide
-* :ghpull:`27605`: Ignore masked values in boxplot
-* :ghpull:`26884`: Remove deprecated code from _fontconfig_patterns
-* :ghpull:`27602`: Let FormatStrFormatter respect axes.unicode_minus.
-* :ghpull:`27601`: Clarify dollar_ticks example and FormatStrFormatter docs.
-* :ghpull:`24834`: Deprecate apply_theta_transforms=True to PolarTransform
-* :ghpull:`27591`: Use macOS instead of OSX in comments/docs
-* :ghpull:`27577`: MNT: add the running version to pickle warning message
-* :ghpull:`25191`: Deprecate 'prune' kwarg to MaxNLocator
-* :ghpull:`27566`: DOC: changed tag ``plot type`` to ``plot-type``
-* :ghpull:`27105`: Use Axes instead of axes core library code
-* :ghpull:`27575`: Add quotes round .[dev] in editable install command
-* :ghpull:`27104`: Use Axes instead of axes in galleries
-* :ghpull:`27373`: Transpose grid_finder tick representation.
-* :ghpull:`27363`: ci: Improve coverage for compiled code
-* :ghpull:`27200`: DOC: Add role for custom informal types like color
-* :ghpull:`27548`: DOC: typo fix in contribute doc
-* :ghpull:`27458`: Check if the mappable is in a different Figure than the one fig.color…
-* :ghpull:`27546`: MNT: Clean up some style exceptions
-* :ghpull:`27514`: Improve check for bbox
-* :ghpull:`27265`: DOC: reorganizing contributing docs to clean up toc, better separate topics
-* :ghpull:`27517`: Best-legend-location microoptimization
-* :ghpull:`27540`: Bump github/codeql-action from 2 to 3
-* :ghpull:`27520`: [Doc] Minor consistency changes and correction of Marker docs
-* :ghpull:`27505`: Download Qhull source from Github, not Qhull servers, in meson build
-* :ghpull:`27518`: Micro-optimizations related to list handling
-* :ghpull:`27495`: Bump actions/stale from 8 to 9
-* :ghpull:`27523`: Changes for stale GHA v9
-* :ghpull:`27519`: [Doc] Improve/correct docs for 3D
-* :ghpull:`27447`: TST: Compress some hist geometry tests
-* :ghpull:`27513`: Fix docs and add tests for transform and deprecate ``BboxTransformToMaxOnly``
-* :ghpull:`27511`: TST: Add tests for Affine2D
-* :ghpull:`27424`: Added Axes.stairs test in test_datetime.py
-* :ghpull:`27267`: Fix/restore secondary axis support for Transform-type functions
-* :ghpull:`27013`: Add test_contour under test_datetime.py
-* :ghpull:`27497`: Clarify that set_axisbelow doesn't move grids below images.
-* :ghpull:`27498`: Remove unnecessary del local variables at end of Gcf.destroy.
-* :ghpull:`27466`: Add test_eventplot to test_datetime.py
-* :ghpull:`25905`: Use annotate coordinate systems to simplify label_subplots.
-* :ghpull:`27471`: Doc: visualizing_tests and ``triage_tests`` tools
-* :ghpull:`27474`: Added smoke test for Axes.matshow to test_datetime.py
-* :ghpull:`27470`: Fix test visualization tool for non-PNG files
-* :ghpull:`27426`: DOC: normalizing histograms
-* :ghpull:`27452`: Cleanup unit_cube-methods
-* :ghpull:`27431`: Added test for Axes.bar_label
-* :ghpull:`26962`: Remove backend 3.7-deprecated API
-* :ghpull:`27410`: Add test_vlines to test_datetime.py
-* :ghpull:`27425`: Added test_fill_betweenx in test_datetime.py
-* :ghpull:`27449`: Remove test_quiverkey from test_datetime.py
-* :ghpull:`27427`: MNT/TST: remove xcorr and acorr from test_datetime
-* :ghpull:`27390`: Add test_bxp in test_datetime.py
-* :ghpull:`27428`: Added test for broken_barh to test_datetime.py
-* :ghpull:`27222`: [TST] Added test_annotate in test_datetime.py
-* :ghpull:`27135`: Added smoke test for Axes.stem
-* :ghpull:`27343`: Fix draggable annotations on subfigures.
-* :ghpull:`27033`: Add test_bar in test_datetime
-* :ghpull:`27423`: Add test for fill_between in test_datetime.py
-* :ghpull:`27409`: Fix setting ``_selection_completed`` in ``SpanSelector`` when spanselector is initialised using ``extents``
-* :ghpull:`27440`: Fix get_path for 3d artists
-* :ghpull:`27422`: TST: Cache available interactive backends
-* :ghpull:`27401`: Add test_fill in test_datetime.py
-* :ghpull:`27419`: DOC: Add AsinhScale to list of built-in scales
-* :ghpull:`27417`: Switch pytest fixture from tmpdir to tmp_path
-* :ghpull:`27172`: ENH: Change logging to warning when creating a legend with no labels
-* :ghpull:`27405`: Check that xerr/yerr values are not None in errorbar
-* :ghpull:`27392`: Remove test_spy from test_datetime.py
-* :ghpull:`27331`: Added smoke test for Axes.barbs in test_datetime.py
-* :ghpull:`27393`: MNT: Fix doc makefiles
-* :ghpull:`27387`: Revert "MNT: add _version.py to .gitignore"
-* :ghpull:`27347`: FIX: scale norm of collections when first array is set
-* :ghpull:`27374`: MNT: add _version.py to .gitignore
-* :ghpull:`19011`: Simplify tk tooltip setup.
-* :ghpull:`27367`: Fix _find_fonts_by_props docstring
-* :ghpull:`27359`: Fix build on PyPy
-* :ghpull:`27362`: Implement SubFigure.remove.
-* :ghpull:`27360`: Fix removal of colorbars on nested subgridspecs.
-* :ghpull:`27211`: Add test_hlines to test_datetimes.py
-* :ghpull:`27353`: Refactor AxisArtistHelpers
-* :ghpull:`27357`: [DOC]: Update 3d axis limits what's new
-* :ghpull:`26992`: Convert TkAgg utilities to pybind11
-* :ghpull:`27215`: Add ``@QtCore.Slot()`` decorations to ``NavigationToolbar2QT``
-* :ghpull:`26907`: Removal of deprecations for Contour
-* :ghpull:`27285`: Factor out common parts of qt and macos interrupt handling.
-* :ghpull:`27306`: Simplify GridSpec setup in make_axes_gridspec.
-* :ghpull:`27313`: FIX: allow re-shown Qt windows to be re-destroyed
-* :ghpull:`27184`: Use pybind11 for qhull wrapper
-* :ghpull:`26794`: Use pybind11 in _c_internal_utils module
-* :ghpull:`27300`: Remove idiosyncratic get_tick_iterator API.
-* :ghpull:`27275`: MAINT: fix .yml in tag issue template
-* :ghpull:`27288`: Use int.from_bytes instead of implementing the conversion ourselves.
-* :ghpull:`27286`: Various cleanups
-* :ghpull:`27279`: Tweak a few docstrings.
-* :ghpull:`27256`: merge up v3.8.1
-* :ghpull:`27254`: Remove redundant axes_grid colorbar examples.
-* :ghpull:`27251`: webagg: Don't resize canvas if WebSocket isn't connected
-* :ghpull:`27236`: Tagging Example - Tags for multiple figs demo
-* :ghpull:`27245`: MNT: be more careful in Qt backend that there is actually a Figure
-* :ghpull:`27158`: First attempt for individual hatching styles for stackplot
-* :ghpull:`26851`: Establish draft Tag glossary and Tagging guidelines
-* :ghpull:`27083`: DOC: Add tags infrastructure for gallery examples
-* :ghpull:`27204`: BLD: Use NumPy nightly wheels for non-release builds
-* :ghpull:`27208`: Add test_axvline to test_datetime.py
-* :ghpull:`26989`: MNT: print fontname in missing glyph warning
-* :ghpull:`27177`: Add test_axhline in test_datetime.py
-* :ghpull:`27164`: docs: adding explanation for color in ``set_facecolor``
-* :ghpull:`27175`: Deprecate mixing positional and keyword args for legend(handles, labels)
-* :ghpull:`27199`: DOC: clean up links under table formatting docs
-* :ghpull:`27185`: Added smoke tests for Axes.errorbar in test_datetime.py
-* :ghpull:`27091`: Add test_step to test_datetime.py
-* :ghpull:`27182`: Add example for plotting a bihistogram
-* :ghpull:`27130`: added test_axvspan in test.datetime.py
-* :ghpull:`27094`: MNT: move pytest.ini configs to .toml
-* :ghpull:`27139`: added test_axhspan in test_datetime.py
-* :ghpull:`27058`: DOC: concise dependency heading + small clarifications
-* :ghpull:`27053`: Added info for getting compilation output from meson on autorebuild
-* :ghpull:`26906`: Fix masking for Axes3D.plot()
-* :ghpull:`27142`: Added smoke test for Axes.text in test_datetime.py
-* :ghpull:`27024`: Add test_contourf in test_datetime.py
-* :ghpull:`22347`: correctly treat pan/zoom events of overlapping axes
-* :ghpull:`26900`: #26865 removing deprecations to axislines.py
-* :ghpull:`26696`: DOC: Fix colLoc default
-* :ghpull:`27064`: Close all plot windows of a blocking show() on Ctrl+C
-* :ghpull:`26882`: Add scatter test for datetime units
-* :ghpull:`27114`: add test_stackplot in test_datetime.py
-* :ghpull:`27084`: Add test_barh to test_datetime.py
-* :ghpull:`27110`: DOC: Move figure member sections one level down
-* :ghpull:`27127`: BLD: use python3 for shebang consistent with pep-394
-* :ghpull:`27111`: BLD: Fix setting FreeType build type in extension
-* :ghpull:`26921`: MNT: clarify path.sketch rcparam format + test validate_sketch
-* :ghpull:`27109`: TST: Use importlib for subprocess tests
-* :ghpull:`27119`: Update clabel comment.
-* :ghpull:`27117`: Remove datetime test for axes.pie
-* :ghpull:`27095`: Deprecate nth_coord parameter from FixedAxisArtistHelper.new_fixed_axis.
-* :ghpull:`27066`: Tweak array_view to be more like pybind11
-* :ghpull:`27090`: Restore figaspect() API documentation
-* :ghpull:`27074`: Issue #26990: Split the histogram image into two for each code block.
-* :ghpull:`27086`: Rename py namespace to mpl in extension code
-* :ghpull:`27082`: MAINT: Update environment.yml to match requirements files
-* :ghpull:`27072`: Remove datetime test stubs for spectral methods/table
-* :ghpull:`26830`: Update stix table with Unicode names
-* :ghpull:`26969`: DOC: add units to user/explain [ci doc]
-* :ghpull:`27028`: Added test_hist in test_datetime.py
-* :ghpull:`26876`: issue: 26871 - Remove SimplePath class from patches.py
-* :ghpull:`26875`: Fix Deprecation in patches.py
-* :ghpull:`26890`: Removing deprecated api from patches
-* :ghpull:`27037`: add test_plot_date in test_datetime.py
-* :ghpull:`27012`: Bump required C++ standard to c++17
-* :ghpull:`27021`: Add a section to Highlight past winners for JDH plotting contest in docs
-* :ghpull:`27004`: Warning if handles and labels have a len mismatch
-* :ghpull:`24061`: #24050 No error was thrown even number of handles mismatched labels
-* :ghpull:`26754`: DOC: separate and clarify axisartist default tables
-* :ghpull:`27020`: CI: Update scientific-python/upload-nightly-action to 0.2.0
-* :ghpull:`26951`: Clarify that explicit ticklabels are used without further formatting.
-* :ghpull:`26894`: Deprecate setting the timer interval while starting it.
-* :ghpull:`13401`: New clear() method for Radio and Check buttons
-* :ghpull:`23829`: Start transitioning to pyproject.toml
-* :ghpull:`26621`: Port build system to Meson
-* :ghpull:`26928`: [TYP] Add tool for running stubtest
-* :ghpull:`26917`: Deprecate ContourLabeler.add_label_clabeltext.
-* :ghpull:`26960`: Deprecate backend_ps.get_bbox_header, and split it for internal use.
-* :ghpull:`26967`: Minor cleanups.
-* :ghpull:`26909`: deprecated api tri
-* :ghpull:`26946`: Inline Cursor._update into its sole caller.
-* :ghpull:`26915`: DOC: Clarify description and add examples in colors.Normalize
-* :ghpull:`26874`: Cleaned up the span_where class method from Polycollections.
-* :ghpull:`26586`: Support standard formatters in axisartist.
-* :ghpull:`26788`: Fix axh{line,span} on polar axes.
-* :ghpull:`26935`: add tomli to rstcheck extras
-* :ghpull:`26275`: Use pybind11 in image module
-* :ghpull:`26887`: DOC: improve removal for julian dates [ci doc]
-* :ghpull:`26929`: DOC: Fix removal doc for Animation attributes
-* :ghpull:`26918`: 26865 Removed deprecations from quiver.py
-* :ghpull:`26902`: Fixed deprecated APIs in lines.py
-* :ghpull:`26903`: Simplify CheckButtons and RadioButtons click handler.
-* :ghpull:`26899`: MNT: only account for Artists once in fig.get_tightbbox
-* :ghpull:`26861`: QT/NavigationToolbar2: configure subplots dialog should be modal
-* :ghpull:`26885`: Removed deprecated code from gridspec.py
-* :ghpull:`26880`: Updated offsetbox.py
-* :ghpull:`26910`: Removed the deprecated code from offsetbox.py
-* :ghpull:`26905`: Add users/explain to default skip subdirs
-* :ghpull:`26853`: Widgets: Remove deprecations and make arguments keyword only
-* :ghpull:`26877`: Fixes deprecation in lines.py
-* :ghpull:`26871`: Removed the deprecated code from ``axis.py``
-* :ghpull:`26872`: Deprecated code removed in animation.py
-* :ghpull:`26859`: Add datetime testing skeleton
-* :ghpull:`26848`: ci: Don't install recommended packages on Circle
-* :ghpull:`26852`: Remove Julian date support
-* :ghpull:`26801`: [MNT]: Cleanup ticklabel_format (style=)
-* :ghpull:`26840`: Reduce redundant information in _process_plot_var_args.
-* :ghpull:`26731`: Explicitly set foreground color to black in svg icons
-* :ghpull:`26826`: [MNT] Move NUM_VERTICES from mplutils.h to the only file it is used in
-* :ghpull:`26742`: [TYP] Add typing for some private methods and modules
-* :ghpull:`26819`: Reorder safe_first_element() and _safe_first_finite() code
-* :ghpull:`26813`: Bump docker/setup-qemu-action from 2 to 3
-* :ghpull:`26797`: Remove deprecated draw_gouraud_triangle
-* :ghpull:`26815`: Remove plt.Axes from tests
-* :ghpull:`26818`: Fix doc build (alternative)
-* :ghpull:`26785`: merge up v3.8.0
-* :ghpull:`25272`: Do not add padding to 3D axis limits when limits are manually set
-* :ghpull:`26798`: Remove deprecated methods and attributed in Axes3D
-* :ghpull:`26744`: Use cbook methods for string checking
-* :ghpull:`26802`: specify input range in logs when image data must be clipped
-* :ghpull:`26787`: Remove unused Axis private init helpers.
-* :ghpull:`26629`: DOC: organize figure API
-* :ghpull:`26690`: Make generated pgf code more robust against later changes of tex engine.
-* :ghpull:`26577`: Bugfix: data sanitizing for barh
-* :ghpull:`26684`: Update PR template doc links
-* :ghpull:`26686`: PR template: shorten comment and pull up top
-* :ghpull:`26670`: Added sanitize_sequence to kwargs in _preprocess_data
-* :ghpull:`26634`: [MNT] Move SubplotParams from figure to gridspec
-* :ghpull:`26609`: Cleanup AutoMinorLocator implementation.
-* :ghpull:`26293`: Added get_xmargin(), get_ymargin() and get_zmargin() and tests.
-* :ghpull:`26516`: Replace reference to %pylab by %matplotlib.
-* :ghpull:`26483`: Improve legend(loc='best') warning and test
-* :ghpull:`26482`: [DOC]: print pydata sphinx/mpl theme versions
-* :ghpull:`23787`: Use pybind11 for C/C++ extensions
+* :ghpull:`28507`: Backport PR #28430 on branch v3.9.x (Fix pickling of AxesWidgets.)
+* :ghpull:`28506`: Backport PR #28451 on branch v3.9.x (Fix GTK cairo backends)
+* :ghpull:`28430`: Fix pickling of AxesWidgets.
+* :ghpull:`25861`: Fix Hidpi scaling for GTK4Cairo
+* :ghpull:`28451`: Fix GTK cairo backends
+* :ghpull:`28499`: Backport PR #28498 on branch v3.9.x (Don't fail if we can't query system fonts on macOS)
+* :ghpull:`28498`: Don't fail if we can't query system fonts on macOS
+* :ghpull:`28491`: Backport PR #28487 on branch v3.9.x (Fix autoscaling with axhspan)
+* :ghpull:`28490`: Backport PR #28486 on branch v3.9.x (Fix CompositeGenericTransform.contains_branch_seperately)
+* :ghpull:`28487`: Fix autoscaling with axhspan
+* :ghpull:`28486`: Fix CompositeGenericTransform.contains_branch_seperately
+* :ghpull:`28483`: Backport PR #28393 on branch v3.9.x (Make sticky edges only apply if the sticky edge is the most extreme limit point)
+* :ghpull:`28482`: Backport PR #28473 on branch v3.9.x (Do not lowercase module:// backends)
+* :ghpull:`28393`: Make sticky edges only apply if the sticky edge is the most extreme limit point
+* :ghpull:`28473`: Do not lowercase module:// backends
+* :ghpull:`28480`: Backport PR #28474 on branch v3.9.x (Fix typing and docs for containers)
+* :ghpull:`28479`: Backport PR #28397 (FIX: stale root Figure when adding/updating subfigures)
+* :ghpull:`28474`: Fix typing and docs for containers
+* :ghpull:`28472`: Backport PR #28289 on branch v3.9.x (Promote mpltype Sphinx role to a public extension)
+* :ghpull:`28471`: Backport PR #28342 on branch v3.9.x (DOC: Document the parameter *position* of apply_aspect() as internal)
+* :ghpull:`28470`: Backport PR #28398 on branch v3.9.x (Add GIL Release to flush_events in macosx backend)
+* :ghpull:`28469`: Backport PR #28355 on branch v3.9.x (MNT: Re-add matplotlib.cm.get_cmap)
+* :ghpull:`28397`: FIX: stale root Figure when adding/updating subfigures
+* :ghpull:`28289`: Promote mpltype Sphinx role to a public extension
+* :ghpull:`28342`: DOC: Document the parameter *position* of apply_aspect() as internal
+* :ghpull:`28398`: Add GIL Release to flush_events in macosx backend
+* :ghpull:`28355`: MNT: Re-add matplotlib.cm.get_cmap
+* :ghpull:`28468`: Backport PR #28465 on branch v3.9.x (Fix pickling of SubFigures)
+* :ghpull:`28465`: Fix pickling of SubFigures
+* :ghpull:`28462`: Backport PR #28440 on branch v3.9.x (DOC: Add note about simplification of to_polygons)
+* :ghpull:`28460`: Backport PR #28459 on branch v3.9.x (DOC: Document kwargs scope for tick setter functions)
+* :ghpull:`28461`: Backport PR #28458 on branch v3.9.x (Correct numpy dtype comparisons in image_resample)
+* :ghpull:`28440`: DOC: Add note about simplification of to_polygons
+* :ghpull:`28458`: Correct numpy dtype comparisons in image_resample
+* :ghpull:`28459`: DOC: Document kwargs scope for tick setter functions
+* :ghpull:`28450`: Backport of 28371 and 28411
+* :ghpull:`28446`: Backport PR #28403 on branch v3.9.x (FIX: Autoscale support in add_collection3d for Line3DCollection and Poly3DCollection
+* :ghpull:`28445`: Backport PR #28403 on branch v3.9.x (FIX: Autoscale support in add_collection3d for Line3DCollection and Poly3DCollection)
+* :ghpull:`28438`: Backport PR #28436 on branch v3.9.x (Fix ``is_color_like`` for 2-tuple of strings and fix ``to_rgba`` for ``(nth_color, alpha)``)
+* :ghpull:`28403`: FIX: Autoscale support in add_collection3d for Line3DCollection and Poly3DCollection
+* :ghpull:`28443`: Backport PR #28441 on branch v3.9.x (MNT: Update basic units example to work with numpy 2.0)
+* :ghpull:`28441`: MNT: Update basic units example to work with numpy 2.0
+* :ghpull:`28436`: Fix ``is_color_like`` for 2-tuple of strings and fix ``to_rgba`` for ``(nth_color, alpha)``
+* :ghpull:`28426`: Backport PR #28425 on branch v3.9.x (Fix Circle yaml line length)
+* :ghpull:`28427`: Fix circleci yaml
+* :ghpull:`28425`: Fix Circle yaml line length
+* :ghpull:`28422`: Backport PR #28401 on branch v3.9.x (FIX: Fix text wrapping)
+* :ghpull:`28424`: Backport PR #28423 on branch v3.9.x (Update return type for Axes.axhspan and Axes.axvspan)
+* :ghpull:`28423`: Update return type for Axes.axhspan and Axes.axvspan
+* :ghpull:`28401`: FIX: Fix text wrapping
+* :ghpull:`28419`: Backport PR #28414 on branch v3.9.x (Clean up obsolete widget code)
+* :ghpull:`28411`: Bump the actions group with 3 updates
+* :ghpull:`28414`: Clean up obsolete widget code
+* :ghpull:`28415`: Backport PR #28413 on branch v3.9.x (CI: update action that got moved org)
+* :ghpull:`28413`: CI: update action that got moved org
+* :ghpull:`28392`: Backport PR #28388 on branch v3.9.x (Allow duplicate (name, value) entry points for backends)
+* :ghpull:`28362`: Backport PR #28337 on branch v3.9.x (Bump the actions group across 1 directory with 3 updates)
+* :ghpull:`28388`: Allow duplicate (name, value) entry points for backends
+* :ghpull:`28389`: Backport PR #28380 on branch v3.9.x (Remove outdated docstring section in RendererBase.draw_text.)
+* :ghpull:`28380`: Remove outdated docstring section in RendererBase.draw_text.
+* :ghpull:`28385`: Backport PR #28377 on branch v3.9.x (DOC: Clarify scope of wrap.)
+* :ghpull:`28377`: DOC: Clarify scope of wrap.
+* :ghpull:`28368`: Backport PR #28359 on branch v3.9.x (Document that axes unsharing is impossible.)
+* :ghpull:`28359`: Document that axes unsharing is impossible.
+* :ghpull:`28337`: Bump the actions group across 1 directory with 3 updates
+* :ghpull:`28351`: Backport PR #28307 on branch v3.9.x (DOC: New color line by value example)
+* :ghpull:`28307`: DOC: New color line by value example
+* :ghpull:`28339`: Backport PR #28336 on branch v3.9.x (DOC: Add version warning banner for docs versions different from stable)
+* :ghpull:`28336`: DOC: Add version warning banner for docs versions different from stable
+* :ghpull:`28334`: Backport PR #28332 on branch v3.9.x (Call IPython.enable_gui when install repl displayhook)
+* :ghpull:`28332`: Call IPython.enable_gui when install repl displayhook
+* :ghpull:`28331`: Backport PR #28329 on branch v3.9.x (DOC: Add example for 3D intersecting planes)
+* :ghpull:`28329`: DOC: Add example for 3D intersecting planes
+* :ghpull:`28327`: Backport PR #28292 on branch v3.9.x (Resolve MaxNLocator IndexError when no large steps)
+* :ghpull:`28292`: Resolve MaxNLocator IndexError when no large steps
+* :ghpull:`28326`: Backport PR #28041 on branch v3.9.x ([BUG]: Shift box_aspect according to vertical_axis)
+* :ghpull:`28041`: [BUG]: Shift box_aspect according to vertical_axis
+* :ghpull:`28320`: Backport PR #27001 on branch v3.9.x ([TYP] Add overload of ``pyplot.subplots``)
+* :ghpull:`27001`: [TYP] Add overload of ``pyplot.subplots``
+* :ghpull:`28318`: Backport PR #28273 on branch v3.9.x (CI: Add GitHub artifact attestations to package distribution)
+* :ghpull:`28273`: CI: Add GitHub artifact attestations to package distribution
+* :ghpull:`28305`: Backport PR #28303 on branch v3.9.x (Removed drawedges repeated definition from function doc string)
+* :ghpull:`28303`: Removed drawedges repeated definition from function doc string
+* :ghpull:`28299`: Backport PR #28297 on branch v3.9.x (Solved #28296 Added missing comma)
+* :ghpull:`28297`: Solved #28296 Added missing comma
+* :ghpull:`28294`: Backport PR #28261 on branch v3.9.x (Correct roll angle units, issue #28256)
+* :ghpull:`28261`: Correct roll angle units, issue #28256
+* :ghpull:`28283`: Backport PR #28280 on branch v3.9.x (DOC: Add an example for 2D images in 3D plots)
+* :ghpull:`28280`: DOC: Add an example for 2D images in 3D plots
+* :ghpull:`28278`: Backport PR #28272 on branch v3.9.x (BLD: Move macos builders from 11 to 12)
+* :ghpull:`28277`: Backport PR #28274 on branch v3.9.x (ci: Remove deprecated codeql option)
+* :ghpull:`28272`: BLD: Move macos builders from 11 to 12
+* :ghpull:`28274`: ci: Remove deprecated codeql option
+* :ghpull:`28270`: Backport PR #28269 on branch v3.9.x (Handle GetForegroundWindow() returning NULL.)
+* :ghpull:`28269`: Handle GetForegroundWindow() returning NULL.
+* :ghpull:`28266`: Backport PR #28257 on branch v3.9.x (Clean up some Meson-related leftovers)
+* :ghpull:`28257`: Clean up some Meson-related leftovers
+* :ghpull:`28255`: Backport PR #28254 on branch v3.9.x ([DOC] plot type heading consistency)
+* :ghpull:`28254`: [DOC] plot type heading consistency
+* :ghpull:`28253`: Backport PR #28252 on branch v3.9.x (DOC: Flip the imshow plot types example to match the other examples)
+* :ghpull:`28252`: DOC: Flip the imshow plot types example to match the other examples
+* :ghpull:`28247`: Backport PR #28230 on branch v3.9.x (Add extra imports to improve typing)
+* :ghpull:`28230`: Add extra imports to improve typing
+* :ghpull:`28246`: Backport PR #28243 on branch v3.9.x (DOC: Add more 3D plot types)
+* :ghpull:`28243`: DOC: Add more 3D plot types
+* :ghpull:`28241`: Backport PR #28219 on branch v3.9.x (Bump the actions group with 2 updates)
+* :ghpull:`28219`: Bump the actions group with 2 updates
+* :ghpull:`28237`: Backport PR #28233 on branch v3.9.x (CI: Fix font install on macOS/Homebrew)
+* :ghpull:`28236`: Backport PR #28231 on branch v3.9.x (DOC: we do not need the blit call in on_draw)
+* :ghpull:`28233`: CI: Fix font install on macOS/Homebrew
+* :ghpull:`28231`: DOC: we do not need the blit call in on_draw
-Issues (97):
+Issues (30):
-* :ghissue:`28202`: [Bug]: Qt test_ipython fails on older ipython
-* :ghissue:`28145`: [TST] Upcoming dependency test failures
-* :ghissue:`28034`: [TST] Upcoming dependency test failures
-* :ghissue:`28168`: [TST] Upcoming dependency test failures
-* :ghissue:`28040`: [Bug]: vertical_axis not respected when rotating plots interactively
-* :ghissue:`28146`: [Bug]: Useless recursive group in SVG output when using path_effects
-* :ghissue:`28067`: [Bug]: ``LinearSegmentedColormap.from_list`` does not have all type hints for argument ``colors``
-* :ghissue:`26778`: [MNT]: Numpy 2.0 support strategy
-* :ghissue:`28020`: [Bug]: imsave fails on RGBA data when origin is set to lower
-* :ghissue:`7720`: WXAgg backend not rendering nicely on retina
-* :ghissue:`28069`: [Bug]: Cant save with custom toolbar
-* :ghissue:`28005`: [Doc]: Improve contribute instructions
-* :ghissue:`22376`: [ENH]: align_titles
-* :ghissue:`5506`: Confusing status bar values in presence of multiple axes
-* :ghissue:`4284`: Twin axis message coordinates
-* :ghissue:`18940`: WxAgg backend draws the wrong size when wxpython app is high DPI aware on Windows
-* :ghissue:`27792`: [ENH]: Legend entries for boxplot
-* :ghissue:`27828`: [Bug]: ".C10" does not work as plot shorthand format spec
-* :ghissue:`27911`: redirect not working for updated contribute page
-* :ghissue:`21876`: [Doc]: redirect-from directive appears broken?
-* :ghissue:`27941`: [Bug]: ShrinkA and ShrinkB are ignored in ax.annotate(arrowprops=...)
-* :ghissue:`26477`: [ENH]: Add interpolation_stage selector for images in qt figureoptions
-* :ghissue:`363`: Enable hatches for Cairo backend
-* :ghissue:`27852`: [Bug]: matplotlib.pyplot.matshow "(first dimension of the array) are displayed horizontally" but are displayed vertically
-* :ghissue:`27400`: [Bug]: tk backend confused by presence of file named "move" in current working directory
-* :ghissue:`25882`: [Bug]: plt.hist takes significantly more time with torch and jax arrays
-* :ghissue:`25204`: [Bug]: Pyparsing warnings emitted in mathtext
-* :ghissue:`17707`: getpwuid(): uid not found: 99
-* :ghissue:`27896`: [Doc]: Empty "User guide tutorials page" in docs
-* :ghissue:`27824`: [Bug]: polygon from axvspan not correct in polar plot after set_xy
-* :ghissue:`27378`: [ENH]: Suggest 'CN' if color is an integer
-* :ghissue:`27843`: [Bug]: close_group is not called when using patheffects
-* :ghissue:`27839`: [Bug]: PathCollection using alpha ignores 'none' facecolors
-* :ghissue:`25119`: [ENH]: secondary_x/yaxis accept transform argument
-* :ghissue:`27876`: [Doc]: Fix version switcher in devdocs
-* :ghissue:`27301`: [Bug]: ``imshow`` allows RGB(A) images with ``np.nan`` values to pass
-* :ghissue:`23839`: [MNT]: Add tests to codify ``ax.clear``
-* :ghissue:`27652`: [Doc]: Low contrast on clicked links in dark mode
-* :ghissue:`27865`: [Bug]: Zoom und pan not working after writing pdf pages.
-* :ghissue:`25871`: [Bug]: Colorbar cannot be added to another figure
-* :ghissue:`8072`: plot_date() ignores timezone in matplotlib version 2.0.0
-* :ghissue:`27812`: [ENH]: Add split feature for violin plots
-* :ghissue:`27659`: [MNT]: Improve return type of ``ioff`` and ``ion`` to improve Pyright analysis of bound variables
-* :ghissue:`27805`: [Bug]: Saving a figure with indicate_inset_zoom to pdf and then pickling it causes TypeError
-* :ghissue:`27701`: [Bug]: axis set_xscale('log') interferes with set_xticks
-* :ghissue:`19807`: radius modification in contains_point function when linewidth is specified
-* :ghissue:`27762`: [Bug]: Inconsistent treatment of list of labels in ``plot`` when the input is a dataframe
-* :ghissue:`27745`: [MNT]: ``_ImageBase.draw`` and ``Axis.draw`` args and kwargs
-* :ghissue:`27782`: [Doc]: Link to citation page in read me broken
-* :ghissue:`8789`: legend handle size does not automatically scale with linewidth
-* :ghissue:`27746`: [Doc]: Citation link in the readme.md points to 404
-* :ghissue:`20853`: Add deprecation for colormaps
-* :ghissue:`26865`: [MNT]: Remove 3.7-deprecated API
-* :ghissue:`24168`: [Bug]: ``subprocess-exited-with-error`` when trying to build on M1 mac
-* :ghissue:`27727`: [Doc]: Text in the colormap normalization gallery doesn't match the code
-* :ghissue:`27635`: [Bug]: test_figure_leak_20490 repeatedly failing on CI
-* :ghissue:`14217`: [Feature request] Add a way to update the position of the Arrow patch.
-* :ghissue:`20512`: Bad boxplot legend entries
-* :ghissue:`22011`: [Bug]: subfigures messes up with fig.legend zorder
-* :ghissue:`27414`: [Bug]: Legend overlaps shaded area in fill_between with legend location "best"
-* :ghissue:`23323`: Legend with "loc=best" does not try to avoid text
-* :ghissue:`27648`: [Doc]: ``Axes.inset_axes`` is still experimental
-* :ghissue:`27277`: [Doc]: Two license pages in docs
-* :ghissue:`24648`: [Doc]: make html fail early if latex not present
-* :ghissue:`27554`: [Bug]: Large image draw performance deterioration in recent releases
-* :ghissue:`25239`: [Bug]: colors.PowerNorm results in incorrect colorbar
-* :ghissue:`13533`: Boxplotting Masked Arrays
-* :ghissue:`25967`: [Doc]: dollar_ticks example refers to unused formatter classes
-* :ghissue:`24859`: [Doc]: Document color in a consistent way, including link
-* :ghissue:`27159`: [Bug]: Meson build fails due to qhull link issue.
-* :ghissue:`25691`: [Bug]: Secondary axis does not support Transform as functions
-* :ghissue:`25860`: [Bug]: canvas pick events not working when Axes belongs to a subfigure
-* :ghissue:`27361`: [Bug]: (Tight) layout engine breaks for 3D patches
-* :ghissue:`27145`: [ENH]: Make "No artists with labels found to put in legend" a warning
-* :ghissue:`27399`: [Bug]: None in y or yerr arrays leads to TypeError when using errorbar
-* :ghissue:`13887`: Accessing default ``norm`` of a Collection removes its colors.
-* :ghissue:`26593`: [ENH]: Support SubFigure.remove()
-* :ghissue:`27329`: [Bug]: Removing a colorbar for an axes positioned in a subgridspec restores the axes' position to the wrong place.
-* :ghissue:`27214`: [Bug]: ``NavigationToolbar2QT`` should use ``@Slot`` annotation
-* :ghissue:`27146`: [ENH]: Multi hatching in ``ax.stackplot()``
-* :ghissue:`27168`: [Doc]: Instructions for editable installation on Windows potentially missing a step
-* :ghissue:`27174`: [MNT]: Build nightly wheels with NumPy nightly wheels
-* :ghissue:`25043`: [ENH]: Plotting masked arrays correctly in 3D line plot
-* :ghissue:`26990`: [Doc]: Histogram path example renders poorly in HTML
-* :ghissue:`25738`: [MNT]: Improve readability of _mathtext_data.stix_virtual_fonts table
-* :ghissue:`11129`: Highlight past winners for JDH plotting contest in docs
-* :ghissue:`24050`: No error message in matplotlib.axes.Axes.legend() if there are more labels than handles
-* :ghissue:`10922`: ENH: clear() method for widgets.RadioButtons
-* :ghissue:`18295`: How to modify ticklabels in axisartist?
-* :ghissue:`24996`: [Bug]: for non-rectilinear axes, axvline/axhline should behave as "draw a gridline at that x/y"
-* :ghissue:`26841`: [Bug]: Global legend weird behaviors
-* :ghissue:`25974`: [MNT]: Cleanup ticklabel_format(..., style=)
-* :ghissue:`26786`: Please upload new dev wheel so we pick up 3.9.dev after 3.8 release
-* :ghissue:`18052`: the limits of axes are inexact with mplot3d
-* :ghissue:`25596`: [MNT]: Consistency on Interface
-* :ghissue:`26557`: [ENH]: Nightly Python 3.12 builds
-* :ghissue:`26281`: [ENH]: Add get_xmargin, get_ymargin, get_zmargin axes methods
+* :ghissue:`22482`: [ENH]: pickle (or save) matplotlib figure with insteractive slider
+* :ghissue:`25847`: [Bug]: Graph gets cut off with scaled resolution using gtk4cairo backend
+* :ghissue:`28341`: [Bug]: Incorrect X-axis scaling with date values
+* :ghissue:`28383`: [Bug]: axvspan no longer participating in limit calculations
+* :ghissue:`28223`: [Bug]: Inconsistent Visualization of Intervals in ax.barh for Different Duration Widths
+* :ghissue:`28432`: [Bug]: Backend name specified as module gets lowercased since 3.9
+* :ghissue:`28467`: [Bug]: Incorrect type stub for ``ErrorbarContainer``'s ``lines`` attribute.
+* :ghissue:`28384`: [Bug]: subfigure artists not drawn interactively
+* :ghissue:`28234`: [Bug]: mpltype custom role breaks sphinx build for third-party projects that have intersphinx links to matplotlib
+* :ghissue:`28464`: [Bug]: figure with subfigures cannot be pickled
+* :ghissue:`28448`: [Bug]: Making an RGB image from pickled data throws error
+* :ghissue:`23317`: [Bug]: ``add_collection3d`` does not update view limits
+* :ghissue:`17130`: autoscale_view is not working with Line3DCollection
+* :ghissue:`28434`: [Bug]: Setting exactly 2 colors with tuple in ``plot`` method gives confusing error
+* :ghissue:`28417`: [Doc]: axhspan and axvspan now return Rectangles, not Polygons.
+* :ghissue:`28378`: [ENH]: Switch text wrapping boundary to subfigure
+* :ghissue:`28404`: [Doc]: matplotlib.widgets.CheckButtons no longer has .rectangles attribute, needs removed.
+* :ghissue:`28367`: [Bug]: Backend entry points can be erroneously duplicated
+* :ghissue:`28358`: [Bug]: Labels don't get wrapped when set_yticks() is used in subplots
+* :ghissue:`28374`: [Bug]: rcParam ``tk.window_focus: True`` is causes crash on Linux in version 3.9.0.
+* :ghissue:`28324`: [Bug]: show(block=False) freezes
+* :ghissue:`28239`: [Doc]: Gallery example showing 3D slice planes
+* :ghissue:`27603`: [Bug]: _raw_ticker() istep
+* :ghissue:`24328`: [Bug]: class Axes3D.set_box_aspect() sets wrong aspect ratios when Axes3D.view_init( vertical_axis='y') is enabled.
+* :ghissue:`28221`: [Doc]: drawedges attribute described twice in matplotlib.colorbar documentation
+* :ghissue:`28296`: [Doc]: Missing comma
+* :ghissue:`28256`: [Bug]: axes3d.py's _on_move() converts the roll angle to radians, but then passes it to view_init() as if it were still in degrees
+* :ghissue:`28267`: [Bug]: for Python 3.11.9 gor error ValueError: PyCapsule_New called with null pointer
+* :ghissue:`28022`: [Bug]: Type of Axes is unknown pyright
+* :ghissue:`28002`: Segfault from path editor example with QtAgg
Previous GitHub statistics
diff --git a/doc/users/prev_whats_new/github_stats_3.9.0.rst b/doc/users/prev_whats_new/github_stats_3.9.0.rst
new file mode 100644
index 000000000000..b1d229ffbfa1
--- /dev/null
+++ b/doc/users/prev_whats_new/github_stats_3.9.0.rst
@@ -0,0 +1,744 @@
+.. _github-stats-3-9.0:
+
+GitHub statistics for 3.9.0 (May 15, 2024)
+==========================================
+
+GitHub statistics for 2023/09/15 (tag: v3.8.0) - 2024/05/15
+
+These lists are automatically generated, and may be incomplete or contain duplicates.
+
+We closed 97 issues and merged 450 pull requests.
+The full list can be seen `on GitHub `__
+
+The following 175 authors contributed 2584 commits.
+
+* 0taj
+* Abdul Razak Taha
+* Adam J. Stewart
+* Adam Turner
+* Aditi Gautam
+* agautam478
+* Alan Lau
+* Albert Y. Shih
+* Alec Vercruysse
+* Alexander Volkov
+* Alice Descoeudres
+* Allan Haldane
+* Amirreza Aflakparast
+* Ananya Devarakonda
+* ananya314
+* Anja Beck
+* Anjini2004
+* Ant Lockyer
+* Antony Lee
+* Anvi Verma
+* Artyom Romanov
+* Augusto Borges
+* avramid9
+* Ben Root
+* bersbersbers
+* Binaya Sharma
+* Cameron
+* Chaoyi Hu
+* chaoyihu
+* Chiraag Balu
+* Christoph Hasse
+* ConstableCatnip
+* CozyFrog
+* Cyril Gadal
+* Dale Dai
+* Daniel Bergman
+* Daniel Hitchcock
+* danielcobej
+* David Gilbertson
+* David Stansby
+* ddale1128@gmail.com
+* dependabot[bot]
+* Devilsaint
+* dohyun
+* Drew Kinneer
+* DWesl
+* Elisa Heckelmann
+* ElisaHeck
+* Elliott Sales de Andrade
+* Eric Firing
+* Eric Prestat
+* esibinga
+* Eva Sibinga
+* Evgenii Radchenko
+* Faisal Fawad
+* Felipe Cybis Pereira
+* Garrett Sward
+* Gaurav-Kumar-Soni
+* Gauri Chaudhari
+* Gautam Sagar
+* Greg Lucas
+* Gurudatta Shanbhag
+* hannah
+* Haoying Zhang
+* Hugues Hoppe
+* i-jey
+* iamfaham
+* Ian Hunt-Isaak
+* Ian Thomas
+* ifEricReturnTrue
+* Illviljan
+* Issam
+* Issam Arabi
+* Jacob Stevens-Haas
+* Jacob Tomlinson
+* Jake
+* Jake Stevens-Haas
+* James Salsman
+* Jaroza727
+* Jeremy Farrell
+* Jirka
+* Jody Klymak
+* Jorge Moraleda
+* Joshua Stevenson
+* jovianw
+* João Andrade
+* jpgianfaldoni
+* jsdodge
+* jsjeelshah
+* judfs
+* Juhan Oskar Hennoste
+* Junpei Ota
+* Katherine Turk
+* katotaisei
+* KheshavKumar
+* Koustav Ghosh
+* Kritika Verma
+* Kyle Sunden
+* Linyi Li
+* linyilily
+* lkkmpn
+* Lucia Korpas
+* madisonwong210
+* Maggie Liu
+* Marc Bresson
+* Matthew Feickert
+* Matthew Morrison
+* Matthias Bussonnier
+* Melissa Weber Mendonça
+* melissawm
+* mliu08
+* Mostafa Noah
+* MostafaNouh0011
+* n-aswin
+* Nabil
+* nbarlowATI
+* Nidaa Rabah
+* Nivedita Chaudhari
+* Oscar Gustafsson
+* patel-zeel
+* Pavel Liavonau
+* Pedro
+* Pedro Peçanha
+* Peter Talley
+* Pradeep Reddy Raamana
+* Prajwal Agrawal
+* Pranav Raghu
+* prateetishah
+* pre-commit-ci[bot]
+* QuadroTec
+* Rafael Tsuha
+* Raghuram Sirigiri
+* Raphael
+* Raphael Quast
+* Ratnabali Dutta
+* rawwash
+* rsp2210
+* Ruoyi
+* Ruoyi Xie
+* Rushikesh Pandya
+* Ruth Comer
+* samGreer
+* Samuel Diebolt
+* saranti
+* Scott Shambaugh
+* Sebastian Berg
+* Seohyeon Lee
+* Sheepfan0828
+* ShivamPathak99
+* Shriya Kalakata
+* shriyakalakata
+* Stefan
+* Steffen Rehberg
+* stevezhang1999
+* Sudhanshu Pandey
+* Talha Irfan
+* thehappycheese
+* Thomas A Caswell
+* Tiago Lubiana
+* Tim Hoffmann
+* tobias
+* Tom Sarantis
+* trananso
+* turnipseason
+* tusharkulkarni008
+* UFEddy
+* Vashesh08
+* vicky6
+* vigneshvetrivel8
+* wemi3
+* yangyangdotcom
+* YiLun Fan
+* Zach Champion
+* zachjweiner
+* zoehcycy
+
+GitHub issues and pull requests:
+
+Pull Requests (450):
+
+* :ghpull:`28206`: Backport PR #28205 on branch v3.9.x (TST: Fix tests with older versions of ipython)
+* :ghpull:`28207`: TST: Followup corrections to #28205
+* :ghpull:`28205`: TST: Fix tests with older versions of ipython
+* :ghpull:`28203`: Backport PR #28164 on branch v3.9.x (CI: Ensure code coverage is always uploaded)
+* :ghpull:`28204`: Backport PR #28195 on branch v3.9.x (TST: Prepare for pytest 9)
+* :ghpull:`28191`: DOC: Use released mpl-sphinx-theme on v3.9.x
+* :ghpull:`28195`: TST: Prepare for pytest 9
+* :ghpull:`28193`: Backport PR #28185 on branch v3.9.x (DOC: Bump mpl-sphinx-theme to 3.9)
+* :ghpull:`28190`: Backport PR #28103 on branch v3.9.x ([DOC]: Fix compatibility with sphinx-gallery 0.16)
+* :ghpull:`28164`: CI: Ensure code coverage is always uploaded
+* :ghpull:`28194`: Backport PR #28188 on branch v3.9.x ([TST] Bump some tolerances for Macos ARM)
+* :ghpull:`28188`: [TST] Bump some tolerances for Macos ARM
+* :ghpull:`28185`: DOC: Bump mpl-sphinx-theme to 3.9
+* :ghpull:`28189`: Backport PR #28181 on branch v3.9.x (DOC: Prepare release notes for 3.9)
+* :ghpull:`28103`: [DOC]: Fix compatibility with sphinx-gallery 0.16
+* :ghpull:`28181`: DOC: Prepare release notes for 3.9
+* :ghpull:`28184`: Backport PR #28182 on branch v3.9.x (Bump custom hatch deprecation expiration)
+* :ghpull:`28182`: Bump custom hatch deprecation expiration
+* :ghpull:`28178`: Backport PR #28171 on branch v3.9.x (Support removing absent tools from ToolContainerBase.)
+* :ghpull:`28171`: Support removing absent tools from ToolContainerBase.
+* :ghpull:`28174`: Backport PR #28169 on branch v3.9.x (Clarify public-ness of some ToolContainerBase APIs.)
+* :ghpull:`28169`: Clarify public-ness of some ToolContainerBase APIs.
+* :ghpull:`28160`: Backport PR #28039 on branch v3.9.x (Respect vertical_axis when rotating plot interactively)
+* :ghpull:`28159`: Backport PR #28157 on branch v3.9.x (Remove call to non-existent method _default_contains in Artist)
+* :ghpull:`28162`: Backport PR #27948 on branch v3.9.x (Move IPython backend mapping to Matplotlib and support entry points)
+* :ghpull:`28163`: Backport PR #28144 on branch v3.9.x (DOC: Refactor code in the fishbone diagram example)
+* :ghpull:`28144`: DOC: Refactor code in the fishbone diagram example
+* :ghpull:`27948`: Move IPython backend mapping to Matplotlib and support entry points
+* :ghpull:`28039`: Respect vertical_axis when rotating plot interactively
+* :ghpull:`28157`: Remove call to non-existent method _default_contains in Artist
+* :ghpull:`28141`: Backport PR #27960 on branch v3.9.x (Update AppVeyor config)
+* :ghpull:`28138`: Backport PR #28068 on branch v3.9.x ([TYP] Add possible type hint to ``colors`` argument in ``LinearSegmentedColormap.from_list``)
+* :ghpull:`28140`: Backport PR #28136 on branch v3.9.x (Appease pycodestyle.)
+* :ghpull:`27960`: Update AppVeyor config
+* :ghpull:`28068`: [TYP] Add possible type hint to ``colors`` argument in ``LinearSegmentedColormap.from_list``
+* :ghpull:`28136`: Appease pycodestyle.
+* :ghpull:`28135`: Backport PR #28134 on branch v3.9.x (DOC: Minor improvements on quickstart)
+* :ghpull:`28134`: DOC: Minor improvements on quickstart
+* :ghpull:`28121`: Backport PR #28085 on branch v3.9.x (Clarify that the pgf backend is never actually used interactively.)
+* :ghpull:`28120`: Backport PR #28102 on branch v3.9.x (Fix typo in color mapping documentation in quick_start.py)
+* :ghpull:`28109`: Backport PR #28100 on branch v3.9.x (TST: wxcairo sometimes raises OSError on missing cairo libraries)
+* :ghpull:`28100`: TST: wxcairo sometimes raises OSError on missing cairo libraries
+* :ghpull:`28108`: Backport PR #28107 on branch v3.9.x ([DOC] Fix description in CapStyle example)
+* :ghpull:`28107`: [DOC] Fix description in CapStyle example
+* :ghpull:`28102`: Fix typo in color mapping documentation in quick_start.py
+* :ghpull:`28095`: Backport PR #28094 on branch v3.9.x (DOC: exclude sphinx 7.3.*)
+* :ghpull:`28081`: Backport PR #28078 on branch v3.9.x (Clarify that findfont & _find_fonts_by_props return paths.)
+* :ghpull:`28080`: Backport PR #28077 on branch v3.9.x (Parent tk StringVar to the canvas widget, not to the toolbar.)
+* :ghpull:`28092`: Backport PR #28032 on branch v3.9.x (FIX: ensure images are C order before passing to pillow)
+* :ghpull:`28032`: FIX: ensure images are C order before passing to pillow
+* :ghpull:`28088`: Backport PR #28087 on branch v3.9.x (Document Qt5 minimal version.)
+* :ghpull:`28085`: Clarify that the pgf backend is never actually used interactively.
+* :ghpull:`28078`: Clarify that findfont & _find_fonts_by_props return paths.
+* :ghpull:`28077`: Parent tk StringVar to the canvas widget, not to the toolbar.
+* :ghpull:`28062`: Backport PR #28056 on branch v3.9.x (Strip trailing spaces from log-formatter cursor output.)
+* :ghpull:`28063`: Backport PR #28055 on branch v3.9.x (DOC: Improve inverted axis example)
+* :ghpull:`28056`: Strip trailing spaces from log-formatter cursor output.
+* :ghpull:`28049`: Backport PR #28036 on branch v3.9.x (BLD: Fetch version from setuptools_scm at build time)
+* :ghpull:`28036`: BLD: Fetch version from setuptools_scm at build time
+* :ghpull:`28038`: Backport PR #28023 on branch v3.9.x (ci: Update merge conflict labeler)
+* :ghpull:`28023`: ci: Update merge conflict labeler
+* :ghpull:`28035`: Backport PR #28026 on branch v3.9.x ([DOC] reshuffle of contributing)
+* :ghpull:`28026`: [DOC] reshuffle of contributing
+* :ghpull:`28024`: DOC: Rewrite "Work on an issue" section
+* :ghpull:`28011`: DOC: Move bug reports and feature requests to top of contributing index
+* :ghpull:`27747`: Move doc/users/installing/ to doc/install/
+* :ghpull:`27952`: ENH: Align titles
+* :ghpull:`28017`: Merge up v3.8.4
+* :ghpull:`28014`: Improve timeline example.
+* :ghpull:`28019`: DOC: correct path to mpl_toolkits reference images
+* :ghpull:`26981`: Fixes Issue #26377 - Auto-escape % Symbol in Latex in pie labels
+* :ghpull:`28007`: wx: Fix file extension for toolmanager-style toolbar
+* :ghpull:`25556`: Display cursor coordinates for all axes twinned with the current one.
+* :ghpull:`23597`: Always use PyQT/PySide6 for GitHub CI
+* :ghpull:`28013`: Avoid plt.xticks/plt.yticks in gallery examples.
+* :ghpull:`28006`: Fix deprecation warnings in ft2font extension
+* :ghpull:`27723`: ci: Enable testing on M1 macOS
+* :ghpull:`26375`: Add ``widths``, ``heights`` and ``angles`` setter to ``EllipseCollection``
+* :ghpull:`27999`: Remove documentation that some backends don't support hatching.
+* :ghpull:`26710`: Add support for High DPI displays to wxAgg backend
+* :ghpull:`27148`: Correctly treat pan/zoom events of overlapping axes.
+* :ghpull:`27981`: DOC: Fix label type specification in parameter descriptions
+* :ghpull:`27979`: Clarify error message for bad-dimensionality in pcolorfast().
+* :ghpull:`27962`: DOC: Document axes_grid1.Grid attributes
+* :ghpull:`27968`: MNT: Remove remaining 3.7 deprecations
+* :ghpull:`27965`: DOC: Rewrite the example illustrating bxp()
+* :ghpull:`26453`: add documentation for reloading font cache
+* :ghpull:`26131`: Tst/restore old tests
+* :ghpull:`27730`: Add an rcparam for image.interpolation_stage.
+* :ghpull:`27956`: Use PyOS_setsig in macos backend
+* :ghpull:`27829`: Simplify color/marker disambiguation logic in _process_plot_format.
+* :ghpull:`27840`: Add legend support for boxplots
+* :ghpull:`27943`: Support Cn, n>9 in plot() shorthand format.
+* :ghpull:`27950`: ci: Fix condition for publishing wheels
+* :ghpull:`27909`: Add a note to pyplot docstrings referencing the corresponding object methods
+* :ghpull:`27929`: DOC: Add summary lines to plot types
+* :ghpull:`27915`: [BUG] Fix redirect-from Sphinx extension
+* :ghpull:`27945`: DOC: Explain leading dot in object references
+* :ghpull:`27947`: Update docs for ``FancyArrowPatch`` & ``Annotation`` to make it clear that ShrinkA/B parameters are in points and not fractional.
+* :ghpull:`27944`: Bump the actions group with 2 updates
+* :ghpull:`27932`: Fix pickling of make_axes_area_auto_adjustable'd axes.
+* :ghpull:`26500`: closes #26477 ENH: Add interpolation_stage in qt figureoptions
+* :ghpull:`27927`: Update docs
+* :ghpull:`27916`: Revert renaming labels to tick_labels in boxplot_stats()
+* :ghpull:`27931`: Highlight development_setup code snippets as bash, not python.
+* :ghpull:`27856`: Support hatching in cairo backends.
+* :ghpull:`27922`: Fix cbook style
+* :ghpull:`27668`: MNT: prevent merging using labels + branch protection rules
+* :ghpull:`27857`: Documentation edit for matshow function
+* :ghpull:`27928`: DOC: Fix syntax for ToolBase.image docstring
+* :ghpull:`27873`: Simplify the LineCollection example
+* :ghpull:`27492`: Fix semantics of MEP22 image names.
+* :ghpull:`27918`: Fix new flake8 errors from old merge
+* :ghpull:`27874`: Modernize macosx backend a bit
+* :ghpull:`25887`: Update ``_unpack_to_numpy`` function to convert JAX and PyTorch arrays to NumPy
+* :ghpull:`27685`: Work around pyparsing diagnostic warnings
+* :ghpull:`26594`: Added optional props argument to Lasso Widget __init__ to customize Lasso line
+* :ghpull:`22761`: Add minor ticks on and off in Axis
+* :ghpull:`22407`: Add ``set_XY`` and ``set_data`` to ``Quiver``
+* :ghpull:`27901`: Rename boxplot's tick label parameter
+* :ghpull:`27883`: Fix build on older macOS deployment targets
+* :ghpull:`27900`: Remove empty user guide tutorials page
+* :ghpull:`27885`: Clean up headers in extensions
+* :ghpull:`27910`: DOC: Fix dead link in README
+* :ghpull:`26567`: Use SVG inheritance diagrams now that linking has been fixed
+* :ghpull:`27899`: Merge up 3.8.x into main
+* :ghpull:`27905`: Improved error message for malformed colors
+* :ghpull:`27906`: Override open_group, close_group methods in PathEffectRenderer
+* :ghpull:`27904`: FIX: Restore D213 in flake8
+* :ghpull:`27895`: Remove versions from sidebar in docs
+* :ghpull:`27894`: Mark triangulation classes as final
+* :ghpull:`27557`: Use :mpltype:``color`` for color types
+* :ghpull:`27845`: Make sure custom alpha param does not change 'none' colors in a list of colors
+* :ghpull:`27719`: Add BackendRegistry singleton class
+* :ghpull:`27890`: DOC: State approximate documentation build time
+* :ghpull:`27887`: BLD: Add a fallback URL for FreeType
+* :ghpull:`25224`: Allow passing a transformation to secondary_xaxis/_yaxis
+* :ghpull:`27886`: Fix devdocs version switcher
+* :ghpull:`27884`: FIX: don't copy twice on RGB input
+* :ghpull:`27087`: Convert path extension to pybind11
+* :ghpull:`27867`: DOC: Update some animation related topics
+* :ghpull:`27848`: FIX: handle nans in RGBA input with ScalarMappables
+* :ghpull:`27821`: BLD,Cygwin: Include Python.h first in various C++ files
+* :ghpull:`27457`: TST: adding tests of current clear behavior on ticks
+* :ghpull:`27872`: doc: add description of ``**kwargs`` usage to collections
+* :ghpull:`27868`: Use pybind11 string formatter for exception messages
+* :ghpull:`27862`: Add dtype/copy args to internal testing class
+* :ghpull:`27658`: Bump pydata-sphinx-theme
+* :ghpull:`27303`: FIX: also exclude np.nan in RGB(A) in color mapping
+* :ghpull:`27860`: Bump the actions group with 2 updates
+* :ghpull:`27869`: Correctly set temporary pdf/pgf backends
+* :ghpull:`27850`: Deprecate ``plot_date``
+* :ghpull:`27815`: Add side option to violinplot
+* :ghpull:`27836`: DOC: use ... for continuation prompt in docstrings
+* :ghpull:`27819`: MNT: remove draw method args and kwargs
+* :ghpull:`27813`: DOC: Update violinplot() docs
+* :ghpull:`27698`: Add linting and validation of all YAML files
+* :ghpull:`27811`: Fix Annulus width check
+* :ghpull:`27667`: Change return type of ``ion`` and ``ioff`` to fix unbound variable errors with Pyright
+* :ghpull:`27807`: Expand CI pytest reporting config to ignore xfails
+* :ghpull:`27806`: Remove self._renderer from AnnotationBbox and ConnectionPatch
+* :ghpull:`27799`: Clarify that set_ticks() affects major/minor ticks independently
+* :ghpull:`27787`: Improve documentation on boxplot and violinplot
+* :ghpull:`27800`: Deactivate sidebar for release notes
+* :ghpull:`27798`: Fix sphinx-gallery CSS
+* :ghpull:`27462`: DOC: clarify the default value of *radius* in Patch.contains_point
+* :ghpull:`27565`: MNT: arghandling subplotspec
+* :ghpull:`27796`: Make mypy a bit stricter
+* :ghpull:`27767`: Update handling of sequence labels for plot
+* :ghpull:`27795`: Add EffVer badge
+* :ghpull:`27780`: Partly revert #27711
+* :ghpull:`27768`: MNT: deprecate draw method args and kwargs
+* :ghpull:`27783`: Update README.md to fix citation link
+* :ghpull:`27726`: TST: always set a (long) timeout for subprocess and always use our wrapper
+* :ghpull:`27781`: Simplify example: Box plots with custom fill colors
+* :ghpull:`27750`: Bump the actions group with 2 updates
+* :ghpull:`27771`: Add marker-only and line+marker visuals to the plot() plot types
+* :ghpull:`27764`: Increase size of legend in Legend guide example
+* :ghpull:`26800`: Bump minimum NumPy version to 1.23
+* :ghpull:`27752`: Update some Meson internals
+* :ghpull:`27702`: GOV: adopt EffVer
+* :ghpull:`26965`: Removal of deprecated API cm
+* :ghpull:`27758`: [Doc] Remove special casing for removed method
+* :ghpull:`25815`: [TST] Make jpl units instantiated with datetimes consistent with mpl converters
+* :ghpull:`27729`: DOC: Improve colormap normalization example
+* :ghpull:`27732`: TST: Remove memory leak test
+* :ghpull:`27733`: ci: Simplify CodeQL setup
+* :ghpull:`27692`: Add method to update position of arrow patch
+* :ghpull:`27736`: Fix incorrect API reference in docs
+* :ghpull:`27731`: DOC: Create explicit rename legend entry section in guide
+* :ghpull:`27560`: Moved /users/project to /doc/project
+* :ghpull:`27728`: Simplify Figure._suplabels.
+* :ghpull:`27715`: Bump the actions group with 3 updates
+* :ghpull:`27711`: Fix boxplot legend entries part 2
+* :ghpull:`27696`: DOC: clean up automated tests section of workflow docs
+* :ghpull:`27686`: Improve Locator docstrings
+* :ghpull:`27704`: ci: Remove prerelease conditions from Azure Pipelines
+* :ghpull:`27568`: Fix boxplot legend entries
+* :ghpull:`27694`: MNT: fix labeller
+* :ghpull:`26953`: MNT: test that table doesn't try to convert unitized data
+* :ghpull:`27690`: Remove "Past versions" section from release notes
+* :ghpull:`26926`: Closes #22011: Changes to SubFigures so it behaves like a regular artist
+* :ghpull:`27469`: Fixed legend with legend location "best" when legend overlaps shaded area and text
+* :ghpull:`27684`: Bump the actions group with 1 update
+* :ghpull:`27665`: Axes.inset_axes - warning message removed
+* :ghpull:`27688`: CI: skip code coverage upload on scheduled tests
+* :ghpull:`27689`: ci: Don't include API/what's new notes in general doc labels
+* :ghpull:`27640`: Add ``get_cursor_data`` to ``NonUniformImage``
+* :ghpull:`27676`: BLD: Downgrade FreeType to 2.6.1 on Windows ARM
+* :ghpull:`27619`: Use GH action to install reviewdog
+* :ghpull:`27552`: TST: Use importlib for importing in pytest
+* :ghpull:`27650`: DOC: Added call out to API guidelines to contribute + small API guidelines reorg
+* :ghpull:`27618`: Add option of running stubtest using tox
+* :ghpull:`27656`: Bump the actions group with 1 update
+* :ghpull:`27415`: Use class form of data classes
+* :ghpull:`27649`: Check for latex binary before building docs
+* :ghpull:`27641`: MNT: fix api changes link in PR template
+* :ghpull:`27644`: ci: Fix mpl_toolkits label
+* :ghpull:`27230`: Query macOS for available system fonts.
+* :ghpull:`27643`: ci: Update nightly upload for artifacts v4
+* :ghpull:`27642`: Fix auto-labeler configuration
+* :ghpull:`27639`: Doc: typo fix for #22699
+* :ghpull:`26978`: [pre-commit.ci] pre-commit autoupdate
+* :ghpull:`27563`: Enable PyPI publishing from GitHub Actions
+* :ghpull:`22699`: Proof of concept for adding kwdoc content to properties using a decorator
+* :ghpull:`27633`: Auto-label PRs based on changed files
+* :ghpull:`27607`: Error on bad input to hexbin extents
+* :ghpull:`27629`: Don't run CI twice on dependabot branches
+* :ghpull:`27562`: Avoid an extra copy/resample if imshow input has no alpha
+* :ghpull:`27628`: Bump the actions group with 2 updates
+* :ghpull:`27626`: CI: Group dependabot updates
+* :ghpull:`27589`: Don't clip PowerNorm inputs < vmin
+* :ghpull:`27613`: Fix marker validator with cycler (allow mix of classes)
+* :ghpull:`27615`: MNT: add spaces to PR template
+* :ghpull:`27614`: DOC: Updated link in annotation API docs to point to annotation user guide
+* :ghpull:`27605`: Ignore masked values in boxplot
+* :ghpull:`26884`: Remove deprecated code from _fontconfig_patterns
+* :ghpull:`27602`: Let FormatStrFormatter respect axes.unicode_minus.
+* :ghpull:`27601`: Clarify dollar_ticks example and FormatStrFormatter docs.
+* :ghpull:`24834`: Deprecate apply_theta_transforms=True to PolarTransform
+* :ghpull:`27591`: Use macOS instead of OSX in comments/docs
+* :ghpull:`27577`: MNT: add the running version to pickle warning message
+* :ghpull:`25191`: Deprecate 'prune' kwarg to MaxNLocator
+* :ghpull:`27566`: DOC: changed tag ``plot type`` to ``plot-type``
+* :ghpull:`27105`: Use Axes instead of axes core library code
+* :ghpull:`27575`: Add quotes round .[dev] in editable install command
+* :ghpull:`27104`: Use Axes instead of axes in galleries
+* :ghpull:`27373`: Transpose grid_finder tick representation.
+* :ghpull:`27363`: ci: Improve coverage for compiled code
+* :ghpull:`27200`: DOC: Add role for custom informal types like color
+* :ghpull:`27548`: DOC: typo fix in contribute doc
+* :ghpull:`27458`: Check if the mappable is in a different Figure than the one fig.color…
+* :ghpull:`27546`: MNT: Clean up some style exceptions
+* :ghpull:`27514`: Improve check for bbox
+* :ghpull:`27265`: DOC: reorganizing contributing docs to clean up toc, better separate topics
+* :ghpull:`27517`: Best-legend-location microoptimization
+* :ghpull:`27540`: Bump github/codeql-action from 2 to 3
+* :ghpull:`27520`: [Doc] Minor consistency changes and correction of Marker docs
+* :ghpull:`27505`: Download Qhull source from Github, not Qhull servers, in meson build
+* :ghpull:`27518`: Micro-optimizations related to list handling
+* :ghpull:`27495`: Bump actions/stale from 8 to 9
+* :ghpull:`27523`: Changes for stale GHA v9
+* :ghpull:`27519`: [Doc] Improve/correct docs for 3D
+* :ghpull:`27447`: TST: Compress some hist geometry tests
+* :ghpull:`27513`: Fix docs and add tests for transform and deprecate ``BboxTransformToMaxOnly``
+* :ghpull:`27511`: TST: Add tests for Affine2D
+* :ghpull:`27424`: Added Axes.stairs test in test_datetime.py
+* :ghpull:`27267`: Fix/restore secondary axis support for Transform-type functions
+* :ghpull:`27013`: Add test_contour under test_datetime.py
+* :ghpull:`27497`: Clarify that set_axisbelow doesn't move grids below images.
+* :ghpull:`27498`: Remove unnecessary del local variables at end of Gcf.destroy.
+* :ghpull:`27466`: Add test_eventplot to test_datetime.py
+* :ghpull:`25905`: Use annotate coordinate systems to simplify label_subplots.
+* :ghpull:`27471`: Doc: visualizing_tests and ``triage_tests`` tools
+* :ghpull:`27474`: Added smoke test for Axes.matshow to test_datetime.py
+* :ghpull:`27470`: Fix test visualization tool for non-PNG files
+* :ghpull:`27426`: DOC: normalizing histograms
+* :ghpull:`27452`: Cleanup unit_cube-methods
+* :ghpull:`27431`: Added test for Axes.bar_label
+* :ghpull:`26962`: Remove backend 3.7-deprecated API
+* :ghpull:`27410`: Add test_vlines to test_datetime.py
+* :ghpull:`27425`: Added test_fill_betweenx in test_datetime.py
+* :ghpull:`27449`: Remove test_quiverkey from test_datetime.py
+* :ghpull:`27427`: MNT/TST: remove xcorr and acorr from test_datetime
+* :ghpull:`27390`: Add test_bxp in test_datetime.py
+* :ghpull:`27428`: Added test for broken_barh to test_datetime.py
+* :ghpull:`27222`: [TST] Added test_annotate in test_datetime.py
+* :ghpull:`27135`: Added smoke test for Axes.stem
+* :ghpull:`27343`: Fix draggable annotations on subfigures.
+* :ghpull:`27033`: Add test_bar in test_datetime
+* :ghpull:`27423`: Add test for fill_between in test_datetime.py
+* :ghpull:`27409`: Fix setting ``_selection_completed`` in ``SpanSelector`` when spanselector is initialised using ``extents``
+* :ghpull:`27440`: Fix get_path for 3d artists
+* :ghpull:`27422`: TST: Cache available interactive backends
+* :ghpull:`27401`: Add test_fill in test_datetime.py
+* :ghpull:`27419`: DOC: Add AsinhScale to list of built-in scales
+* :ghpull:`27417`: Switch pytest fixture from tmpdir to tmp_path
+* :ghpull:`27172`: ENH: Change logging to warning when creating a legend with no labels
+* :ghpull:`27405`: Check that xerr/yerr values are not None in errorbar
+* :ghpull:`27392`: Remove test_spy from test_datetime.py
+* :ghpull:`27331`: Added smoke test for Axes.barbs in test_datetime.py
+* :ghpull:`27393`: MNT: Fix doc makefiles
+* :ghpull:`27387`: Revert "MNT: add _version.py to .gitignore"
+* :ghpull:`27347`: FIX: scale norm of collections when first array is set
+* :ghpull:`27374`: MNT: add _version.py to .gitignore
+* :ghpull:`19011`: Simplify tk tooltip setup.
+* :ghpull:`27367`: Fix _find_fonts_by_props docstring
+* :ghpull:`27359`: Fix build on PyPy
+* :ghpull:`27362`: Implement SubFigure.remove.
+* :ghpull:`27360`: Fix removal of colorbars on nested subgridspecs.
+* :ghpull:`27211`: Add test_hlines to test_datetimes.py
+* :ghpull:`27353`: Refactor AxisArtistHelpers
+* :ghpull:`27357`: [DOC]: Update 3d axis limits what's new
+* :ghpull:`26992`: Convert TkAgg utilities to pybind11
+* :ghpull:`27215`: Add ``@QtCore.Slot()`` decorations to ``NavigationToolbar2QT``
+* :ghpull:`26907`: Removal of deprecations for Contour
+* :ghpull:`27285`: Factor out common parts of qt and macos interrupt handling.
+* :ghpull:`27306`: Simplify GridSpec setup in make_axes_gridspec.
+* :ghpull:`27313`: FIX: allow re-shown Qt windows to be re-destroyed
+* :ghpull:`27184`: Use pybind11 for qhull wrapper
+* :ghpull:`26794`: Use pybind11 in _c_internal_utils module
+* :ghpull:`27300`: Remove idiosyncratic get_tick_iterator API.
+* :ghpull:`27275`: MAINT: fix .yml in tag issue template
+* :ghpull:`27288`: Use int.from_bytes instead of implementing the conversion ourselves.
+* :ghpull:`27286`: Various cleanups
+* :ghpull:`27279`: Tweak a few docstrings.
+* :ghpull:`27256`: merge up v3.8.1
+* :ghpull:`27254`: Remove redundant axes_grid colorbar examples.
+* :ghpull:`27251`: webagg: Don't resize canvas if WebSocket isn't connected
+* :ghpull:`27236`: Tagging Example - Tags for multiple figs demo
+* :ghpull:`27245`: MNT: be more careful in Qt backend that there is actually a Figure
+* :ghpull:`27158`: First attempt for individual hatching styles for stackplot
+* :ghpull:`26851`: Establish draft Tag glossary and Tagging guidelines
+* :ghpull:`27083`: DOC: Add tags infrastructure for gallery examples
+* :ghpull:`27204`: BLD: Use NumPy nightly wheels for non-release builds
+* :ghpull:`27208`: Add test_axvline to test_datetime.py
+* :ghpull:`26989`: MNT: print fontname in missing glyph warning
+* :ghpull:`27177`: Add test_axhline in test_datetime.py
+* :ghpull:`27164`: docs: adding explanation for color in ``set_facecolor``
+* :ghpull:`27175`: Deprecate mixing positional and keyword args for legend(handles, labels)
+* :ghpull:`27199`: DOC: clean up links under table formatting docs
+* :ghpull:`27185`: Added smoke tests for Axes.errorbar in test_datetime.py
+* :ghpull:`27091`: Add test_step to test_datetime.py
+* :ghpull:`27182`: Add example for plotting a bihistogram
+* :ghpull:`27130`: added test_axvspan in test.datetime.py
+* :ghpull:`27094`: MNT: move pytest.ini configs to .toml
+* :ghpull:`27139`: added test_axhspan in test_datetime.py
+* :ghpull:`27058`: DOC: concise dependency heading + small clarifications
+* :ghpull:`27053`: Added info for getting compilation output from meson on autorebuild
+* :ghpull:`26906`: Fix masking for Axes3D.plot()
+* :ghpull:`27142`: Added smoke test for Axes.text in test_datetime.py
+* :ghpull:`27024`: Add test_contourf in test_datetime.py
+* :ghpull:`22347`: correctly treat pan/zoom events of overlapping axes
+* :ghpull:`26900`: #26865 removing deprecations to axislines.py
+* :ghpull:`26696`: DOC: Fix colLoc default
+* :ghpull:`27064`: Close all plot windows of a blocking show() on Ctrl+C
+* :ghpull:`26882`: Add scatter test for datetime units
+* :ghpull:`27114`: add test_stackplot in test_datetime.py
+* :ghpull:`27084`: Add test_barh to test_datetime.py
+* :ghpull:`27110`: DOC: Move figure member sections one level down
+* :ghpull:`27127`: BLD: use python3 for shebang consistent with pep-394
+* :ghpull:`27111`: BLD: Fix setting FreeType build type in extension
+* :ghpull:`26921`: MNT: clarify path.sketch rcparam format + test validate_sketch
+* :ghpull:`27109`: TST: Use importlib for subprocess tests
+* :ghpull:`27119`: Update clabel comment.
+* :ghpull:`27117`: Remove datetime test for axes.pie
+* :ghpull:`27095`: Deprecate nth_coord parameter from FixedAxisArtistHelper.new_fixed_axis.
+* :ghpull:`27066`: Tweak array_view to be more like pybind11
+* :ghpull:`27090`: Restore figaspect() API documentation
+* :ghpull:`27074`: Issue #26990: Split the histogram image into two for each code block.
+* :ghpull:`27086`: Rename py namespace to mpl in extension code
+* :ghpull:`27082`: MAINT: Update environment.yml to match requirements files
+* :ghpull:`27072`: Remove datetime test stubs for spectral methods/table
+* :ghpull:`26830`: Update stix table with Unicode names
+* :ghpull:`26969`: DOC: add units to user/explain [ci doc]
+* :ghpull:`27028`: Added test_hist in test_datetime.py
+* :ghpull:`26876`: issue: 26871 - Remove SimplePath class from patches.py
+* :ghpull:`26875`: Fix Deprecation in patches.py
+* :ghpull:`26890`: Removing deprecated api from patches
+* :ghpull:`27037`: add test_plot_date in test_datetime.py
+* :ghpull:`27012`: Bump required C++ standard to c++17
+* :ghpull:`27021`: Add a section to Highlight past winners for JDH plotting contest in docs
+* :ghpull:`27004`: Warning if handles and labels have a len mismatch
+* :ghpull:`24061`: #24050 No error was thrown even number of handles mismatched labels
+* :ghpull:`26754`: DOC: separate and clarify axisartist default tables
+* :ghpull:`27020`: CI: Update scientific-python/upload-nightly-action to 0.2.0
+* :ghpull:`26951`: Clarify that explicit ticklabels are used without further formatting.
+* :ghpull:`26894`: Deprecate setting the timer interval while starting it.
+* :ghpull:`13401`: New clear() method for Radio and Check buttons
+* :ghpull:`23829`: Start transitioning to pyproject.toml
+* :ghpull:`26621`: Port build system to Meson
+* :ghpull:`26928`: [TYP] Add tool for running stubtest
+* :ghpull:`26917`: Deprecate ContourLabeler.add_label_clabeltext.
+* :ghpull:`26960`: Deprecate backend_ps.get_bbox_header, and split it for internal use.
+* :ghpull:`26967`: Minor cleanups.
+* :ghpull:`26909`: deprecated api tri
+* :ghpull:`26946`: Inline Cursor._update into its sole caller.
+* :ghpull:`26915`: DOC: Clarify description and add examples in colors.Normalize
+* :ghpull:`26874`: Cleaned up the span_where class method from Polycollections.
+* :ghpull:`26586`: Support standard formatters in axisartist.
+* :ghpull:`26788`: Fix axh{line,span} on polar axes.
+* :ghpull:`26935`: add tomli to rstcheck extras
+* :ghpull:`26275`: Use pybind11 in image module
+* :ghpull:`26887`: DOC: improve removal for julian dates [ci doc]
+* :ghpull:`26929`: DOC: Fix removal doc for Animation attributes
+* :ghpull:`26918`: 26865 Removed deprecations from quiver.py
+* :ghpull:`26902`: Fixed deprecated APIs in lines.py
+* :ghpull:`26903`: Simplify CheckButtons and RadioButtons click handler.
+* :ghpull:`26899`: MNT: only account for Artists once in fig.get_tightbbox
+* :ghpull:`26861`: QT/NavigationToolbar2: configure subplots dialog should be modal
+* :ghpull:`26885`: Removed deprecated code from gridspec.py
+* :ghpull:`26880`: Updated offsetbox.py
+* :ghpull:`26910`: Removed the deprecated code from offsetbox.py
+* :ghpull:`26905`: Add users/explain to default skip subdirs
+* :ghpull:`26853`: Widgets: Remove deprecations and make arguments keyword only
+* :ghpull:`26877`: Fixes deprecation in lines.py
+* :ghpull:`26871`: Removed the deprecated code from ``axis.py``
+* :ghpull:`26872`: Deprecated code removed in animation.py
+* :ghpull:`26859`: Add datetime testing skeleton
+* :ghpull:`26848`: ci: Don't install recommended packages on Circle
+* :ghpull:`26852`: Remove Julian date support
+* :ghpull:`26801`: [MNT]: Cleanup ticklabel_format (style=)
+* :ghpull:`26840`: Reduce redundant information in _process_plot_var_args.
+* :ghpull:`26731`: Explicitly set foreground color to black in svg icons
+* :ghpull:`26826`: [MNT] Move NUM_VERTICES from mplutils.h to the only file it is used in
+* :ghpull:`26742`: [TYP] Add typing for some private methods and modules
+* :ghpull:`26819`: Reorder safe_first_element() and _safe_first_finite() code
+* :ghpull:`26813`: Bump docker/setup-qemu-action from 2 to 3
+* :ghpull:`26797`: Remove deprecated draw_gouraud_triangle
+* :ghpull:`26815`: Remove plt.Axes from tests
+* :ghpull:`26818`: Fix doc build (alternative)
+* :ghpull:`26785`: merge up v3.8.0
+* :ghpull:`25272`: Do not add padding to 3D axis limits when limits are manually set
+* :ghpull:`26798`: Remove deprecated methods and attributed in Axes3D
+* :ghpull:`26744`: Use cbook methods for string checking
+* :ghpull:`26802`: specify input range in logs when image data must be clipped
+* :ghpull:`26787`: Remove unused Axis private init helpers.
+* :ghpull:`26629`: DOC: organize figure API
+* :ghpull:`26690`: Make generated pgf code more robust against later changes of tex engine.
+* :ghpull:`26577`: Bugfix: data sanitizing for barh
+* :ghpull:`26684`: Update PR template doc links
+* :ghpull:`26686`: PR template: shorten comment and pull up top
+* :ghpull:`26670`: Added sanitize_sequence to kwargs in _preprocess_data
+* :ghpull:`26634`: [MNT] Move SubplotParams from figure to gridspec
+* :ghpull:`26609`: Cleanup AutoMinorLocator implementation.
+* :ghpull:`26293`: Added get_xmargin(), get_ymargin() and get_zmargin() and tests.
+* :ghpull:`26516`: Replace reference to %pylab by %matplotlib.
+* :ghpull:`26483`: Improve legend(loc='best') warning and test
+* :ghpull:`26482`: [DOC]: print pydata sphinx/mpl theme versions
+* :ghpull:`23787`: Use pybind11 for C/C++ extensions
+
+Issues (97):
+
+* :ghissue:`28202`: [Bug]: Qt test_ipython fails on older ipython
+* :ghissue:`28145`: [TST] Upcoming dependency test failures
+* :ghissue:`28034`: [TST] Upcoming dependency test failures
+* :ghissue:`28168`: [TST] Upcoming dependency test failures
+* :ghissue:`28040`: [Bug]: vertical_axis not respected when rotating plots interactively
+* :ghissue:`28146`: [Bug]: Useless recursive group in SVG output when using path_effects
+* :ghissue:`28067`: [Bug]: ``LinearSegmentedColormap.from_list`` does not have all type hints for argument ``colors``
+* :ghissue:`26778`: [MNT]: Numpy 2.0 support strategy
+* :ghissue:`28020`: [Bug]: imsave fails on RGBA data when origin is set to lower
+* :ghissue:`7720`: WXAgg backend not rendering nicely on retina
+* :ghissue:`28069`: [Bug]: Can't save with custom toolbar
+* :ghissue:`28005`: [Doc]: Improve contribute instructions
+* :ghissue:`22376`: [ENH]: align_titles
+* :ghissue:`5506`: Confusing status bar values in presence of multiple axes
+* :ghissue:`4284`: Twin axis message coordinates
+* :ghissue:`18940`: WxAgg backend draws the wrong size when wxpython app is high DPI aware on Windows
+* :ghissue:`27792`: [ENH]: Legend entries for boxplot
+* :ghissue:`27828`: [Bug]: ".C10" does not work as plot shorthand format spec
+* :ghissue:`27911`: redirect not working for updated contribute page
+* :ghissue:`21876`: [Doc]: redirect-from directive appears broken?
+* :ghissue:`27941`: [Bug]: ShrinkA and ShrinkB are ignored in ax.annotate(arrowprops=...)
+* :ghissue:`26477`: [ENH]: Add interpolation_stage selector for images in qt figureoptions
+* :ghissue:`363`: Enable hatches for Cairo backend
+* :ghissue:`27852`: [Bug]: matplotlib.pyplot.matshow "(first dimension of the array) are displayed horizontally" but are displayed vertically
+* :ghissue:`27400`: [Bug]: tk backend confused by presence of file named "move" in current working directory
+* :ghissue:`25882`: [Bug]: plt.hist takes significantly more time with torch and jax arrays
+* :ghissue:`25204`: [Bug]: Pyparsing warnings emitted in mathtext
+* :ghissue:`17707`: getpwuid(): uid not found: 99
+* :ghissue:`27896`: [Doc]: Empty "User guide tutorials page" in docs
+* :ghissue:`27824`: [Bug]: polygon from axvspan not correct in polar plot after set_xy
+* :ghissue:`27378`: [ENH]: Suggest 'CN' if color is an integer
+* :ghissue:`27843`: [Bug]: close_group is not called when using patheffects
+* :ghissue:`27839`: [Bug]: PathCollection using alpha ignores 'none' facecolors
+* :ghissue:`25119`: [ENH]: secondary_x/yaxis accept transform argument
+* :ghissue:`27876`: [Doc]: Fix version switcher in devdocs
+* :ghissue:`27301`: [Bug]: ``imshow`` allows RGB(A) images with ``np.nan`` values to pass
+* :ghissue:`23839`: [MNT]: Add tests to codify ``ax.clear``
+* :ghissue:`27652`: [Doc]: Low contrast on clicked links in dark mode
+* :ghissue:`27865`: [Bug]: Zoom und pan not working after writing pdf pages.
+* :ghissue:`25871`: [Bug]: Colorbar cannot be added to another figure
+* :ghissue:`8072`: plot_date() ignores timezone in matplotlib version 2.0.0
+* :ghissue:`27812`: [ENH]: Add split feature for violin plots
+* :ghissue:`27659`: [MNT]: Improve return type of ``ioff`` and ``ion`` to improve Pyright analysis of bound variables
+* :ghissue:`27805`: [Bug]: Saving a figure with indicate_inset_zoom to pdf and then pickling it causes TypeError
+* :ghissue:`27701`: [Bug]: axis set_xscale('log') interferes with set_xticks
+* :ghissue:`19807`: radius modification in contains_point function when linewidth is specified
+* :ghissue:`27762`: [Bug]: Inconsistent treatment of list of labels in ``plot`` when the input is a dataframe
+* :ghissue:`27745`: [MNT]: ``_ImageBase.draw`` and ``Axis.draw`` args and kwargs
+* :ghissue:`27782`: [Doc]: Link to citation page in read me broken
+* :ghissue:`8789`: legend handle size does not automatically scale with linewidth
+* :ghissue:`27746`: [Doc]: Citation link in the readme.md points to 404
+* :ghissue:`20853`: Add deprecation for colormaps
+* :ghissue:`26865`: [MNT]: Remove 3.7-deprecated API
+* :ghissue:`24168`: [Bug]: ``subprocess-exited-with-error`` when trying to build on M1 mac
+* :ghissue:`27727`: [Doc]: Text in the colormap normalization gallery doesn't match the code
+* :ghissue:`27635`: [Bug]: test_figure_leak_20490 repeatedly failing on CI
+* :ghissue:`14217`: [Feature request] Add a way to update the position of the Arrow patch.
+* :ghissue:`20512`: Bad boxplot legend entries
+* :ghissue:`22011`: [Bug]: subfigures messes up with fig.legend zorder
+* :ghissue:`27414`: [Bug]: Legend overlaps shaded area in fill_between with legend location "best"
+* :ghissue:`23323`: Legend with "loc=best" does not try to avoid text
+* :ghissue:`27648`: [Doc]: ``Axes.inset_axes`` is still experimental
+* :ghissue:`27277`: [Doc]: Two license pages in docs
+* :ghissue:`24648`: [Doc]: make html fail early if latex not present
+* :ghissue:`27554`: [Bug]: Large image draw performance deterioration in recent releases
+* :ghissue:`25239`: [Bug]: colors.PowerNorm results in incorrect colorbar
+* :ghissue:`13533`: Boxplotting Masked Arrays
+* :ghissue:`25967`: [Doc]: dollar_ticks example refers to unused formatter classes
+* :ghissue:`24859`: [Doc]: Document color in a consistent way, including link
+* :ghissue:`27159`: [Bug]: Meson build fails due to qhull link issue.
+* :ghissue:`25691`: [Bug]: Secondary axis does not support Transform as functions
+* :ghissue:`25860`: [Bug]: canvas pick events not working when Axes belongs to a subfigure
+* :ghissue:`27361`: [Bug]: (Tight) layout engine breaks for 3D patches
+* :ghissue:`27145`: [ENH]: Make "No artists with labels found to put in legend" a warning
+* :ghissue:`27399`: [Bug]: None in y or yerr arrays leads to TypeError when using errorbar
+* :ghissue:`13887`: Accessing default ``norm`` of a Collection removes its colors.
+* :ghissue:`26593`: [ENH]: Support SubFigure.remove()
+* :ghissue:`27329`: [Bug]: Removing a colorbar for an axes positioned in a subgridspec restores the axes' position to the wrong place.
+* :ghissue:`27214`: [Bug]: ``NavigationToolbar2QT`` should use ``@Slot`` annotation
+* :ghissue:`27146`: [ENH]: Multi hatching in ``ax.stackplot()``
+* :ghissue:`27168`: [Doc]: Instructions for editable installation on Windows potentially missing a step
+* :ghissue:`27174`: [MNT]: Build nightly wheels with NumPy nightly wheels
+* :ghissue:`25043`: [ENH]: Plotting masked arrays correctly in 3D line plot
+* :ghissue:`26990`: [Doc]: Histogram path example renders poorly in HTML
+* :ghissue:`25738`: [MNT]: Improve readability of _mathtext_data.stix_virtual_fonts table
+* :ghissue:`11129`: Highlight past winners for JDH plotting contest in docs
+* :ghissue:`24050`: No error message in matplotlib.axes.Axes.legend() if there are more labels than handles
+* :ghissue:`10922`: ENH: clear() method for widgets.RadioButtons
+* :ghissue:`18295`: How to modify ticklabels in axisartist?
+* :ghissue:`24996`: [Bug]: for non-rectilinear axes, axvline/axhline should behave as "draw a gridline at that x/y"
+* :ghissue:`26841`: [Bug]: Global legend weird behaviors
+* :ghissue:`25974`: [MNT]: Cleanup ticklabel_format(..., style=)
+* :ghissue:`26786`: Please upload new dev wheel so we pick up 3.9.dev after 3.8 release
+* :ghissue:`18052`: the limits of axes are inexact with mplot3d
+* :ghissue:`25596`: [MNT]: Consistency on Interface
+* :ghissue:`26557`: [ENH]: Nightly Python 3.12 builds
+* :ghissue:`26281`: [ENH]: Add get_xmargin, get_ymargin, get_zmargin axes methods
diff --git a/doc/users/release_notes.rst b/doc/users/release_notes.rst
index 3befbeee5b77..1204450f6c05 100644
--- a/doc/users/release_notes.rst
+++ b/doc/users/release_notes.rst
@@ -19,8 +19,10 @@ Version 3.9
:maxdepth: 1
prev_whats_new/whats_new_3.9.0.rst
+ ../api/prev_api_changes/api_changes_3.9.1.rst
../api/prev_api_changes/api_changes_3.9.0.rst
github_stats.rst
+ prev_whats_new/github_stats_3.9.0.rst
Version 3.8
^^^^^^^^^^^
diff --git a/galleries/examples/event_handling/path_editor.py b/galleries/examples/event_handling/path_editor.py
index d6e84b454008..2af54bad53ed 100644
--- a/galleries/examples/event_handling/path_editor.py
+++ b/galleries/examples/event_handling/path_editor.py
@@ -94,7 +94,6 @@ def on_draw(self, event):
self.background = self.canvas.copy_from_bbox(self.ax.bbox)
self.ax.draw_artist(self.pathpatch)
self.ax.draw_artist(self.line)
- self.canvas.blit(self.ax.bbox)
def on_button_press(self, event):
"""Callback for mouse button presses."""
diff --git a/galleries/examples/lines_bars_and_markers/multicolored_line.py b/galleries/examples/lines_bars_and_markers/multicolored_line.py
index 5d0727e69181..3d14ecaf8567 100644
--- a/galleries/examples/lines_bars_and_markers/multicolored_line.py
+++ b/galleries/examples/lines_bars_and_markers/multicolored_line.py
@@ -3,47 +3,188 @@
Multicolored lines
==================
-This example shows how to make a multicolored line. In this example, the line
-is colored based on its derivative.
+The example shows two ways to plot a line with the a varying color defined by
+a third value. The first example defines the color at each (x, y) point.
+The second example defines the color between pairs of points, so the length
+of the color value list is one less than the length of the x and y lists.
+
+Color values at points
+----------------------
+
"""
+import warnings
+
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.collections import LineCollection
-from matplotlib.colors import BoundaryNorm, ListedColormap
+
+def colored_line(x, y, c, ax, **lc_kwargs):
+ """
+ Plot a line with a color specified along the line by a third value.
+
+ It does this by creating a collection of line segments. Each line segment is
+ made up of two straight lines each connecting the current (x, y) point to the
+ midpoints of the lines connecting the current point with its two neighbors.
+ This creates a smooth line with no gaps between the line segments.
+
+ Parameters
+ ----------
+ x, y : array-like
+ The horizontal and vertical coordinates of the data points.
+ c : array-like
+ The color values, which should be the same size as x and y.
+ ax : Axes
+ Axis object on which to plot the colored line.
+ **lc_kwargs
+ Any additional arguments to pass to matplotlib.collections.LineCollection
+ constructor. This should not include the array keyword argument because
+ that is set to the color argument. If provided, it will be overridden.
+
+ Returns
+ -------
+ matplotlib.collections.LineCollection
+ The generated line collection representing the colored line.
+ """
+ if "array" in lc_kwargs:
+ warnings.warn('The provided "array" keyword argument will be overridden')
+
+ # Default the capstyle to butt so that the line segments smoothly line up
+ default_kwargs = {"capstyle": "butt"}
+ default_kwargs.update(lc_kwargs)
+
+ # Compute the midpoints of the line segments. Include the first and last points
+ # twice so we don't need any special syntax later to handle them.
+ x = np.asarray(x)
+ y = np.asarray(y)
+ x_midpts = np.hstack((x[0], 0.5 * (x[1:] + x[:-1]), x[-1]))
+ y_midpts = np.hstack((y[0], 0.5 * (y[1:] + y[:-1]), y[-1]))
+
+ # Determine the start, middle, and end coordinate pair of each line segment.
+ # Use the reshape to add an extra dimension so each pair of points is in its
+ # own list. Then concatenate them to create:
+ # [
+ # [(x1_start, y1_start), (x1_mid, y1_mid), (x1_end, y1_end)],
+ # [(x2_start, y2_start), (x2_mid, y2_mid), (x2_end, y2_end)],
+ # ...
+ # ]
+ coord_start = np.column_stack((x_midpts[:-1], y_midpts[:-1]))[:, np.newaxis, :]
+ coord_mid = np.column_stack((x, y))[:, np.newaxis, :]
+ coord_end = np.column_stack((x_midpts[1:], y_midpts[1:]))[:, np.newaxis, :]
+ segments = np.concatenate((coord_start, coord_mid, coord_end), axis=1)
+
+ lc = LineCollection(segments, **default_kwargs)
+ lc.set_array(c) # set the colors of each segment
+
+ return ax.add_collection(lc)
+
+
+# -------------- Create and show plot --------------
+# Some arbitrary function that gives x, y, and color values
+t = np.linspace(-7.4, -0.5, 200)
+x = 0.9 * np.sin(t)
+y = 0.9 * np.cos(1.6 * t)
+color = np.linspace(0, 2, t.size)
+
+# Create a figure and plot the line on it
+fig1, ax1 = plt.subplots()
+lines = colored_line(x, y, color, ax1, linewidth=10, cmap="plasma")
+fig1.colorbar(lines) # add a color legend
+
+# Set the axis limits and tick positions
+ax1.set_xlim(-1, 1)
+ax1.set_ylim(-1, 1)
+ax1.set_xticks((-1, 0, 1))
+ax1.set_yticks((-1, 0, 1))
+ax1.set_title("Color at each point")
+
+plt.show()
+
+####################################################################
+# This method is designed to give a smooth impression when distances and color
+# differences between adjacent points are not too large. The following example
+# does not meet this criteria and by that serves to illustrate the segmentation
+# and coloring mechanism.
+x = [0, 1, 2, 3, 4]
+y = [0, 1, 2, 1, 1]
+c = [1, 2, 3, 4, 5]
+fig, ax = plt.subplots()
+ax.scatter(x, y, c=c, cmap='rainbow')
+colored_line(x, y, c=c, ax=ax, cmap='rainbow')
+
+plt.show()
+
+####################################################################
+# Color values between points
+# ---------------------------
+#
+
+
+def colored_line_between_pts(x, y, c, ax, **lc_kwargs):
+ """
+ Plot a line with a color specified between (x, y) points by a third value.
+
+ It does this by creating a collection of line segments between each pair of
+ neighboring points. The color of each segment is determined by the
+ made up of two straight lines each connecting the current (x, y) point to the
+ midpoints of the lines connecting the current point with its two neighbors.
+ This creates a smooth line with no gaps between the line segments.
+
+ Parameters
+ ----------
+ x, y : array-like
+ The horizontal and vertical coordinates of the data points.
+ c : array-like
+ The color values, which should have a size one less than that of x and y.
+ ax : Axes
+ Axis object on which to plot the colored line.
+ **lc_kwargs
+ Any additional arguments to pass to matplotlib.collections.LineCollection
+ constructor. This should not include the array keyword argument because
+ that is set to the color argument. If provided, it will be overridden.
+
+ Returns
+ -------
+ matplotlib.collections.LineCollection
+ The generated line collection representing the colored line.
+ """
+ if "array" in lc_kwargs:
+ warnings.warn('The provided "array" keyword argument will be overridden')
+
+ # Check color array size (LineCollection still works, but values are unused)
+ if len(c) != len(x) - 1:
+ warnings.warn(
+ "The c argument should have a length one less than the length of x and y. "
+ "If it has the same length, use the colored_line function instead."
+ )
+
+ # Create a set of line segments so that we can color them individually
+ # This creates the points as an N x 1 x 2 array so that we can stack points
+ # together easily to get the segments. The segments array for line collection
+ # needs to be (numlines) x (points per line) x 2 (for x and y)
+ points = np.array([x, y]).T.reshape(-1, 1, 2)
+ segments = np.concatenate([points[:-1], points[1:]], axis=1)
+ lc = LineCollection(segments, **lc_kwargs)
+
+ # Set the values used for colormapping
+ lc.set_array(c)
+
+ return ax.add_collection(lc)
+
+
+# -------------- Create and show plot --------------
x = np.linspace(0, 3 * np.pi, 500)
y = np.sin(x)
dydx = np.cos(0.5 * (x[:-1] + x[1:])) # first derivative
-# Create a set of line segments so that we can color them individually
-# This creates the points as an N x 1 x 2 array so that we can stack points
-# together easily to get the segments. The segments array for line collection
-# needs to be (numlines) x (points per line) x 2 (for x and y)
-points = np.array([x, y]).T.reshape(-1, 1, 2)
-segments = np.concatenate([points[:-1], points[1:]], axis=1)
-
-fig, axs = plt.subplots(2, 1, sharex=True, sharey=True)
-
-# Create a continuous norm to map from data points to colors
-norm = plt.Normalize(dydx.min(), dydx.max())
-lc = LineCollection(segments, cmap='viridis', norm=norm)
-# Set the values used for colormapping
-lc.set_array(dydx)
-lc.set_linewidth(2)
-line = axs[0].add_collection(lc)
-fig.colorbar(line, ax=axs[0])
-
-# Use a boundary norm instead
-cmap = ListedColormap(['r', 'g', 'b'])
-norm = BoundaryNorm([-1, -0.5, 0.5, 1], cmap.N)
-lc = LineCollection(segments, cmap=cmap, norm=norm)
-lc.set_array(dydx)
-lc.set_linewidth(2)
-line = axs[1].add_collection(lc)
-fig.colorbar(line, ax=axs[1])
-
-axs[0].set_xlim(x.min(), x.max())
-axs[0].set_ylim(-1.1, 1.1)
+fig2, ax2 = plt.subplots()
+line = colored_line_between_pts(x, y, dydx, ax2, linewidth=2, cmap="viridis")
+fig2.colorbar(line, ax=ax2, label="dy/dx")
+
+ax2.set_xlim(x.min(), x.max())
+ax2.set_ylim(-1.1, 1.1)
+ax2.set_title("Color between points")
+
plt.show()
diff --git a/galleries/examples/mplot3d/imshow3d.py b/galleries/examples/mplot3d/imshow3d.py
new file mode 100644
index 000000000000..557d96e1bce5
--- /dev/null
+++ b/galleries/examples/mplot3d/imshow3d.py
@@ -0,0 +1,88 @@
+"""
+===============
+2D images in 3D
+===============
+
+This example demonstrates how to plot 2D color coded images (similar to
+`.Axes.imshow`) as a plane in 3D.
+
+Matplotlib does not have a native function for this. Below we build one by relying
+on `.Axes3D.plot_surface`. For simplicity, there are some differences to
+`.Axes.imshow`: This function does not set the aspect of the Axes, hence pixels are
+not necessarily square. Also, pixel edges are on integer values rather than pixel
+centers. Furthermore, many optional parameters of `.Axes.imshow` are not implemented.
+
+Multiple calls of ``imshow3d`` use independent norms and thus different color scales
+by default. If you want to have a single common color scale, you need to construct
+a suitable norm beforehand and pass it to all ``imshow3d`` calls.
+
+A fundamental limitation of the 3D plotting engine is that intersecting objects cannot
+be drawn correctly. One object will always be drawn after the other. Therefore,
+multiple image planes can well be used in the background as shown in this example.
+But this approach is not suitable if the planes intersect.
+"""
+
+import matplotlib.pyplot as plt
+import numpy as np
+
+from matplotlib.colors import Normalize
+
+
+def imshow3d(ax, array, value_direction='z', pos=0, norm=None, cmap=None):
+ """
+ Display a 2D array as a color-coded 2D image embedded in 3d.
+
+ The image will be in a plane perpendicular to the coordinate axis *value_direction*.
+
+ Parameters
+ ----------
+ ax : Axes3D
+ The 3D Axes to plot into.
+ array : 2D numpy array
+ The image values.
+ value_direction : {'x', 'y', 'z'}
+ The axis normal to the image plane.
+ pos : float
+ The numeric value on the *value_direction* axis at which the image plane is
+ located.
+ norm : `~matplotlib.colors.Normalize`, default: Normalize
+ The normalization method used to scale scalar data. See `imshow()`.
+ cmap : str or `~matplotlib.colors.Colormap`, default: :rc:`image.cmap`
+ The Colormap instance or registered colormap name used to map scalar data
+ to colors.
+ """
+ if norm is None:
+ norm = Normalize()
+ colors = plt.get_cmap(cmap)(norm(array))
+
+ if value_direction == 'x':
+ nz, ny = array.shape
+ zi, yi = np.mgrid[0:nz + 1, 0:ny + 1]
+ xi = np.full_like(yi, pos)
+ elif value_direction == 'y':
+ nx, nz = array.shape
+ xi, zi = np.mgrid[0:nx + 1, 0:nz + 1]
+ yi = np.full_like(zi, pos)
+ elif value_direction == 'z':
+ ny, nx = array.shape
+ yi, xi = np.mgrid[0:ny + 1, 0:nx + 1]
+ zi = np.full_like(xi, pos)
+ else:
+ raise ValueError(f"Invalid value_direction: {value_direction!r}")
+ ax.plot_surface(xi, yi, zi, rstride=1, cstride=1, facecolors=colors, shade=False)
+
+
+fig = plt.figure()
+ax = fig.add_subplot(projection='3d')
+ax.set(xlabel="x", ylabel="y", zlabel="z")
+
+nx, ny, nz = 8, 10, 5
+data_xy = np.arange(ny * nx).reshape(ny, nx) + 15 * np.random.random((ny, nx))
+data_yz = np.arange(nz * ny).reshape(nz, ny) + 10 * np.random.random((nz, ny))
+data_zx = np.arange(nx * nz).reshape(nx, nz) + 8 * np.random.random((nx, nz))
+
+imshow3d(ax, data_xy)
+imshow3d(ax, data_yz, value_direction='x', cmap='magma')
+imshow3d(ax, data_zx, value_direction='y', pos=ny, cmap='plasma')
+
+plt.show()
diff --git a/galleries/examples/mplot3d/intersecting_planes.py b/galleries/examples/mplot3d/intersecting_planes.py
new file mode 100644
index 000000000000..b8aa08fd7e18
--- /dev/null
+++ b/galleries/examples/mplot3d/intersecting_planes.py
@@ -0,0 +1,89 @@
+"""
+===================
+Intersecting planes
+===================
+
+This examples demonstrates drawing intersecting planes in 3D. It is a generalization
+of :doc:`/gallery/mplot3d/imshow3d`.
+
+Drawing intersecting planes in `.mplot3d` is complicated, because `.mplot3d` is not a
+real 3D renderer, but only projects the Artists into 3D and draws them in the right
+order. This does not work correctly if Artists overlap each other mutually. In this
+example, we lift the problem of mutual overlap by segmenting the planes at their
+intersections, making four parts out of each plane.
+
+This examples only works correctly for planes that cut each other in haves. This
+limitation is intentional to keep the code more readable. Cutting at arbitrary
+positions would of course be possible but makes the code even more complex.
+Thus, this example is more a demonstration of the concept how to work around
+limitations of the 3D visualization, it's not a refined solution for drawing
+arbitrary intersecting planes, which you can copy-and-paste as is.
+"""
+import matplotlib.pyplot as plt
+import numpy as np
+
+
+def plot_quadrants(ax, array, fixed_coord, cmap):
+ """For a given 3d *array* plot a plane with *fixed_coord*, using four quadrants."""
+ nx, ny, nz = array.shape
+ index = {
+ 'x': (nx // 2, slice(None), slice(None)),
+ 'y': (slice(None), ny // 2, slice(None)),
+ 'z': (slice(None), slice(None), nz // 2),
+ }[fixed_coord]
+ plane_data = array[index]
+
+ n0, n1 = plane_data.shape
+ quadrants = [
+ plane_data[:n0 // 2, :n1 // 2],
+ plane_data[:n0 // 2, n1 // 2:],
+ plane_data[n0 // 2:, :n1 // 2],
+ plane_data[n0 // 2:, n1 // 2:]
+ ]
+
+ min_val = array.min()
+ max_val = array.max()
+
+ cmap = plt.get_cmap(cmap)
+
+ for i, quadrant in enumerate(quadrants):
+ facecolors = cmap((quadrant - min_val) / (max_val - min_val))
+ if fixed_coord == 'x':
+ Y, Z = np.mgrid[0:ny // 2, 0:nz // 2]
+ X = nx // 2 * np.ones_like(Y)
+ Y_offset = (i // 2) * ny // 2
+ Z_offset = (i % 2) * nz // 2
+ ax.plot_surface(X, Y + Y_offset, Z + Z_offset, rstride=1, cstride=1,
+ facecolors=facecolors, shade=False)
+ elif fixed_coord == 'y':
+ X, Z = np.mgrid[0:nx // 2, 0:nz // 2]
+ Y = ny // 2 * np.ones_like(X)
+ X_offset = (i // 2) * nx // 2
+ Z_offset = (i % 2) * nz // 2
+ ax.plot_surface(X + X_offset, Y, Z + Z_offset, rstride=1, cstride=1,
+ facecolors=facecolors, shade=False)
+ elif fixed_coord == 'z':
+ X, Y = np.mgrid[0:nx // 2, 0:ny // 2]
+ Z = nz // 2 * np.ones_like(X)
+ X_offset = (i // 2) * nx // 2
+ Y_offset = (i % 2) * ny // 2
+ ax.plot_surface(X + X_offset, Y + Y_offset, Z, rstride=1, cstride=1,
+ facecolors=facecolors, shade=False)
+
+
+def figure_3D_array_slices(array, cmap=None):
+ """Plot a 3d array using three intersecting centered planes."""
+ fig = plt.figure()
+ ax = fig.add_subplot(projection='3d')
+ ax.set_box_aspect(array.shape)
+ plot_quadrants(ax, array, 'x', cmap=cmap)
+ plot_quadrants(ax, array, 'y', cmap=cmap)
+ plot_quadrants(ax, array, 'z', cmap=cmap)
+ return fig, ax
+
+
+nx, ny, nz = 70, 100, 50
+r_square = (np.mgrid[-1:1:1j * nx, -1:1:1j * ny, -1:1:1j * nz] ** 2).sum(0)
+
+figure_3D_array_slices(r_square, cmap='viridis_r')
+plt.show()
diff --git a/galleries/examples/units/basic_units.py b/galleries/examples/units/basic_units.py
index 0b493ab7216c..f9a94bcf6e37 100644
--- a/galleries/examples/units/basic_units.py
+++ b/galleries/examples/units/basic_units.py
@@ -146,10 +146,10 @@ def __getattribute__(self, name):
return getattr(variable, name)
return object.__getattribute__(self, name)
- def __array__(self, dtype=object):
+ def __array__(self, dtype=object, copy=False):
return np.asarray(self.value, dtype)
- def __array_wrap__(self, array, context):
+ def __array_wrap__(self, array, context=None, return_scalar=False):
return TaggedValue(array, self.unit)
def __repr__(self):
@@ -222,10 +222,10 @@ def __mul__(self, rhs):
def __rmul__(self, lhs):
return self*lhs
- def __array_wrap__(self, array, context):
+ def __array_wrap__(self, array, context=None, return_scalar=False):
return TaggedValue(array, self)
- def __array__(self, t=None, context=None):
+ def __array__(self, t=None, context=None, copy=False):
ret = np.array(1)
if t is not None:
return ret.astype(t)
diff --git a/galleries/plot_types/3D/bar3d_simple.py b/galleries/plot_types/3D/bar3d_simple.py
new file mode 100644
index 000000000000..aa75560de8f2
--- /dev/null
+++ b/galleries/plot_types/3D/bar3d_simple.py
@@ -0,0 +1,29 @@
+"""
+==========================
+bar3d(x, y, z, dx, dy, dz)
+==========================
+
+See `~mpl_toolkits.mplot3d.axes3d.Axes3D.bar3d`.
+"""
+import matplotlib.pyplot as plt
+import numpy as np
+
+plt.style.use('_mpl-gallery')
+
+# Make data
+x = [1, 1, 2, 2]
+y = [1, 2, 1, 2]
+z = [0, 0, 0, 0]
+dx = np.ones_like(x)*0.5
+dy = np.ones_like(x)*0.5
+dz = [2, 3, 1, 4]
+
+# Plot
+fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
+ax.bar3d(x, y, z, dx, dy, dz)
+
+ax.set(xticklabels=[],
+ yticklabels=[],
+ zticklabels=[])
+
+plt.show()
diff --git a/galleries/plot_types/3D/plot3d_simple.py b/galleries/plot_types/3D/plot3d_simple.py
new file mode 100644
index 000000000000..108dbecfbd87
--- /dev/null
+++ b/galleries/plot_types/3D/plot3d_simple.py
@@ -0,0 +1,27 @@
+"""
+================
+plot(xs, ys, zs)
+================
+
+See `~mpl_toolkits.mplot3d.axes3d.Axes3D.plot`.
+"""
+import matplotlib.pyplot as plt
+import numpy as np
+
+plt.style.use('_mpl-gallery')
+
+# Make data
+n = 100
+xs = np.linspace(0, 1, n)
+ys = np.sin(xs * 6 * np.pi)
+zs = np.cos(xs * 6 * np.pi)
+
+# Plot
+fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
+ax.plot(xs, ys, zs)
+
+ax.set(xticklabels=[],
+ yticklabels=[],
+ zticklabels=[])
+
+plt.show()
diff --git a/galleries/plot_types/3D/quiver3d_simple.py b/galleries/plot_types/3D/quiver3d_simple.py
new file mode 100644
index 000000000000..6f4aaa9cad90
--- /dev/null
+++ b/galleries/plot_types/3D/quiver3d_simple.py
@@ -0,0 +1,32 @@
+"""
+========================
+quiver(X, Y, Z, U, V, W)
+========================
+
+See `~mpl_toolkits.mplot3d.axes3d.Axes3D.quiver`.
+"""
+import matplotlib.pyplot as plt
+import numpy as np
+
+plt.style.use('_mpl-gallery')
+
+# Make data
+n = 4
+x = np.linspace(-1, 1, n)
+y = np.linspace(-1, 1, n)
+z = np.linspace(-1, 1, n)
+X, Y, Z = np.meshgrid(x, y, z)
+U = (X + Y)/5
+V = (Y - X)/5
+W = Z*0
+
+
+# Plot
+fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
+ax.quiver(X, Y, Z, U, V, W)
+
+ax.set(xticklabels=[],
+ yticklabels=[],
+ zticklabels=[])
+
+plt.show()
diff --git a/galleries/plot_types/3D/stem3d.py b/galleries/plot_types/3D/stem3d.py
new file mode 100644
index 000000000000..50aa80146bdc
--- /dev/null
+++ b/galleries/plot_types/3D/stem3d.py
@@ -0,0 +1,27 @@
+"""
+=============
+stem(x, y, z)
+=============
+
+See `~mpl_toolkits.mplot3d.axes3d.Axes3D.stem`.
+"""
+import matplotlib.pyplot as plt
+import numpy as np
+
+plt.style.use('_mpl-gallery')
+
+# Make data
+n = 20
+x = np.sin(np.linspace(0, 2*np.pi, n))
+y = np.cos(np.linspace(0, 2*np.pi, n))
+z = np.linspace(0, 1, n)
+
+# Plot
+fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
+ax.stem(x, y, z)
+
+ax.set(xticklabels=[],
+ yticklabels=[],
+ zticklabels=[])
+
+plt.show()
diff --git a/galleries/plot_types/arrays/README.rst b/galleries/plot_types/arrays/README.rst
index d9dbfd10ead7..aba457a69940 100644
--- a/galleries/plot_types/arrays/README.rst
+++ b/galleries/plot_types/arrays/README.rst
@@ -1,7 +1,7 @@
.. _array_plots:
-Gridded data:
--------------
+Gridded data
+------------
Plots of arrays and images :math:`Z_{i, j}` and fields :math:`U_{i, j}, V_{i, j}`
on `regular grids `_ and
diff --git a/galleries/plot_types/arrays/imshow.py b/galleries/plot_types/arrays/imshow.py
index c28278c6c657..b2920e7fd80c 100644
--- a/galleries/plot_types/arrays/imshow.py
+++ b/galleries/plot_types/arrays/imshow.py
@@ -19,6 +19,6 @@
# plot
fig, ax = plt.subplots()
-ax.imshow(Z)
+ax.imshow(Z, origin='lower')
plt.show()
diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py
index b65004b8c272..040c5a4ba4e9 100644
--- a/lib/matplotlib/axes/_axes.py
+++ b/lib/matplotlib/axes/_axes.py
@@ -1006,14 +1006,14 @@ def axhspan(self, ymin, ymax, xmin=0, xmax=1, **kwargs):
Returns
-------
- `~matplotlib.patches.Polygon`
+ `~matplotlib.patches.Rectangle`
Horizontal span (rectangle) from (xmin, ymin) to (xmax, ymax).
Other Parameters
----------------
- **kwargs : `~matplotlib.patches.Polygon` properties
+ **kwargs : `~matplotlib.patches.Rectangle` properties
- %(Polygon:kwdoc)s
+ %(Rectangle:kwdoc)s
See Also
--------
@@ -1028,7 +1028,7 @@ def axhspan(self, ymin, ymax, xmin=0, xmax=1, **kwargs):
# For Rectangles and non-separable transforms, add_patch can be buggy
# and update the x limits even though it shouldn't do so for an
# yaxis_transformed patch, so undo that update.
- ix = self.dataLim.intervalx
+ ix = self.dataLim.intervalx.copy()
mx = self.dataLim.minposx
self.add_patch(p)
self.dataLim.intervalx = ix
@@ -1061,14 +1061,14 @@ def axvspan(self, xmin, xmax, ymin=0, ymax=1, **kwargs):
Returns
-------
- `~matplotlib.patches.Polygon`
+ `~matplotlib.patches.Rectangle`
Vertical span (rectangle) from (xmin, ymin) to (xmax, ymax).
Other Parameters
----------------
- **kwargs : `~matplotlib.patches.Polygon` properties
+ **kwargs : `~matplotlib.patches.Rectangle` properties
- %(Polygon:kwdoc)s
+ %(Rectangle:kwdoc)s
See Also
--------
@@ -1586,7 +1586,7 @@ def plot(self, *args, scalex=True, scaley=True, data=None, **kwargs):
>>> plot(x1, y1, 'bo')
>>> plot(x2, y2, 'go')
- - If *x* and/or *y* are 2D arrays a separate data set will be drawn
+ - If *x* and/or *y* are 2D arrays, a separate data set will be drawn
for every column. If both *x* and *y* are 2D, they must have the
same shape. If only one of them is 2D with shape (N, m) the other
must have length N and will be used for every data set m.
@@ -3527,11 +3527,11 @@ def errorbar(self, x, y, yerr=None, xerr=None,
`.ErrorbarContainer`
The container contains:
- - plotline: `~matplotlib.lines.Line2D` instance of x, y plot markers
+ - data_line : A `~matplotlib.lines.Line2D` instance of x, y plot markers
and/or line.
- - caplines: A tuple of `~matplotlib.lines.Line2D` instances of the error
+ - caplines : A tuple of `~matplotlib.lines.Line2D` instances of the error
bar caps.
- - barlinecols: A tuple of `.LineCollection` with the horizontal and
+ - barlinecols : A tuple of `.LineCollection` with the horizontal and
vertical error ranges.
Other Parameters
diff --git a/lib/matplotlib/axes/_axes.pyi b/lib/matplotlib/axes/_axes.pyi
index b70d330aa442..76aaee77aff8 100644
--- a/lib/matplotlib/axes/_axes.pyi
+++ b/lib/matplotlib/axes/_axes.pyi
@@ -155,10 +155,10 @@ class Axes(_AxesBase):
) -> AxLine: ...
def axhspan(
self, ymin: float, ymax: float, xmin: float = ..., xmax: float = ..., **kwargs
- ) -> Polygon: ...
+ ) -> Rectangle: ...
def axvspan(
self, xmin: float, xmax: float, ymin: float = ..., ymax: float = ..., **kwargs
- ) -> Polygon: ...
+ ) -> Rectangle: ...
def hlines(
self,
y: float | ArrayLike,
diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py
index 0164f4e11169..f83999436cbb 100644
--- a/lib/matplotlib/axes/_base.py
+++ b/lib/matplotlib/axes/_base.py
@@ -597,7 +597,8 @@ def __init__(self, fig,
sharex, sharey : `~matplotlib.axes.Axes`, optional
The x- or y-`~.matplotlib.axis` is shared with the x- or y-axis in
- the input `~.axes.Axes`.
+ the input `~.axes.Axes`. Note that it is not possible to unshare
+ axes.
frameon : bool, default: True
Whether the Axes frame is visible.
@@ -1221,7 +1222,7 @@ def sharex(self, other):
This is equivalent to passing ``sharex=other`` when constructing the
Axes, and cannot be used if the x-axis is already being shared with
- another Axes.
+ another Axes. Note that it is not possible to unshare axes.
"""
_api.check_isinstance(_AxesBase, other=other)
if self._sharex is not None and other is not self._sharex:
@@ -1240,7 +1241,7 @@ def sharey(self, other):
This is equivalent to passing ``sharey=other`` when constructing the
Axes, and cannot be used if the y-axis is already being shared with
- another Axes.
+ another Axes. Note that it is not possible to unshare axes.
"""
_api.check_isinstance(_AxesBase, other=other)
if self._sharey is not None and other is not self._sharey:
@@ -1881,6 +1882,11 @@ def apply_aspect(self, position=None):
Parameters
----------
position : None or .Bbox
+
+ .. note::
+ This parameter exists for historic reasons and is considered
+ internal. End users should not use it.
+
If not ``None``, this defines the position of the
Axes within the figure as a Bbox. See `~.Axes.get_position`
for further details.
@@ -1891,6 +1897,10 @@ def apply_aspect(self, position=None):
to call it yourself if you need to update the Axes position and/or
view limits before the Figure is drawn.
+ An alternative with a broader scope is `.Figure.draw_without_rendering`,
+ which updates all stale components of a figure, not only the positioning /
+ view limits of a single Axes.
+
See Also
--------
matplotlib.axes.Axes.set_aspect
@@ -1899,6 +1909,24 @@ def apply_aspect(self, position=None):
Set how the Axes adjusts to achieve the required aspect ratio.
matplotlib.axes.Axes.set_anchor
Set the position in case of extra space.
+ matplotlib.figure.Figure.draw_without_rendering
+ Update all stale components of a figure.
+
+ Examples
+ --------
+ A typical usage example would be the following. `~.Axes.imshow` sets the
+ aspect to 1, but adapting the Axes position and extent to reflect this is
+ deferred until rendering for performance reasons. If you want to know the
+ Axes size before, you need to call `.apply_aspect` to get the correct
+ values.
+
+ >>> fig, ax = plt.subplots()
+ >>> ax.imshow(np.zeros((3, 3)))
+ >>> ax.bbox.width, ax.bbox.height
+ (496.0, 369.59999999999997)
+ >>> ax.apply_aspect()
+ >>> ax.bbox.width, ax.bbox.height
+ (369.59999999999997, 369.59999999999997)
"""
if position is None:
position = self.get_position(original=True)
@@ -2941,9 +2969,15 @@ def handle_single_axis(
# Index of largest element < x0 + tol, if any.
i0 = stickies.searchsorted(x0 + tol) - 1
x0bound = stickies[i0] if i0 != -1 else None
+ # Ensure the boundary acts only if the sticky is the extreme value
+ if x0bound is not None and x0bound > x0:
+ x0bound = None
# Index of smallest element > x1 - tol, if any.
i1 = stickies.searchsorted(x1 - tol)
x1bound = stickies[i1] if i1 != len(stickies) else None
+ # Ensure the boundary acts only if the sticky is the extreme value
+ if x1bound is not None and x1bound < x1:
+ x1bound = None
# Add the margin in figure space and then transform back, to handle
# non-linear scales.
diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py
index d317f6ec0567..3afc98fac60b 100644
--- a/lib/matplotlib/axis.py
+++ b/lib/matplotlib/axis.py
@@ -2028,7 +2028,9 @@ def set_ticklabels(self, labels, *, minor=False, fontdict=None, **kwargs):
.. warning::
- This only sets the properties of the current ticks.
+ This only sets the properties of the current ticks, which is
+ only sufficient for static plots.
+
Ticks are not guaranteed to be persistent. Various operations
can create, delete and modify the Tick instances. There is an
imminent risk that these settings can get lost if you work on
diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py
index f4273bc03919..53e5f6b23213 100644
--- a/lib/matplotlib/backend_bases.py
+++ b/lib/matplotlib/backend_bases.py
@@ -513,21 +513,7 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
If True, use mathtext parser. If "TeX", use tex for rendering.
mtext : `~matplotlib.text.Text`
The original text object to be rendered.
-
- Notes
- -----
- **Note for backend implementers:**
-
- When you are trying to determine if you have gotten your bounding box
- right (which is what enables the text layout/alignment to work
- properly), it helps to change the line in text.py::
-
- if 0: bbox_artist(self, renderer)
-
- to if 1, and then the actual bounding box will be plotted along with
- your text.
"""
-
self._draw_text_as_path(gc, x, y, s, prop, angle, ismath)
def _get_text_path_transform(self, x, y, s, prop, angle, ismath):
diff --git a/lib/matplotlib/backends/_backend_tk.py b/lib/matplotlib/backends/_backend_tk.py
index 295f6c41372d..df06440a9826 100644
--- a/lib/matplotlib/backends/_backend_tk.py
+++ b/lib/matplotlib/backends/_backend_tk.py
@@ -44,7 +44,7 @@ def _restore_foreground_window_at_end():
try:
yield
finally:
- if mpl.rcParams['tk.window_focus']:
+ if foreground and mpl.rcParams['tk.window_focus']:
_c_internal_utils.Win32_SetForegroundWindow(foreground)
diff --git a/lib/matplotlib/backends/backend_gtk3cairo.py b/lib/matplotlib/backends/backend_gtk3cairo.py
index 24a26111f062..371b8dc1b31f 100644
--- a/lib/matplotlib/backends/backend_gtk3cairo.py
+++ b/lib/matplotlib/backends/backend_gtk3cairo.py
@@ -13,15 +13,19 @@ def on_draw_event(self, widget, ctx):
with (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar
else nullcontext()):
- self._renderer.set_context(ctx)
- scale = self.device_pixel_ratio
- # Scale physical drawing to logical size.
- ctx.scale(1 / scale, 1 / scale)
allocation = self.get_allocation()
+ # Render the background before scaling, as the allocated size here is in
+ # logical pixels.
Gtk.render_background(
self.get_style_context(), ctx,
- allocation.x, allocation.y,
- allocation.width, allocation.height)
+ 0, 0, allocation.width, allocation.height)
+ scale = self.device_pixel_ratio
+ # Scale physical drawing to logical size.
+ ctx.scale(1 / scale, 1 / scale)
+ self._renderer.set_context(ctx)
+ # Set renderer to physical size so it renders in full resolution.
+ self._renderer.width = allocation.width * scale
+ self._renderer.height = allocation.height * scale
self._renderer.dpi = self.figure.dpi
self.figure.draw(self._renderer)
diff --git a/lib/matplotlib/backends/backend_gtk4.py b/lib/matplotlib/backends/backend_gtk4.py
index 256a8ec9e864..dd86ab628ce7 100644
--- a/lib/matplotlib/backends/backend_gtk4.py
+++ b/lib/matplotlib/backends/backend_gtk4.py
@@ -34,7 +34,6 @@ class FigureCanvasGTK4(_FigureCanvasGTK, Gtk.DrawingArea):
required_interactive_framework = "gtk4"
supports_blit = False
manager_class = _api.classproperty(lambda cls: FigureManagerGTK4)
- _context_is_scaled = False
def __init__(self, figure=None):
super().__init__(figure=figure)
@@ -228,13 +227,8 @@ def _post_draw(self, widget, ctx):
lw = 1
dash = 3
- if not self._context_is_scaled:
- x0, y0, w, h = (dim / self.device_pixel_ratio
- for dim in self._rubberband_rect)
- else:
- x0, y0, w, h = self._rubberband_rect
- lw *= self.device_pixel_ratio
- dash *= self.device_pixel_ratio
+ x0, y0, w, h = (dim / self.device_pixel_ratio
+ for dim in self._rubberband_rect)
x1 = x0 + w
y1 = y0 + h
diff --git a/lib/matplotlib/backends/backend_gtk4cairo.py b/lib/matplotlib/backends/backend_gtk4cairo.py
index b1d543704351..838ea03fcce6 100644
--- a/lib/matplotlib/backends/backend_gtk4cairo.py
+++ b/lib/matplotlib/backends/backend_gtk4cairo.py
@@ -5,7 +5,10 @@
class FigureCanvasGTK4Cairo(FigureCanvasCairo, FigureCanvasGTK4):
- _context_is_scaled = True
+ def _set_device_pixel_ratio(self, ratio):
+ # Cairo in GTK4 always uses logical pixels, so we don't need to do anything for
+ # changes to the device pixel ratio.
+ return False
def on_draw_event(self, widget, ctx):
if self._idle_draw_id:
@@ -16,15 +19,11 @@ def on_draw_event(self, widget, ctx):
with (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar
else nullcontext()):
self._renderer.set_context(ctx)
- scale = self.device_pixel_ratio
- # Scale physical drawing to logical size.
- ctx.scale(1 / scale, 1 / scale)
allocation = self.get_allocation()
Gtk.render_background(
self.get_style_context(), ctx,
allocation.x, allocation.y,
allocation.width, allocation.height)
- self._renderer.dpi = self.figure.dpi
self.figure.draw(self._renderer)
diff --git a/lib/matplotlib/backends/registry.py b/lib/matplotlib/backends/registry.py
index 19b4cba254ab..e08817bb089b 100644
--- a/lib/matplotlib/backends/registry.py
+++ b/lib/matplotlib/backends/registry.py
@@ -93,6 +93,9 @@ def __init__(self):
}
def _backend_module_name(self, backend):
+ if backend.startswith("module://"):
+ return backend[9:]
+
# Return name of module containing the specified backend.
# Does not check if the backend is valid, use is_valid_backend for that.
backend = backend.lower()
@@ -168,8 +171,11 @@ def backward_compatible_entry_points(
def _validate_and_store_entry_points(self, entries):
# Validate and store entry points so that they can be used via matplotlib.use()
# in the normal manner. Entry point names cannot be of module:// format, cannot
- # shadow a built-in backend name, and cannot be duplicated.
- for name, module in entries:
+ # shadow a built-in backend name, and there cannot be multiple entry points
+ # with the same name but different modules. Multiple entry points with the same
+ # name and value are permitted (it can sometimes happen outside of our control,
+ # see https://github.com/matplotlib/matplotlib/issues/28367).
+ for name, module in set(entries):
name = name.lower()
if name.startswith("module://"):
raise RuntimeError(
@@ -221,7 +227,8 @@ def is_valid_backend(self, backend):
bool
True if backend is valid, False otherwise.
"""
- backend = backend.lower()
+ if not backend.startswith("module://"):
+ backend = backend.lower()
# For backward compatibility, convert ipympl and matplotlib-inline long
# module:// names to their shortened forms.
@@ -339,7 +346,8 @@ def resolve_backend(self, backend):
The GUI framework, which will be None for a backend that is non-interactive.
"""
if isinstance(backend, str):
- backend = backend.lower()
+ if not backend.startswith("module://"):
+ backend = backend.lower()
else: # Might be _auto_backend_sentinel or None
# Use whatever is already running...
from matplotlib import get_backend
@@ -392,7 +400,8 @@ def resolve_gui_or_backend(self, gui_or_backend):
framework : str or None
The GUI framework, which will be None for a backend that is non-interactive.
"""
- gui_or_backend = gui_or_backend.lower()
+ if not gui_or_backend.startswith("module://"):
+ gui_or_backend = gui_or_backend.lower()
# First check if it is a gui loop name.
backend = self.backend_for_gui_framework(gui_or_backend)
diff --git a/lib/matplotlib/cm.py b/lib/matplotlib/cm.py
index c14973560ac3..b0cb3e9a7ec1 100644
--- a/lib/matplotlib/cm.py
+++ b/lib/matplotlib/cm.py
@@ -232,6 +232,44 @@ def get_cmap(self, cmap):
globals().update(_colormaps)
+# This is an exact copy of pyplot.get_cmap(). It was removed in 3.9, but apparently
+# caused more user trouble than expected. Re-added for 3.9.1 and extended the
+# deprecation period for two additional minor releases.
+@_api.deprecated(
+ '3.7',
+ removal='3.11',
+ alternative="``matplotlib.colormaps[name]`` or ``matplotlib.colormaps.get_cmap()``"
+ " or ``pyplot.get_cmap()``"
+ )
+def get_cmap(name=None, lut=None):
+ """
+ Get a colormap instance, defaulting to rc values if *name* is None.
+
+ Parameters
+ ----------
+ name : `~matplotlib.colors.Colormap` or str or None, default: None
+ If a `.Colormap` instance, it will be returned. Otherwise, the name of
+ a colormap known to Matplotlib, which will be resampled by *lut*. The
+ default, None, means :rc:`image.cmap`.
+ lut : int or None, default: None
+ If *name* is not already a Colormap instance and *lut* is not None, the
+ colormap will be resampled to have *lut* entries in the lookup table.
+
+ Returns
+ -------
+ Colormap
+ """
+ if name is None:
+ name = mpl.rcParams['image.cmap']
+ if isinstance(name, colors.Colormap):
+ return name
+ _api.check_in_list(sorted(_colormaps), name=name)
+ if lut is None:
+ return _colormaps[name]
+ else:
+ return _colormaps[name].resampled(lut)
+
+
def _auto_norm_from_scale(scale_cls):
"""
Automatically generate a norm class from *scale_cls*.
diff --git a/lib/matplotlib/cm.pyi b/lib/matplotlib/cm.pyi
index da78d940ba4a..be8f10b39cb6 100644
--- a/lib/matplotlib/cm.pyi
+++ b/lib/matplotlib/cm.pyi
@@ -19,6 +19,8 @@ class ColormapRegistry(Mapping[str, colors.Colormap]):
_colormaps: ColormapRegistry = ...
+def get_cmap(name: str | colors.Colormap | None = ..., lut: int | None = ...) -> colors.Colormap: ...
+
class ScalarMappable:
cmap: colors.Colormap | None
colorbar: Colorbar | None
diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py
index af61e4671ff4..156ea2ff6497 100644
--- a/lib/matplotlib/colorbar.py
+++ b/lib/matplotlib/colorbar.py
@@ -257,10 +257,6 @@ class Colorbar:
*location* is None, the ticks will be at the bottom for a horizontal
colorbar and at the right for a vertical.
- drawedges : bool
- Whether to draw lines at color boundaries.
-
-
%(_colormap_kw_doc)s
location : None or {'left', 'right', 'top', 'bottom'}
diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py
index c4e5987fdf92..177557b371a6 100644
--- a/lib/matplotlib/colors.py
+++ b/lib/matplotlib/colors.py
@@ -225,7 +225,7 @@ def is_color_like(c):
return True
try:
to_rgba(c)
- except ValueError:
+ except (TypeError, ValueError):
return False
else:
return True
@@ -296,6 +296,11 @@ def to_rgba(c, alpha=None):
Tuple of floats ``(r, g, b, a)``, where each channel (red, green, blue,
alpha) can assume values between 0 and 1.
"""
+ if isinstance(c, tuple) and len(c) == 2:
+ if alpha is None:
+ c, alpha = c
+ else:
+ c = c[0]
# Special-case nth color syntax because it should not be cached.
if _is_nth_color(c):
prop_cycler = mpl.rcParams['axes.prop_cycle']
@@ -325,11 +330,6 @@ def _to_rgba_no_colorcycle(c, alpha=None):
*alpha* is ignored for the color value ``"none"`` (case-insensitive),
which always maps to ``(0, 0, 0, 0)``.
"""
- if isinstance(c, tuple) and len(c) == 2:
- if alpha is None:
- c, alpha = c
- else:
- c = c[0]
if alpha is not None and not 0 <= alpha <= 1:
raise ValueError("'alpha' must be between 0 and 1, inclusive")
orig_c = c
diff --git a/lib/matplotlib/container.py b/lib/matplotlib/container.py
index 0f082e298afc..b6dd43724f34 100644
--- a/lib/matplotlib/container.py
+++ b/lib/matplotlib/container.py
@@ -87,12 +87,12 @@ class ErrorbarContainer(Container):
lines : tuple
Tuple of ``(data_line, caplines, barlinecols)``.
- - data_line : :class:`~matplotlib.lines.Line2D` instance of
- x, y plot markers and/or line.
- - caplines : tuple of :class:`~matplotlib.lines.Line2D` instances of
- the error bar caps.
- - barlinecols : list of :class:`~matplotlib.collections.LineCollection`
- with the horizontal and vertical error ranges.
+ - data_line : A `~matplotlib.lines.Line2D` instance of x, y plot markers
+ and/or line.
+ - caplines : A tuple of `~matplotlib.lines.Line2D` instances of the error
+ bar caps.
+ - barlinecols : A tuple of `~matplotlib.collections.LineCollection` with the
+ horizontal and vertical error ranges.
has_xerr, has_yerr : bool
``True`` if the errorbar has x/y errors.
@@ -115,13 +115,13 @@ class StemContainer(Container):
Attributes
----------
- markerline : :class:`~matplotlib.lines.Line2D`
+ markerline : `~matplotlib.lines.Line2D`
The artist of the markers at the stem heads.
- stemlines : list of :class:`~matplotlib.lines.Line2D`
+ stemlines : `~matplotlib.collections.LineCollection`
The artists of the vertical lines for all stems.
- baseline : :class:`~matplotlib.lines.Line2D`
+ baseline : `~matplotlib.lines.Line2D`
The artist of the horizontal baseline.
"""
def __init__(self, markerline_stemlines_baseline, **kwargs):
@@ -130,7 +130,7 @@ def __init__(self, markerline_stemlines_baseline, **kwargs):
----------
markerline_stemlines_baseline : tuple
Tuple of ``(markerline, stemlines, baseline)``.
- ``markerline`` contains the `.LineCollection` of the markers,
+ ``markerline`` contains the `.Line2D` of the markers,
``stemlines`` is a `.LineCollection` of the main lines,
``baseline`` is the `.Line2D` of the baseline.
"""
diff --git a/lib/matplotlib/container.pyi b/lib/matplotlib/container.pyi
index 9cc2e1ac2acc..c66e7ba4b4c3 100644
--- a/lib/matplotlib/container.pyi
+++ b/lib/matplotlib/container.pyi
@@ -34,12 +34,12 @@ class BarContainer(Container):
) -> None: ...
class ErrorbarContainer(Container):
- lines: tuple[Line2D, Line2D, LineCollection]
+ lines: tuple[Line2D, tuple[Line2D, ...], tuple[LineCollection, ...]]
has_xerr: bool
has_yerr: bool
def __init__(
self,
- lines: tuple[Line2D, Line2D, LineCollection],
+ lines: tuple[Line2D, tuple[Line2D, ...], tuple[LineCollection, ...]],
has_xerr: bool = ...,
has_yerr: bool = ...,
**kwargs
diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py
index 0a0ff01a2571..0d939190a0a9 100644
--- a/lib/matplotlib/figure.py
+++ b/lib/matplotlib/figure.py
@@ -755,6 +755,8 @@ def subplots(self, nrows=1, ncols=1, *, sharex=False, sharey=False,
When subplots have a shared axis that has units, calling
`.Axis.set_units` will update each axis with the new units.
+ Note that it is not possible to unshare axes.
+
squeeze : bool, default: True
- If True, extra dimensions are squeezed out from the returned
array of Axes:
@@ -1631,6 +1633,8 @@ def add_subfigure(self, subplotspec, **kwargs):
sf = SubFigure(self, subplotspec, **kwargs)
self.subfigs += [sf]
sf._remove_method = self.subfigs.remove
+ sf.stale_callback = _stale_figure_callback
+ self.stale = True
return sf
def sca(self, a):
@@ -2222,7 +2226,6 @@ def __init__(self, parent, subplotspec, *,
self.subplotpars = parent.subplotpars
self.dpi_scale_trans = parent.dpi_scale_trans
self._axobservers = parent._axobservers
- self.canvas = parent.canvas
self.transFigure = parent.transFigure
self.bbox_relative = Bbox.null()
self._redo_transform_rel_fig()
@@ -2239,6 +2242,10 @@ def __init__(self, parent, subplotspec, *,
self._set_artist_props(self.patch)
self.patch.set_antialiased(False)
+ @property
+ def canvas(self):
+ return self._parent.canvas
+
@property
def dpi(self):
return self._parent.dpi
diff --git a/lib/matplotlib/figure.pyi b/lib/matplotlib/figure.pyi
index eae21c2614f0..b079312695c1 100644
--- a/lib/matplotlib/figure.pyi
+++ b/lib/matplotlib/figure.pyi
@@ -1,12 +1,12 @@
from collections.abc import Callable, Hashable, Iterable
import os
-from typing import Any, IO, Literal, TypeVar, overload
+from typing import Any, IO, Literal, Sequence, TypeVar, overload
import numpy as np
from numpy.typing import ArrayLike
from matplotlib.artist import Artist
-from matplotlib.axes import Axes, SubplotBase
+from matplotlib.axes import Axes
from matplotlib.backend_bases import (
FigureCanvasBase,
MouseButton,
@@ -92,6 +92,20 @@ class FigureBase(Artist):
@overload
def add_subplot(self, **kwargs) -> Axes: ...
@overload
+ def subplots(
+ self,
+ nrows: Literal[1] = ...,
+ ncols: Literal[1] = ...,
+ *,
+ sharex: bool | Literal["none", "all", "row", "col"] = ...,
+ sharey: bool | Literal["none", "all", "row", "col"] = ...,
+ squeeze: Literal[True] = ...,
+ width_ratios: Sequence[float] | None = ...,
+ height_ratios: Sequence[float] | None = ...,
+ subplot_kw: dict[str, Any] | None = ...,
+ gridspec_kw: dict[str, Any] | None = ...,
+ ) -> Axes: ...
+ @overload
def subplots(
self,
nrows: int = ...,
@@ -100,11 +114,11 @@ class FigureBase(Artist):
sharex: bool | Literal["none", "all", "row", "col"] = ...,
sharey: bool | Literal["none", "all", "row", "col"] = ...,
squeeze: Literal[False],
- width_ratios: ArrayLike | None = ...,
- height_ratios: ArrayLike | None = ...,
+ width_ratios: Sequence[float] | None = ...,
+ height_ratios: Sequence[float] | None = ...,
subplot_kw: dict[str, Any] | None = ...,
- gridspec_kw: dict[str, Any] | None = ...
- ) -> np.ndarray: ...
+ gridspec_kw: dict[str, Any] | None = ...,
+ ) -> np.ndarray: ... # TODO numpy/numpy#24738
@overload
def subplots(
self,
@@ -114,11 +128,11 @@ class FigureBase(Artist):
sharex: bool | Literal["none", "all", "row", "col"] = ...,
sharey: bool | Literal["none", "all", "row", "col"] = ...,
squeeze: bool = ...,
- width_ratios: ArrayLike | None = ...,
- height_ratios: ArrayLike | None = ...,
+ width_ratios: Sequence[float] | None = ...,
+ height_ratios: Sequence[float] | None = ...,
subplot_kw: dict[str, Any] | None = ...,
- gridspec_kw: dict[str, Any] | None = ...
- ) -> np.ndarray | SubplotBase | Axes: ...
+ gridspec_kw: dict[str, Any] | None = ...,
+ ) -> Axes | np.ndarray: ...
def delaxes(self, ax: Axes) -> None: ...
def clear(self, keep_observers: bool = ...) -> None: ...
def clf(self, keep_observers: bool = ...) -> None: ...
@@ -249,7 +263,6 @@ class SubFigure(FigureBase):
figure: Figure
subplotpars: SubplotParams
dpi_scale_trans: Affine2D
- canvas: FigureCanvasBase
transFigure: Transform
bbox_relative: Bbox
figbbox: BboxBase
@@ -268,6 +281,8 @@ class SubFigure(FigureBase):
**kwargs
) -> None: ...
@property
+ def canvas(self) -> FigureCanvasBase: ...
+ @property
def dpi(self) -> float: ...
@dpi.setter
def dpi(self, value: float) -> None: ...
diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py
index 312e8ee97246..813bee6eb623 100644
--- a/lib/matplotlib/font_manager.py
+++ b/lib/matplotlib/font_manager.py
@@ -266,8 +266,11 @@ def _get_fontconfig_fonts():
@lru_cache
def _get_macos_fonts():
"""Cache and list the font paths known to ``system_profiler SPFontsDataType``."""
- d, = plistlib.loads(
- subprocess.check_output(["system_profiler", "-xml", "SPFontsDataType"]))
+ try:
+ d, = plistlib.loads(
+ subprocess.check_output(["system_profiler", "-xml", "SPFontsDataType"]))
+ except (OSError, subprocess.CalledProcessError, plistlib.InvalidFileException):
+ return []
return [Path(entry["path"]) for entry in d["_items"]]
diff --git a/lib/matplotlib/gridspec.pyi b/lib/matplotlib/gridspec.pyi
index 1ac1bb0b40e7..b6732ad8fafa 100644
--- a/lib/matplotlib/gridspec.pyi
+++ b/lib/matplotlib/gridspec.pyi
@@ -54,7 +54,7 @@ class GridSpecBase:
sharey: bool | Literal["all", "row", "col", "none"] = ...,
squeeze: Literal[True] = ...,
subplot_kw: dict[str, Any] | None = ...
- ) -> np.ndarray | SubplotBase | Axes: ...
+ ) -> np.ndarray | Axes: ...
class GridSpec(GridSpecBase):
left: float | None
diff --git a/lib/matplotlib/path.py b/lib/matplotlib/path.py
index e72eb1a9ca73..94fd97d7b599 100644
--- a/lib/matplotlib/path.py
+++ b/lib/matplotlib/path.py
@@ -697,6 +697,9 @@ def to_polygons(self, transform=None, width=0, height=0, closed_only=True):
be simplified so that vertices outside of (0, 0), (width,
height) will be clipped.
+ The resulting polygons will be simplified if the
+ :attr:`Path.should_simplify` attribute of the path is `True`.
+
If *closed_only* is `True` (default), only closed polygons,
with the last point being the same as the first point, will be
returned. Any unclosed polylines in the path will be
diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py
index b1354341617d..8b4769342c7d 100644
--- a/lib/matplotlib/pyplot.py
+++ b/lib/matplotlib/pyplot.py
@@ -90,6 +90,9 @@
import PIL.Image
from numpy.typing import ArrayLike
+ import matplotlib.axes
+ import matplotlib.artist
+ import matplotlib.backend_bases
from matplotlib.axis import Tick
from matplotlib.axes._base import _AxesBase
from matplotlib.backend_bases import RendererBase, Event
@@ -301,10 +304,12 @@ def install_repl_displayhook() -> None:
# This code can be removed when Python 3.12, the latest version supported by
# IPython < 8.24, reaches end-of-life in late 2028.
from IPython.core.pylabtools import backend2gui
- # trigger IPython's eventloop integration, if available
ipython_gui_name = backend2gui.get(get_backend())
- if ipython_gui_name:
- ip.enable_gui(ipython_gui_name)
+ else:
+ _, ipython_gui_name = backend_registry.resolve_backend(get_backend())
+ # trigger IPython's eventloop integration, if available
+ if ipython_gui_name:
+ ip.enable_gui(ipython_gui_name)
def uninstall_repl_displayhook() -> None:
@@ -1545,6 +1550,57 @@ def subplot(*args, **kwargs) -> Axes:
return ax
+@overload
+def subplots(
+ nrows: Literal[1] = ...,
+ ncols: Literal[1] = ...,
+ *,
+ sharex: bool | Literal["none", "all", "row", "col"] = ...,
+ sharey: bool | Literal["none", "all", "row", "col"] = ...,
+ squeeze: Literal[True] = ...,
+ width_ratios: Sequence[float] | None = ...,
+ height_ratios: Sequence[float] | None = ...,
+ subplot_kw: dict[str, Any] | None = ...,
+ gridspec_kw: dict[str, Any] | None = ...,
+ **fig_kw
+) -> tuple[Figure, Axes]:
+ ...
+
+
+@overload
+def subplots(
+ nrows: int = ...,
+ ncols: int = ...,
+ *,
+ sharex: bool | Literal["none", "all", "row", "col"] = ...,
+ sharey: bool | Literal["none", "all", "row", "col"] = ...,
+ squeeze: Literal[False],
+ width_ratios: Sequence[float] | None = ...,
+ height_ratios: Sequence[float] | None = ...,
+ subplot_kw: dict[str, Any] | None = ...,
+ gridspec_kw: dict[str, Any] | None = ...,
+ **fig_kw
+) -> tuple[Figure, np.ndarray]: # TODO numpy/numpy#24738
+ ...
+
+
+@overload
+def subplots(
+ nrows: int = ...,
+ ncols: int = ...,
+ *,
+ sharex: bool | Literal["none", "all", "row", "col"] = ...,
+ sharey: bool | Literal["none", "all", "row", "col"] = ...,
+ squeeze: bool = ...,
+ width_ratios: Sequence[float] | None = ...,
+ height_ratios: Sequence[float] | None = ...,
+ subplot_kw: dict[str, Any] | None = ...,
+ gridspec_kw: dict[str, Any] | None = ...,
+ **fig_kw
+) -> tuple[Figure, Axes | np.ndarray]:
+ ...
+
+
def subplots(
nrows: int = 1, ncols: int = 1, *,
sharex: bool | Literal["none", "all", "row", "col"] = False,
@@ -1583,8 +1639,9 @@ def subplots(
on, use `~matplotlib.axes.Axes.tick_params`.
When subplots have a shared axis that has units, calling
- `~matplotlib.axis.Axis.set_units` will update each axis with the
- new units.
+ `.Axis.set_units` will update each axis with the new units.
+
+ Note that it is not possible to unshare axes.
squeeze : bool, default: True
- If True, extra dimensions are squeezed out from the returned
@@ -2113,6 +2170,21 @@ def xticks(
**kwargs
`.Text` properties can be used to control the appearance of the labels.
+ .. warning::
+
+ This only sets the properties of the current ticks, which is
+ only sufficient if you either pass *ticks*, resulting in a
+ fixed list of ticks, or if the plot is static.
+
+ Ticks are not guaranteed to be persistent. Various operations
+ can create, delete and modify the Tick instances. There is an
+ imminent risk that these settings can get lost if you work on
+ the figure further (including also panning/zooming on a
+ displayed figure).
+
+ Use `~.pyplot.tick_params` instead if possible.
+
+
Returns
-------
locs
@@ -2184,6 +2256,20 @@ def yticks(
**kwargs
`.Text` properties can be used to control the appearance of the labels.
+ .. warning::
+
+ This only sets the properties of the current ticks, which is
+ only sufficient if you either pass *ticks*, resulting in a
+ fixed list of ticks, or if the plot is static.
+
+ Ticks are not guaranteed to be persistent. Various operations
+ can create, delete and modify the Tick instances. There is an
+ imminent risk that these settings can get lost if you work on
+ the figure further (including also panning/zooming on a
+ displayed figure).
+
+ Use `~.pyplot.tick_params` instead if possible.
+
Returns
-------
locs
@@ -2813,7 +2899,7 @@ def axhline(y: float = 0, xmin: float = 0, xmax: float = 1, **kwargs) -> Line2D:
@_copy_docstring_and_deprecators(Axes.axhspan)
def axhspan(
ymin: float, ymax: float, xmin: float = 0, xmax: float = 1, **kwargs
-) -> Polygon:
+) -> Rectangle:
return gca().axhspan(ymin, ymax, xmin=xmin, xmax=xmax, **kwargs)
@@ -2851,7 +2937,7 @@ def axvline(x: float = 0, ymin: float = 0, ymax: float = 1, **kwargs) -> Line2D:
@_copy_docstring_and_deprecators(Axes.axvspan)
def axvspan(
xmin: float, xmax: float, ymin: float = 0, ymax: float = 1, **kwargs
-) -> Polygon:
+) -> Rectangle:
return gca().axvspan(xmin, xmax, ymin=ymin, ymax=ymax, **kwargs)
diff --git a/lib/matplotlib/sphinxext/meson.build b/lib/matplotlib/sphinxext/meson.build
index 5dc7388384eb..35bb96fecbe1 100644
--- a/lib/matplotlib/sphinxext/meson.build
+++ b/lib/matplotlib/sphinxext/meson.build
@@ -3,6 +3,7 @@ python_sources = [
'figmpl_directive.py',
'mathmpl.py',
'plot_directive.py',
+ 'roles.py',
]
typing_sources = [
diff --git a/lib/matplotlib/sphinxext/roles.py b/lib/matplotlib/sphinxext/roles.py
new file mode 100644
index 000000000000..301adcd8a5f5
--- /dev/null
+++ b/lib/matplotlib/sphinxext/roles.py
@@ -0,0 +1,147 @@
+"""
+Custom roles for the Matplotlib documentation.
+
+.. warning::
+
+ These roles are considered semi-public. They are only intended to be used in
+ the Matplotlib documentation.
+
+However, it can happen that downstream packages end up pulling these roles into
+their documentation, which will result in documentation build errors. The following
+describes the exact mechanism and how to fix the errors.
+
+There are two ways, Matplotlib docstrings can end up in downstream documentation.
+You have to subclass a Matplotlib class and either use the ``:inherited-members:``
+option in your autodoc configuration, or you have to override a method without
+specifying a new docstring; the new method will inherit the original docstring and
+still render in your autodoc. If the docstring contains one of the custom sphinx
+roles, you'll see one of the following error messages:
+
+.. code-block:: none
+
+ Unknown interpreted text role "mpltype".
+ Unknown interpreted text role "rc".
+
+To fix this, you can add this module as extension to your sphinx :file:`conf.py`::
+
+ extensions = [
+ 'matplotlib.sphinxext.roles',
+ # Other extensions.
+ ]
+
+.. warning::
+
+ Direct use of these roles in other packages is not officially supported. We
+ reserve the right to modify or remove these roles without prior notification.
+"""
+
+from urllib.parse import urlsplit, urlunsplit
+
+from docutils import nodes
+
+import matplotlib
+from matplotlib import rcParamsDefault
+
+
+class _QueryReference(nodes.Inline, nodes.TextElement):
+ """
+ Wraps a reference or pending reference to add a query string.
+
+ The query string is generated from the attributes added to this node.
+
+ Also equivalent to a `~docutils.nodes.literal` node.
+ """
+
+ def to_query_string(self):
+ """Generate query string from node attributes."""
+ return '&'.join(f'{name}={value}' for name, value in self.attlist())
+
+
+def _visit_query_reference_node(self, node):
+ """
+ Resolve *node* into query strings on its ``reference`` children.
+
+ Then act as if this is a `~docutils.nodes.literal`.
+ """
+ query = node.to_query_string()
+ for refnode in node.findall(nodes.reference):
+ uri = urlsplit(refnode['refuri'])._replace(query=query)
+ refnode['refuri'] = urlunsplit(uri)
+
+ self.visit_literal(node)
+
+
+def _depart_query_reference_node(self, node):
+ """
+ Act as if this is a `~docutils.nodes.literal`.
+ """
+ self.depart_literal(node)
+
+
+def _rcparam_role(name, rawtext, text, lineno, inliner, options=None, content=None):
+ """
+ Sphinx role ``:rc:`` to highlight and link ``rcParams`` entries.
+
+ Usage: Give the desired ``rcParams`` key as parameter.
+
+ :code:`:rc:`figure.dpi`` will render as: :rc:`figure.dpi`
+ """
+ # Generate a pending cross-reference so that Sphinx will ensure this link
+ # isn't broken at some point in the future.
+ title = f'rcParams["{text}"]'
+ target = 'matplotlibrc-sample'
+ ref_nodes, messages = inliner.interpreted(title, f'{title} <{target}>',
+ 'ref', lineno)
+
+ qr = _QueryReference(rawtext, highlight=text)
+ qr += ref_nodes
+ node_list = [qr]
+
+ # The default backend would be printed as "agg", but that's not correct (as
+ # the default is actually determined by fallback).
+ if text in rcParamsDefault and text != "backend":
+ node_list.extend([
+ nodes.Text(' (default: '),
+ nodes.literal('', repr(rcParamsDefault[text])),
+ nodes.Text(')'),
+ ])
+
+ return node_list, messages
+
+
+def _mpltype_role(name, rawtext, text, lineno, inliner, options=None, content=None):
+ """
+ Sphinx role ``:mpltype:`` for custom matplotlib types.
+
+ In Matplotlib, there are a number of type-like concepts that do not have a
+ direct type representation; example: color. This role allows to properly
+ highlight them in the docs and link to their definition.
+
+ Currently supported values:
+
+ - :code:`:mpltype:`color`` will render as: :mpltype:`color`
+
+ """
+ mpltype = text
+ type_to_link_target = {
+ 'color': 'colors_def',
+ }
+ if mpltype not in type_to_link_target:
+ raise ValueError(f"Unknown mpltype: {mpltype!r}")
+
+ node_list, messages = inliner.interpreted(
+ mpltype, f'{mpltype} <{type_to_link_target[mpltype]}>', 'ref', lineno)
+ return node_list, messages
+
+
+def setup(app):
+ app.add_role("rc", _rcparam_role)
+ app.add_role("mpltype", _mpltype_role)
+ app.add_node(
+ _QueryReference,
+ html=(_visit_query_reference_node, _depart_query_reference_node),
+ latex=(_visit_query_reference_node, _depart_query_reference_node),
+ text=(_visit_query_reference_node, _depart_query_reference_node),
+ )
+ return {"version": matplotlib.__version__,
+ "parallel_read_safe": True, "parallel_write_safe": True}
diff --git a/lib/matplotlib/testing/__init__.py b/lib/matplotlib/testing/__init__.py
index 8e60267ed608..19113d399626 100644
--- a/lib/matplotlib/testing/__init__.py
+++ b/lib/matplotlib/testing/__init__.py
@@ -211,3 +211,24 @@ def ipython_in_subprocess(requested_backend_or_gui_framework, all_expected_backe
)
assert proc.stdout.strip().endswith(f"'{expected_backend}'")
+
+
+def is_ci_environment():
+ # Common CI variables
+ ci_environment_variables = [
+ 'CI', # Generic CI environment variable
+ 'CONTINUOUS_INTEGRATION', # Generic CI environment variable
+ 'TRAVIS', # Travis CI
+ 'CIRCLECI', # CircleCI
+ 'JENKINS', # Jenkins
+ 'GITLAB_CI', # GitLab CI
+ 'GITHUB_ACTIONS', # GitHub Actions
+ 'TEAMCITY_VERSION' # TeamCity
+ # Add other CI environment variables as needed
+ ]
+
+ for env_var in ci_environment_variables:
+ if os.getenv(env_var):
+ return True
+
+ return False
diff --git a/lib/matplotlib/testing/__init__.pyi b/lib/matplotlib/testing/__init__.pyi
index 1f52a8ccb8ee..6917b6a5a380 100644
--- a/lib/matplotlib/testing/__init__.pyi
+++ b/lib/matplotlib/testing/__init__.pyi
@@ -51,3 +51,4 @@ def ipython_in_subprocess(
requested_backend_or_gui_framework: str,
all_expected_backends: dict[tuple[int, int], str],
) -> None: ...
+def is_ci_environment() -> bool: ...
diff --git a/lib/matplotlib/tests/baseline_images/test_axes/sticky_tolerance.png b/lib/matplotlib/tests/baseline_images/test_axes/sticky_tolerance.png
new file mode 100644
index 000000000000..a3fb13d0716a
Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/sticky_tolerance.png differ
diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py
index 0ed5a11c1398..3c0407ee4098 100644
--- a/lib/matplotlib/tests/test_axes.py
+++ b/lib/matplotlib/tests/test_axes.py
@@ -682,6 +682,25 @@ def test_sticky_shared_axes(fig_test, fig_ref):
ax0.pcolormesh(Z)
+@image_comparison(['sticky_tolerance.png'], remove_text=True, style="mpl20")
+def test_sticky_tolerance():
+ fig, axs = plt.subplots(2, 2)
+
+ width = .1
+
+ axs.flat[0].bar(x=0, height=width, bottom=20000.6)
+ axs.flat[0].bar(x=1, height=width, bottom=20000.1)
+
+ axs.flat[1].bar(x=0, height=-width, bottom=20000.6)
+ axs.flat[1].bar(x=1, height=-width, bottom=20000.1)
+
+ axs.flat[2].barh(y=0, width=-width, left=-20000.6)
+ axs.flat[2].barh(y=1, width=-width, left=-20000.1)
+
+ axs.flat[3].barh(y=0, width=width, left=-20000.6)
+ axs.flat[3].barh(y=1, width=width, left=-20000.1)
+
+
def test_nargs_stem():
with pytest.raises(TypeError, match='0 were given'):
# stem() takes 1-3 arguments.
@@ -8176,10 +8195,10 @@ def test_relative_ticklabel_sizes(size):
def test_multiplot_autoscale():
fig = plt.figure()
ax1, ax2 = fig.subplots(2, 1, sharex='all')
- ax1.scatter([1, 2, 3, 4], [2, 3, 2, 3])
+ ax1.plot([18000, 18250, 18500, 18750], [2, 3, 2, 3])
ax2.axhspan(-5, 5)
xlim = ax1.get_xlim()
- assert np.allclose(xlim, [0.5, 4.5])
+ assert np.allclose(xlim, [18000, 18800])
def test_sharing_does_not_link_positions():
diff --git a/lib/matplotlib/tests/test_backend_registry.py b/lib/matplotlib/tests/test_backend_registry.py
index eaf8417e7a5f..80c2ce4fc51a 100644
--- a/lib/matplotlib/tests/test_backend_registry.py
+++ b/lib/matplotlib/tests/test_backend_registry.py
@@ -86,6 +86,15 @@ def test_is_valid_backend(backend, is_valid):
assert backend_registry.is_valid_backend(backend) == is_valid
+@pytest.mark.parametrize("backend, normalized", [
+ ("agg", "matplotlib.backends.backend_agg"),
+ ("QtAgg", "matplotlib.backends.backend_qtagg"),
+ ("module://Anything", "Anything"),
+])
+def test_backend_normalization(backend, normalized):
+ assert backend_registry._backend_module_name(backend) == normalized
+
+
def test_deprecated_rcsetup_attributes():
match = "was deprecated in Matplotlib 3.9"
with pytest.warns(mpl.MatplotlibDeprecationWarning, match=match):
@@ -121,6 +130,17 @@ def test_entry_point_name_duplicate(clear_backend_registry):
[('some_name', 'module1'), ('some_name', 'module2')])
+def test_entry_point_identical(clear_backend_registry):
+ # Issue https://github.com/matplotlib/matplotlib/issues/28367
+ # Multiple entry points with the same name and value (value is the module)
+ # are acceptable.
+ n = len(backend_registry._name_to_module)
+ backend_registry._validate_and_store_entry_points(
+ [('some_name', 'some.module'), ('some_name', 'some.module')])
+ assert len(backend_registry._name_to_module) == n+1
+ assert backend_registry._name_to_module['some_name'] == 'module://some.module'
+
+
def test_entry_point_name_is_module(clear_backend_registry):
with pytest.raises(RuntimeError):
backend_registry._validate_and_store_entry_points(
diff --git a/lib/matplotlib/tests/test_backend_template.py b/lib/matplotlib/tests/test_backend_template.py
index d7e2a5cd1266..964d15c1559a 100644
--- a/lib/matplotlib/tests/test_backend_template.py
+++ b/lib/matplotlib/tests/test_backend_template.py
@@ -49,3 +49,14 @@ def test_show_old_global_api(monkeypatch):
mpl.use("module://mpl_test_backend")
plt.show()
mock_show.assert_called_with()
+
+
+def test_load_case_sensitive(monkeypatch):
+ mpl_test_backend = SimpleNamespace(**vars(backend_template))
+ mock_show = MagicMock()
+ monkeypatch.setattr(
+ mpl_test_backend.FigureManagerTemplate, "pyplot_show", mock_show)
+ monkeypatch.setitem(sys.modules, "mpl_Test_Backend", mpl_test_backend)
+ mpl.use("module://mpl_Test_Backend")
+ plt.show()
+ mock_show.assert_called_with()
diff --git a/lib/matplotlib/tests/test_backends_interactive.py b/lib/matplotlib/tests/test_backends_interactive.py
index 6830e7d5c845..d624b5db0ac2 100644
--- a/lib/matplotlib/tests/test_backends_interactive.py
+++ b/lib/matplotlib/tests/test_backends_interactive.py
@@ -19,7 +19,7 @@
import matplotlib as mpl
from matplotlib import _c_internal_utils
from matplotlib.backend_tools import ToolToggleBase
-from matplotlib.testing import subprocess_run_helper as _run_helper
+from matplotlib.testing import subprocess_run_helper as _run_helper, is_ci_environment
class _WaitForStringPopen(subprocess.Popen):
@@ -110,27 +110,6 @@ def _get_testable_interactive_backends():
for env, marks in _get_available_interactive_backends()]
-def is_ci_environment():
- # Common CI variables
- ci_environment_variables = [
- 'CI', # Generic CI environment variable
- 'CONTINUOUS_INTEGRATION', # Generic CI environment variable
- 'TRAVIS', # Travis CI
- 'CIRCLECI', # CircleCI
- 'JENKINS', # Jenkins
- 'GITLAB_CI', # GitLab CI
- 'GITHUB_ACTIONS', # GitHub Actions
- 'TEAMCITY_VERSION' # TeamCity
- # Add other CI environment variables as needed
- ]
-
- for env_var in ci_environment_variables:
- if os.getenv(env_var):
- return True
-
- return False
-
-
# Reasonable safe values for slower CI/Remote and local architectures.
_test_timeout = 120 if is_ci_environment() else 20
diff --git a/lib/matplotlib/tests/test_colors.py b/lib/matplotlib/tests/test_colors.py
index 63f2d4f00399..4fd9f86c06e3 100644
--- a/lib/matplotlib/tests/test_colors.py
+++ b/lib/matplotlib/tests/test_colors.py
@@ -19,7 +19,7 @@
import matplotlib.scale as mscale
from matplotlib.rcsetup import cycler
from matplotlib.testing.decorators import image_comparison, check_figures_equal
-from matplotlib.colors import to_rgba_array
+from matplotlib.colors import is_color_like, to_rgba_array
@pytest.mark.parametrize('N, result', [
@@ -1697,3 +1697,16 @@ def test_to_rgba_array_none_color_with_alpha_param():
assert_array_equal(
to_rgba_array(c, alpha), [[0., 0., 1., 1.], [0., 0., 0., 0.]]
)
+
+
+@pytest.mark.parametrize('input, expected',
+ [('red', True),
+ (('red', 0.5), True),
+ (('red', 2), False),
+ (['red', 0.5], False),
+ (('red', 'blue'), False),
+ (['red', 'blue'], False),
+ ('C3', True),
+ (('C3', 0.5), True)])
+def test_is_color_like(input, expected):
+ assert is_color_like(input) is expected
diff --git a/lib/matplotlib/tests/test_figure.py b/lib/matplotlib/tests/test_figure.py
index 58aecd3dea8b..6e6daa77062d 100644
--- a/lib/matplotlib/tests/test_figure.py
+++ b/lib/matplotlib/tests/test_figure.py
@@ -1733,3 +1733,27 @@ def test_warn_colorbar_mismatch():
subfig3_1.colorbar(im3_2) # should not warn
with pytest.warns(UserWarning, match="different Figure"):
subfig3_1.colorbar(im4_1)
+
+
+def test_subfigure_stale_propagation():
+ fig = plt.figure()
+
+ fig.draw_without_rendering()
+ assert not fig.stale
+
+ sfig1 = fig.subfigures()
+ assert fig.stale
+
+ fig.draw_without_rendering()
+ assert not fig.stale
+ assert not sfig1.stale
+
+ sfig2 = sfig1.subfigures()
+ assert fig.stale
+
+ fig.draw_without_rendering()
+ assert not fig.stale
+ assert not sfig2.stale
+
+ sfig2.stale = True
+ assert fig.stale
diff --git a/lib/matplotlib/tests/test_image.py b/lib/matplotlib/tests/test_image.py
index 1602f86716cb..a043d3aec983 100644
--- a/lib/matplotlib/tests/test_image.py
+++ b/lib/matplotlib/tests/test_image.py
@@ -1585,3 +1585,20 @@ def test_non_transdata_image_does_not_touch_aspect():
assert ax.get_aspect() == 1
ax.imshow(im, transform=ax.transAxes, aspect=2)
assert ax.get_aspect() == 2
+
+
+@pytest.mark.parametrize(
+ 'dtype',
+ ('float64', 'float32', 'int16', 'uint16', 'int8', 'uint8'),
+)
+@pytest.mark.parametrize('ndim', (2, 3))
+def test_resample_dtypes(dtype, ndim):
+ # Issue 28448, incorrect dtype comparisons in C++ image_resample can raise
+ # ValueError: arrays must be of dtype byte, short, float32 or float64
+ rng = np.random.default_rng(4181)
+ shape = (2, 2) if ndim == 2 else (2, 2, 3)
+ data = rng.uniform(size=shape).astype(np.dtype(dtype, copy=True))
+ fig, ax = plt.subplots()
+ axes_image = ax.imshow(data)
+ # Before fix the following raises ValueError for some dtypes.
+ axes_image.make_image(None)[0]
diff --git a/lib/matplotlib/tests/test_pickle.py b/lib/matplotlib/tests/test_pickle.py
index 7e7ccc14bf8f..1474a67d28aa 100644
--- a/lib/matplotlib/tests/test_pickle.py
+++ b/lib/matplotlib/tests/test_pickle.py
@@ -1,5 +1,7 @@
from io import BytesIO
import ast
+import os
+import sys
import pickle
import pickletools
@@ -8,7 +10,7 @@
import matplotlib as mpl
from matplotlib import cm
-from matplotlib.testing import subprocess_run_helper
+from matplotlib.testing import subprocess_run_helper, is_ci_environment
from matplotlib.testing.decorators import check_figures_equal
from matplotlib.dates import rrulewrapper
from matplotlib.lines import VertexSelector
@@ -93,6 +95,11 @@ def _generate_complete_test_figure(fig_ref):
plt.errorbar(x, x * -0.5, xerr=0.2, yerr=0.4, label='$-.5 x$')
plt.legend(draggable=True)
+ # Ensure subfigure parenting works.
+ subfigs = fig_ref.subfigures(2)
+ subfigs[0].subplots(1, 2)
+ subfigs[1].subplots(1, 2)
+
fig_ref.align_ylabels() # Test handling of _align_label_groups Groupers.
@@ -302,3 +309,23 @@ def test_cycler():
ax = pickle.loads(pickle.dumps(ax))
l, = ax.plot([3, 4])
assert l.get_color() == "m"
+
+
+# Run under an interactive backend to test that we don't try to pickle the
+# (interactive and non-picklable) canvas.
+def _test_axeswidget_interactive():
+ ax = plt.figure().add_subplot()
+ pickle.dumps(mpl.widgets.Button(ax, "button"))
+
+
+@pytest.mark.xfail( # https://github.com/actions/setup-python/issues/649
+ ('TF_BUILD' in os.environ or 'GITHUB_ACTION' in os.environ) and
+ sys.platform == 'darwin' and sys.version_info[:2] < (3, 11),
+ reason='Tk version mismatch on Azure macOS CI'
+ )
+def test_axeswidget_interactive():
+ subprocess_run_helper(
+ _test_axeswidget_interactive,
+ timeout=120 if is_ci_environment() else 20,
+ extra_env={'MPLBACKEND': 'tkagg'}
+ )
diff --git a/lib/matplotlib/tests/test_text.py b/lib/matplotlib/tests/test_text.py
index f8837d8a5f1b..8904337f68ba 100644
--- a/lib/matplotlib/tests/test_text.py
+++ b/lib/matplotlib/tests/test_text.py
@@ -15,6 +15,7 @@
from matplotlib.font_manager import FontProperties
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
+from matplotlib.gridspec import GridSpec
import matplotlib.transforms as mtransforms
from matplotlib.testing.decorators import check_figures_equal, image_comparison
from matplotlib.testing._markers import needs_usetex
@@ -707,9 +708,13 @@ def test_large_subscript_title():
(0.3, 0, 'right'),
(0.3, 185, 'left')])
def test_wrap(x, rotation, halign):
- fig = plt.figure(figsize=(6, 6))
+ fig = plt.figure(figsize=(18, 18))
+ gs = GridSpec(nrows=3, ncols=3, figure=fig)
+ subfig = fig.add_subfigure(gs[1, 1])
+ # we only use the central subfigure, which does not align with any
+ # figure boundary, to ensure only subfigure boundaries are relevant
s = 'This is a very long text that should be wrapped multiple times.'
- text = fig.text(x, 0.7, s, wrap=True, rotation=rotation, ha=halign)
+ text = subfig.text(x, 0.7, s, wrap=True, rotation=rotation, ha=halign)
fig.canvas.draw()
assert text._get_wrapped_text() == ('This is a very long\n'
'text that should be\n'
diff --git a/lib/matplotlib/tests/test_ticker.py b/lib/matplotlib/tests/test_ticker.py
index 36b83c95b3d3..ac68a5d90b14 100644
--- a/lib/matplotlib/tests/test_ticker.py
+++ b/lib/matplotlib/tests/test_ticker.py
@@ -130,6 +130,14 @@ def test_view_limits_round_numbers_with_offset(self):
loc = mticker.MultipleLocator(base=3.147, offset=1.3)
assert_almost_equal(loc.view_limits(-4, 4), (-4.994, 4.447))
+ def test_view_limits_single_bin(self):
+ """
+ Test that 'round_numbers' works properly with a single bin.
+ """
+ with mpl.rc_context({'axes.autolimit_mode': 'round_numbers'}):
+ loc = mticker.MaxNLocator(nbins=1)
+ assert_almost_equal(loc.view_limits(-2.3, 2.3), (-4, 4))
+
def test_set_params(self):
"""
Create multiple locator with 0.7 base, and change it to something else.
diff --git a/lib/matplotlib/tests/test_transforms.py b/lib/matplotlib/tests/test_transforms.py
index 959814de82db..3d12b90d5210 100644
--- a/lib/matplotlib/tests/test_transforms.py
+++ b/lib/matplotlib/tests/test_transforms.py
@@ -667,6 +667,13 @@ def test_contains_branch(self):
assert not self.stack1.contains_branch(self.tn1 + self.ta2)
+ blend = mtransforms.BlendedGenericTransform(self.tn2, self.stack2)
+ x, y = blend.contains_branch_seperately(self.stack2_subset)
+ stack_blend = self.tn3 + blend
+ sx, sy = stack_blend.contains_branch_seperately(self.stack2_subset)
+ assert x is sx is False
+ assert y is sy is True
+
def test_affine_simplification(self):
# tests that a transform stack only calls as much is absolutely
# necessary "non-affine" allowing the best possible optimization with
diff --git a/lib/matplotlib/text.py b/lib/matplotlib/text.py
index 40cd8c8cd6f7..af990ec1bf9f 100644
--- a/lib/matplotlib/text.py
+++ b/lib/matplotlib/text.py
@@ -606,6 +606,9 @@ def set_wrap(self, wrap):
"""
Set whether the text can be wrapped.
+ Wrapping makes sure the text is confined to the (sub)figure box. It
+ does not take into account any other artists.
+
Parameters
----------
wrap : bool
@@ -653,16 +656,16 @@ def _get_dist_to_box(self, rotation, x0, y0, figure_box):
"""
if rotation > 270:
quad = rotation - 270
- h1 = y0 / math.cos(math.radians(quad))
+ h1 = (y0 - figure_box.y0) / math.cos(math.radians(quad))
h2 = (figure_box.x1 - x0) / math.cos(math.radians(90 - quad))
elif rotation > 180:
quad = rotation - 180
- h1 = x0 / math.cos(math.radians(quad))
- h2 = y0 / math.cos(math.radians(90 - quad))
+ h1 = (x0 - figure_box.x0) / math.cos(math.radians(quad))
+ h2 = (y0 - figure_box.y0) / math.cos(math.radians(90 - quad))
elif rotation > 90:
quad = rotation - 90
h1 = (figure_box.y1 - y0) / math.cos(math.radians(quad))
- h2 = x0 / math.cos(math.radians(90 - quad))
+ h2 = (x0 - figure_box.x0) / math.cos(math.radians(90 - quad))
else:
h1 = (figure_box.x1 - x0) / math.cos(math.radians(rotation))
h2 = (figure_box.y1 - y0) / math.cos(math.radians(90 - rotation))
diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py
index f042372a7be9..2b00937f9e29 100644
--- a/lib/matplotlib/ticker.py
+++ b/lib/matplotlib/ticker.py
@@ -2137,7 +2137,10 @@ def _raw_ticks(self, vmin, vmax):
large_steps = large_steps & (floored_vmaxs >= _vmax)
# Find index of smallest large step
- istep = np.nonzero(large_steps)[0][0]
+ if any(large_steps):
+ istep = np.nonzero(large_steps)[0][0]
+ else:
+ istep = len(steps) - 1
# Start at smallest of the steps greater than the raw step, and check
# if it provides enough ticks. If not, work backwards through
diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py
index 5003e2113930..3575bd1fc14d 100644
--- a/lib/matplotlib/transforms.py
+++ b/lib/matplotlib/transforms.py
@@ -1423,7 +1423,7 @@ def contains_branch_seperately(self, other_transform):
'transforms with 2 output dimensions')
# for a non-blended transform each separate dimension is the same, so
# just return the appropriate shape.
- return [self.contains_branch(other_transform)] * 2
+ return (self.contains_branch(other_transform), ) * 2
def __sub__(self, other):
"""
@@ -2404,6 +2404,15 @@ def _iter_break_from_left_to_right(self):
for left, right in self._b._iter_break_from_left_to_right():
yield self._a + left, right
+ def contains_branch_seperately(self, other_transform):
+ # docstring inherited
+ if self.output_dims != 2:
+ raise ValueError('contains_branch_seperately only supports '
+ 'transforms with 2 output dimensions')
+ if self == other_transform:
+ return (True, True)
+ return self._b.contains_branch_seperately(other_transform)
+
depth = property(lambda self: self._a.depth + self._b.depth)
is_affine = property(lambda self: self._a.is_affine and self._b.is_affine)
is_separable = property(
diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py
index eaa35e25440b..a298f3ae3d6a 100644
--- a/lib/matplotlib/widgets.py
+++ b/lib/matplotlib/widgets.py
@@ -90,22 +90,6 @@ def ignore(self, event):
"""
return not self.active
- def _changed_canvas(self):
- """
- Someone has switched the canvas on us!
-
- This happens if `savefig` needs to save to a format the previous
- backend did not support (e.g. saving a figure using an Agg based
- backend saved to a vector format).
-
- Returns
- -------
- bool
- True if the canvas has been changed.
-
- """
- return self.canvas is not self.ax.figure.canvas
-
class AxesWidget(Widget):
"""
@@ -131,9 +115,10 @@ class AxesWidget(Widget):
def __init__(self, ax):
self.ax = ax
- self.canvas = ax.figure.canvas
self._cids = []
+ canvas = property(lambda self: self.ax.figure.canvas)
+
def connect_event(self, event, callback):
"""
Connect a callback function with an event.
@@ -1001,14 +986,8 @@ class CheckButtons(AxesWidget):
----------
ax : `~matplotlib.axes.Axes`
The parent Axes for the widget.
-
labels : list of `~matplotlib.text.Text`
-
- rectangles : list of `~matplotlib.patches.Rectangle`
-
- lines : list of (`.Line2D`, `.Line2D`) pairs
- List of lines for the x's in the checkboxes. These lines exist for
- each box, but have ``set_visible(False)`` when its box is not checked.
+ The text label objects of the check buttons.
"""
def __init__(self, ax, labels, actives=None, *, useblit=True,
@@ -1106,7 +1085,7 @@ def __init__(self, ax, labels, actives=None, *, useblit=True,
def _clear(self, event):
"""Internal event handler to clear the buttons."""
- if self.ignore(event) or self._changed_canvas():
+ if self.ignore(event) or self.canvas.is_saving():
return
self._background = self.canvas.copy_from_bbox(self.ax.bbox)
self.ax.draw_artist(self._checks)
@@ -1571,8 +1550,6 @@ class RadioButtons(AxesWidget):
The color of the selected button.
labels : list of `.Text`
The button labels.
- circles : list of `~.patches.Circle`
- The buttons.
value_selected : str
The label text of the currently selected button.
index_selected : int
@@ -1685,7 +1662,7 @@ def __init__(self, ax, labels, active=0, activecolor=None, *,
def _clear(self, event):
"""Internal event handler to clear the buttons."""
- if self.ignore(event) or self._changed_canvas():
+ if self.ignore(event) or self.canvas.is_saving():
return
self._background = self.canvas.copy_from_bbox(self.ax.bbox)
self.ax.draw_artist(self._buttons)
@@ -1751,11 +1728,6 @@ def activecolor(self, activecolor):
colors._check_color_like(activecolor=activecolor)
self._activecolor = activecolor
self.set_radio_props({'facecolor': activecolor})
- # Make sure the deprecated version is updated.
- # Remove once circles is removed.
- labels = [label.get_text() for label in self.labels]
- with cbook._setattr_cm(self, eventson=False):
- self.set_active(labels.index(self.value_selected))
def set_active(self, index):
"""
@@ -1946,7 +1918,7 @@ def __init__(self, ax, *, horizOn=True, vertOn=True, useblit=False,
def clear(self, event):
"""Internal event handler to clear the cursor."""
- if self.ignore(event) or self._changed_canvas():
+ if self.ignore(event) or self.canvas.is_saving():
return
if self.useblit:
self.background = self.canvas.copy_from_bbox(self.ax.bbox)
@@ -2586,9 +2558,7 @@ def __init__(self, ax, onselect, direction, *, minspan=0, useblit=False,
self.drag_from_anywhere = drag_from_anywhere
self.ignore_event_outside = ignore_event_outside
- # Reset canvas so that `new_axes` connects events.
- self.canvas = None
- self.new_axes(ax, _props=props)
+ self.new_axes(ax, _props=props, _init=True)
# Setup handles
self._handle_props = {
@@ -2601,14 +2571,15 @@ def __init__(self, ax, onselect, direction, *, minspan=0, useblit=False,
self._active_handle = None
- def new_axes(self, ax, *, _props=None):
+ def new_axes(self, ax, *, _props=None, _init=False):
"""Set SpanSelector to operate on a new Axes."""
- self.ax = ax
- if self.canvas is not ax.figure.canvas:
+ reconnect = False
+ if _init or self.canvas is not ax.figure.canvas:
if self.canvas is not None:
self.disconnect_events()
-
- self.canvas = ax.figure.canvas
+ reconnect = True
+ self.ax = ax
+ if reconnect:
self.connect_default_events()
# Reset
diff --git a/lib/matplotlib/widgets.pyi b/lib/matplotlib/widgets.pyi
index c85ad2158ee7..58adf85aae60 100644
--- a/lib/matplotlib/widgets.pyi
+++ b/lib/matplotlib/widgets.pyi
@@ -33,8 +33,9 @@ class Widget:
class AxesWidget(Widget):
ax: Axes
- canvas: FigureCanvasBase | None
def __init__(self, ax: Axes) -> None: ...
+ @property
+ def canvas(self) -> FigureCanvasBase | None: ...
def connect_event(self, event: Event, callback: Callable) -> None: ...
def disconnect_events(self) -> None: ...
@@ -310,7 +311,6 @@ class SpanSelector(_SelectorWidget):
grab_range: float
drag_from_anywhere: bool
ignore_event_outside: bool
- canvas: FigureCanvasBase | None
def __init__(
self,
ax: Axes,
@@ -330,7 +330,13 @@ class SpanSelector(_SelectorWidget):
ignore_event_outside: bool = ...,
snap_values: ArrayLike | None = ...,
) -> None: ...
- def new_axes(self, ax: Axes, *, _props: dict[str, Any] | None = ...) -> None: ...
+ def new_axes(
+ self,
+ ax: Axes,
+ *,
+ _props: dict[str, Any] | None = ...,
+ _init: bool = ...,
+ ) -> None: ...
def connect_default_events(self) -> None: ...
@property
def direction(self) -> Literal["horizontal", "vertical"]: ...
diff --git a/lib/mpl_toolkits/mplot3d/axes3d.py b/lib/mpl_toolkits/mplot3d/axes3d.py
index d0f5c8d2b23b..71cd8f062d40 100644
--- a/lib/mpl_toolkits/mplot3d/axes3d.py
+++ b/lib/mpl_toolkits/mplot3d/axes3d.py
@@ -83,7 +83,8 @@ def __init__(
axis. A positive angle spins the camera clockwise, causing the
scene to rotate counter-clockwise.
sharez : Axes3D, optional
- Other Axes to share z-limits with.
+ Other Axes to share z-limits with. Note that it is not possible to
+ unshare axes.
proj_type : {'persp', 'ortho'}
The projection type, default 'persp'.
box_aspect : 3-tuple of floats, default: None
@@ -107,7 +108,8 @@ def __init__(
The focal length can be computed from a desired Field Of View via
the equation: focal_length = 1/tan(FOV/2)
shareview : Axes3D, optional
- Other Axes to share view angles with.
+ Other Axes to share view angles with. Note that it is not possible
+ to unshare axes.
**kwargs
Other optional keyword arguments:
@@ -383,7 +385,7 @@ def set_box_aspect(self, aspect, *, zoom=1):
# of the axes in mpl3.8.
aspect *= 1.8294640721620434 * 25/24 * zoom / np.linalg.norm(aspect)
- self._box_aspect = aspect
+ self._box_aspect = self._roll_to_vertical(aspect, reverse=True)
self.stale = True
def apply_aspect(self, position=None):
@@ -1191,9 +1193,23 @@ def set_proj_type(self, proj_type, focal_length=None):
f"None for proj_type = {proj_type}")
self._focal_length = np.inf
- def _roll_to_vertical(self, arr):
- """Roll arrays to match the different vertical axis."""
- return np.roll(arr, self._vertical_axis - 2)
+ def _roll_to_vertical(
+ self, arr: "np.typing.ArrayLike", reverse: bool = False
+ ) -> np.ndarray:
+ """
+ Roll arrays to match the different vertical axis.
+
+ Parameters
+ ----------
+ arr : ArrayLike
+ Array to roll.
+ reverse : bool, default: False
+ Reverse the direction of the roll.
+ """
+ if reverse:
+ return np.roll(arr, (self._vertical_axis - 2) * -1)
+ else:
+ return np.roll(arr, (self._vertical_axis - 2))
def get_proj(self):
"""Create the projection matrix from the current viewing position."""
@@ -1293,7 +1309,7 @@ def sharez(self, other):
This is equivalent to passing ``sharez=other`` when constructing the
Axes, and cannot be used if the z-axis is already being shared with
- another Axes.
+ another Axes. Note that it is not possible to unshare axes.
"""
_api.check_isinstance(Axes3D, other=other)
if self._sharez is not None and other is not self._sharez:
@@ -1310,9 +1326,9 @@ def shareview(self, other):
"""
Share the view angles with *other*.
- This is equivalent to passing ``shareview=other`` when
- constructing the Axes, and cannot be used if the view angles are
- already being shared with another Axes.
+ This is equivalent to passing ``shareview=other`` when constructing the
+ Axes, and cannot be used if the view angles are already being shared
+ with another Axes. Note that it is not possible to unshare axes.
"""
_api.check_isinstance(Axes3D, other=other)
if self._shareview is not None and other is not self._shareview:
@@ -1524,6 +1540,7 @@ def _on_move(self, event):
dazim = -(dy/h)*180*np.sin(roll) - (dx/w)*180*np.cos(roll)
elev = self.elev + delev
azim = self.azim + dazim
+ roll = self.roll
vertical_axis = self._axis_names[self._vertical_axis]
self.view_init(
elev=elev,
@@ -2561,7 +2578,7 @@ def tricontourf(self, *args, zdir='z', offset=None, **kwargs):
self._auto_scale_contourf(X, Y, Z, zdir, levels, had_data)
return cset
- def add_collection3d(self, col, zs=0, zdir='z'):
+ def add_collection3d(self, col, zs=0, zdir='z', autolim=True):
"""
Add a 3D collection object to the plot.
@@ -2573,8 +2590,21 @@ def add_collection3d(self, col, zs=0, zdir='z'):
- `.PolyCollection`
- `.LineCollection`
- - `.PatchCollection`
+ - `.PatchCollection` (currently not supporting *autolim*)
+
+ Parameters
+ ----------
+ col : `.Collection`
+ A 2D collection object.
+ zs : float or array-like, default: 0
+ The z-positions to be used for the 2D objects.
+ zdir : {'x', 'y', 'z'}, default: 'z'
+ The direction to use for the z-positions.
+ autolim : bool, default: True
+ Whether to update the data limits.
"""
+ had_data = self.has_data()
+
zvals = np.atleast_1d(zs)
zsortval = (np.min(zvals) if zvals.size
else 0) # FIXME: arbitrary default
@@ -2592,6 +2622,18 @@ def add_collection3d(self, col, zs=0, zdir='z'):
art3d.patch_collection_2d_to_3d(col, zs=zs, zdir=zdir)
col.set_sort_zpos(zsortval)
+ if autolim:
+ if isinstance(col, art3d.Line3DCollection):
+ self.auto_scale_xyz(*np.array(col._segments3d).transpose(),
+ had_data=had_data)
+ elif isinstance(col, art3d.Poly3DCollection):
+ self.auto_scale_xyz(*col._vec[:-1], had_data=had_data)
+ elif isinstance(col, art3d.Patch3DCollection):
+ pass
+ # FIXME: Implement auto-scaling function for Patch3DCollection
+ # Currently unable to do so due to issues with Patch3DCollection
+ # See https://github.com/matplotlib/matplotlib/issues/14298 for details
+
collection = super().add_collection(col)
return collection
diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/voxels-named-colors.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/voxels-named-colors.png
index b71ad19c1608..33dfc2f2313a 100644
Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/voxels-named-colors.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/voxels-named-colors.png differ
diff --git a/lib/mpl_toolkits/mplot3d/tests/test_axes3d.py b/lib/mpl_toolkits/mplot3d/tests/test_axes3d.py
index ed56e5505d8e..ecb51b724c27 100644
--- a/lib/mpl_toolkits/mplot3d/tests/test_axes3d.py
+++ b/lib/mpl_toolkits/mplot3d/tests/test_axes3d.py
@@ -961,8 +961,8 @@ def test_poly3dcollection_closed():
facecolor=(0.5, 0.5, 1, 0.5), closed=True)
c2 = art3d.Poly3DCollection([poly2], linewidths=3, edgecolor='k',
facecolor=(1, 0.5, 0.5, 0.5), closed=False)
- ax.add_collection3d(c1)
- ax.add_collection3d(c2)
+ ax.add_collection3d(c1, autolim=False)
+ ax.add_collection3d(c2, autolim=False)
def test_poly_collection_2d_to_3d_empty():
@@ -995,8 +995,8 @@ def test_poly3dcollection_alpha():
c2.set_facecolor((1, 0.5, 0.5))
c2.set_edgecolor('k')
c2.set_alpha(0.5)
- ax.add_collection3d(c1)
- ax.add_collection3d(c2)
+ ax.add_collection3d(c1, autolim=False)
+ ax.add_collection3d(c2, autolim=False)
@mpl3d_image_comparison(['add_collection3d_zs_array.png'], style='mpl20')
@@ -1055,6 +1055,32 @@ def test_add_collection3d_zs_scalar():
ax.set_zlim(0, 2)
+def test_line3dCollection_autoscaling():
+ fig = plt.figure()
+ ax = fig.add_subplot(projection='3d')
+
+ lines = [[(0, 0, 0), (1, 4, 2)],
+ [(1, 1, 3), (2, 0, 2)],
+ [(1, 0, 4), (1, 4, 5)]]
+
+ lc = art3d.Line3DCollection(lines)
+ ax.add_collection3d(lc)
+ assert np.allclose(ax.get_xlim3d(), (-0.041666666666666664, 2.0416666666666665))
+ assert np.allclose(ax.get_ylim3d(), (-0.08333333333333333, 4.083333333333333))
+ assert np.allclose(ax.get_zlim3d(), (-0.10416666666666666, 5.104166666666667))
+
+
+def test_poly3dCollection_autoscaling():
+ fig = plt.figure()
+ ax = fig.add_subplot(projection='3d')
+ poly = np.array([[0, 0, 0], [1, 1, 3], [1, 0, 4]])
+ col = art3d.Poly3DCollection([poly])
+ ax.add_collection3d(col)
+ assert np.allclose(ax.get_xlim3d(), (-0.020833333333333332, 1.0208333333333333))
+ assert np.allclose(ax.get_ylim3d(), (-0.020833333333333332, 1.0208333333333333))
+ assert np.allclose(ax.get_zlim3d(), (-0.0833333333333333, 4.083333333333333))
+
+
@mpl3d_image_comparison(['axes3d_labelpad.png'],
remove_text=False, style='mpl20')
def test_axes3d_labelpad():
@@ -1766,6 +1792,31 @@ def test_shared_axes_retick():
assert ax2.get_zlim() == (-0.5, 2.5)
+def test_rotate():
+ """Test rotating using the left mouse button."""
+ for roll in [0, 30]:
+ fig = plt.figure()
+ ax = fig.add_subplot(1, 1, 1, projection='3d')
+ ax.view_init(0, 0, roll)
+ ax.figure.canvas.draw()
+
+ # drag mouse horizontally to change azimuth
+ dx = 0.1
+ dy = 0.2
+ ax._button_press(
+ mock_event(ax, button=MouseButton.LEFT, xdata=0, ydata=0))
+ ax._on_move(
+ mock_event(ax, button=MouseButton.LEFT,
+ xdata=dx*ax._pseudo_w, ydata=dy*ax._pseudo_h))
+ ax.figure.canvas.draw()
+ roll_radians = np.deg2rad(ax.roll)
+ cs = np.cos(roll_radians)
+ sn = np.sin(roll_radians)
+ assert ax.elev == (-dy*180*cs + dx*180*sn)
+ assert ax.azim == (-dy*180*sn - dx*180*cs)
+ assert ax.roll == roll
+
+
def test_pan():
"""Test mouse panning using the middle mouse button."""
@@ -2276,6 +2327,24 @@ def test_on_move_vertical_axis(vertical_axis: str) -> None:
)
+@pytest.mark.parametrize(
+ "vertical_axis, aspect_expected",
+ [
+ ("x", [1.190476, 0.892857, 1.190476]),
+ ("y", [0.892857, 1.190476, 1.190476]),
+ ("z", [1.190476, 1.190476, 0.892857]),
+ ],
+)
+def test_set_box_aspect_vertical_axis(vertical_axis, aspect_expected):
+ ax = plt.subplot(1, 1, 1, projection="3d")
+ ax.view_init(elev=0, azim=0, roll=0, vertical_axis=vertical_axis)
+ ax.figure.canvas.draw()
+
+ ax.set_box_aspect(None)
+
+ np.testing.assert_allclose(aspect_expected, ax._box_aspect, rtol=1e-6)
+
+
@image_comparison(baseline_images=['arc_pathpatch.png'],
remove_text=True,
style='mpl20')
diff --git a/pyproject.toml b/pyproject.toml
index fe75b325dc89..52bbe308c0f9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -95,7 +95,6 @@ install = ['--tags=data,python-runtime,runtime']
[tool.setuptools_scm]
version_scheme = "release-branch-semver"
local_scheme = "node-and-date"
-write_to = "lib/matplotlib/_version.py"
parentdir_prefix_version = "matplotlib-"
fallback_version = "0.0+UNKNOWN"
@@ -284,11 +283,11 @@ ignore_directives = [
"include"
]
ignore_roles = [
- # sphinxext.custom_roles
- "rc",
# matplotlib.sphinxext.mathmpl
"mathmpl",
"math-stix",
+ # matplotlib.sphinxext.roles
+ "rc",
# sphinxext.github
"ghissue",
"ghpull",
diff --git a/src/_c_internal_utils.cpp b/src/_c_internal_utils.cpp
index 464aabcb2e3a..e118183ecc8b 100644
--- a/src/_c_internal_utils.cpp
+++ b/src/_c_internal_utils.cpp
@@ -111,7 +111,11 @@ static py::object
mpl_GetForegroundWindow(void)
{
#ifdef _WIN32
- return py::capsule(GetForegroundWindow(), "HWND");
+ if (HWND hwnd = GetForegroundWindow()) {
+ return py::capsule(hwnd, "HWND");
+ } else {
+ return py::none();
+ }
#else
return py::none();
#endif
diff --git a/src/_image_wrapper.cpp b/src/_image_wrapper.cpp
index 65c8c8324ebc..856dcf4ea3ce 100644
--- a/src/_image_wrapper.cpp
+++ b/src/_image_wrapper.cpp
@@ -173,20 +173,20 @@ image_resample(py::array input_array,
if (auto resampler =
(ndim == 2) ? (
- (dtype.is(py::dtype::of())) ? resample :
- (dtype.is(py::dtype::of())) ? resample :
- (dtype.is(py::dtype::of())) ? resample :
- (dtype.is(py::dtype::of())) ? resample :
- (dtype.is(py::dtype::of())) ? resample :
- (dtype.is(py::dtype::of())) ? resample :
+ (dtype.equal(py::dtype::of())) ? resample :
+ (dtype.equal(py::dtype::of())) ? resample :
+ (dtype.equal(py::dtype::of())) ? resample :
+ (dtype.equal(py::dtype::of())) ? resample :
+ (dtype.equal(py::dtype::of())) ? resample :
+ (dtype.equal(py::dtype::of())) ? resample :
nullptr) : (
// ndim == 3
- (dtype.is(py::dtype::of())) ? resample :
- (dtype.is(py::dtype::of())) ? resample :
- (dtype.is(py::dtype::of())) ? resample :
- (dtype.is(py::dtype::of())) ? resample :
- (dtype.is(py::dtype::of())) ? resample :
- (dtype.is(py::dtype::of())) ? resample :
+ (dtype.equal(py::dtype::of())) ? resample :
+ (dtype.equal(py::dtype::of())) ? resample :
+ (dtype.equal(py::dtype::of())) ? resample :
+ (dtype.equal(py::dtype::of())) ? resample :
+ (dtype.equal(py::dtype::of())) ? resample :
+ (dtype.equal(py::dtype::of())) ? resample :
nullptr)) {
Py_BEGIN_ALLOW_THREADS
resampler(
diff --git a/src/_macosx.m b/src/_macosx.m
index 656d502fa17c..fda928536ab5 100755
--- a/src/_macosx.m
+++ b/src/_macosx.m
@@ -77,6 +77,9 @@ static int wait_for_stdin() {
// continuously run an event loop until the stdin_received flag is set to exit
while (!stdin_received && !stdin_sigint) {
+ // This loop is similar to the main event loop and flush_events which have
+ // Py_[BEGIN|END]_ALLOW_THREADS surrounding the loop.
+ // This should not be necessary here because PyOS_InputHook releases the GIL for us.
while (true) {
NSEvent *event = [NSApp nextEventMatchingMask: NSEventMaskAny
untilDate: [NSDate distantPast]
@@ -380,6 +383,9 @@ static CGFloat _get_device_scale(CGContextRef cr)
// to process, breaking out of the loop when no events remain and
// displaying the canvas if needed.
NSEvent *event;
+
+ Py_BEGIN_ALLOW_THREADS
+
while (true) {
event = [NSApp nextEventMatchingMask: NSEventMaskAny
untilDate: [NSDate distantPast]
@@ -390,6 +396,9 @@ static CGFloat _get_device_scale(CGContextRef cr)
}
[NSApp sendEvent:event];
}
+
+ Py_END_ALLOW_THREADS
+
[self->view displayIfNeeded];
Py_RETURN_NONE;
}
diff --git a/src/checkdep_freetype2.c b/src/checkdep_freetype2.c
index 8d9d8ca24a07..16e8ac23919e 100644
--- a/src/checkdep_freetype2.c
+++ b/src/checkdep_freetype2.c
@@ -1,7 +1,7 @@
#ifdef __has_include
#if !__has_include()
#error "FreeType version 2.3 or higher is required. \
-You may unset the system_freetype entry in mplsetup.cfg to let Matplotlib download it."
+You may set the system-freetype Meson build option to false to let Matplotlib download it."
#endif
#endif
@@ -15,5 +15,5 @@ You may unset the system_freetype entry in mplsetup.cfg to let Matplotlib downlo
XSTR(FREETYPE_MAJOR) "." XSTR(FREETYPE_MINOR) "." XSTR(FREETYPE_PATCH) ".")
#if FREETYPE_MAJOR << 16 + FREETYPE_MINOR << 8 + FREETYPE_PATCH < 0x020300
#error "FreeType version 2.3 or higher is required. \
-You may unset the system_freetype entry in mplsetup.cfg to let Matplotlib download it."
+You may set the system-freetype Meson build option to false to let Matplotlib download it."
#endif
diff --git a/tools/cache_zenodo_svg.py b/tools/cache_zenodo_svg.py
index fbdbceaf0fbb..600e87efc498 100644
--- a/tools/cache_zenodo_svg.py
+++ b/tools/cache_zenodo_svg.py
@@ -63,6 +63,7 @@ def _get_xdg_cache_dir():
if __name__ == "__main__":
data = {
+ "v3.9.0": "11201097",
"v3.8.4": "10916799",
"v3.8.3": "10661079",
"v3.8.2": "10150955",