diff --git a/.appveyor.yml b/.appveyor.yml index 0e8e0a553fd4..c637dae4d869 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -60,6 +60,8 @@ install: # pull pywin32 from conda because on py38 there is something wrong with finding # the dlls when insalled from pip - conda install -c conda-forge pywin32 + # install pyqt from conda-forge + - conda install -c conda-forge pyqt - echo %PYTHON_VERSION% %TARGET_ARCH% # Install dependencies from PyPI. - python -m pip install --upgrade -r requirements/testing/all.txt %EXTRAREQS% %PINNEDVERS% diff --git a/.circleci/config.yml b/.circleci/config.yml index 4a0f262a4eb7..93335b41bbac 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -124,15 +124,13 @@ commands: command: | # Set epoch to date of latest tag. export SOURCE_DATE_EPOCH="$(git log -1 --format=%at $(git describe --abbrev=0))" - # Include analytics only when deploying to devdocs. - if [ "$CIRCLE_PROJECT_USERNAME" != "matplotlib" ] || \ - [ "$CIRCLE_BRANCH" != "master" ] || \ - [[ "$CIRCLE_PULL_REQUEST" == https://github.com/matplotlib/matplotlib/pull/* ]]; then - export ANALYTICS=False - else - export ANALYTICS=True + # Set release mode only when deploying to devdocs. + if [ "$CIRCLE_PROJECT_USERNAME" = "matplotlib" ] && \ + [ "$CIRCLE_BRANCH" = "master" ] && \ + [ "$CIRCLE_PR_NUMBER" = "" ]; then + export RELEASE_TAG='-t release' fi - make html O="-T -Ainclude_analytics=$ANALYTICS" + make html O="-T $RELEASE_TAG" rm -r build/html/_sources working_directory: doc - save_cache: diff --git a/.flake8 b/.flake8 index 7094b6c49b5f..9de6a96bfc59 100644 --- a/.flake8 +++ b/.flake8 @@ -85,7 +85,7 @@ per-file-ignores = lib/mpl_toolkits/tests/conftest.py: F401 lib/pylab.py: F401, F403 - doc/conf.py: E402, E501 + doc/conf.py: E402 tutorials/advanced/path_tutorial.py: E402 tutorials/advanced/patheffects_guide.py: E402 tutorials/advanced/transforms_tutorial.py: E402, E501 diff --git a/.github/workflows/cibuildwheel.yml b/.github/workflows/cibuildwheel.yml index 6bd35f22567d..6d64422adb66 100644 --- a/.github/workflows/cibuildwheel.yml +++ b/.github/workflows/cibuildwheel.yml @@ -18,7 +18,7 @@ jobs: CIBW_ARCHS_MACOS: "x86_64 universal2 arm64" strategy: matrix: - os: [ubuntu-18.04, windows-latest, macos-latest] + os: [ubuntu-18.04, windows-latest, macos-10.15] cibw_archs: ["auto"] include: - os: ubuntu-18.04 @@ -64,7 +64,7 @@ jobs: - name: Build wheels for CPython 3.10 run: | python -m cibuildwheel --output-dir dist - if: matrix.os != 'macos-latest' + if: matrix.os != 'macos-10.15' env: CIBW_BUILD: "cp310-*" CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 @@ -102,6 +102,7 @@ jobs: CIBW_BUILD: "pp37-*" CIBW_BEFORE_BUILD: pip install certifi numpy==${{ env.min-numpy-version }} CIBW_ARCHS: ${{ matrix.cibw_archs }} + PIP_USE_FEATURE: in-tree-build if: matrix.cibw_archs != 'aarch64' - name: Validate that LICENSE files are included in wheels diff --git a/.github/workflows/clean_pr.yml b/.github/workflows/clean_pr.yml new file mode 100644 index 000000000000..fc2acc0b2e4c --- /dev/null +++ b/.github/workflows/clean_pr.yml @@ -0,0 +1,22 @@ +name: PR cleanliness +on: [pull_request] + +jobs: + pr_clean: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: '0' + - name: Check for added-and-deleted files + run: | + git fetch --quiet origin "$GITHUB_BASE_REF" + base="$(git merge-base "origin/$GITHUB_BASE_REF" 'HEAD^2')" + ad="$(git log "$base..HEAD^2" --pretty=tformat: --name-status --diff-filter=AD | + cut --fields 2 | sort | uniq --repeated)" + if [[ -n "$ad" ]]; then + printf 'The following files were both added and deleted in this PR:\n%s\n' "$ad" + exit 1 + fi + diff --git a/LICENSE/LICENSE_COURIERTEN b/LICENSE/LICENSE_COURIERTEN new file mode 100644 index 000000000000..c6d3fd7410a2 --- /dev/null +++ b/LICENSE/LICENSE_COURIERTEN @@ -0,0 +1,18 @@ +The Courier10PitchBT-Bold.pfb file is a Type-1 version of +Courier 10 Pitch BT Bold by Bitstream, obtained from +. It is included +here as test data only, but the following license applies. + + +(c) Copyright 1989-1992, Bitstream Inc., Cambridge, MA. + +You are hereby granted permission under all Bitstream propriety rights +to use, copy, modify, sublicense, sell, and redistribute the 4 Bitstream +Charter (r) Type 1 outline fonts and the 4 Courier Type 1 outline fonts +for any purpose and without restriction; provided, that this notice is +left intact on all copies of such fonts and that Bitstream's trademark +is acknowledged as shown below on all unmodified copies of the 4 Charter +Type 1 fonts. + +BITSTREAM CHARTER is a registered trademark of Bitstream Inc. + diff --git a/README.rst b/README.rst index f6695c964068..a705ba3ab477 100644 --- a/README.rst +++ b/README.rst @@ -42,53 +42,52 @@ .. image:: https://matplotlib.org/_static/logo2.svg -Matplotlib is a comprehensive library for creating static, animated, and interactive visualizations in Python. +Matplotlib is a comprehensive library for creating static, animated, and +interactive visualizations in Python. Check out our `home page `_ for more information. .. image:: https://matplotlib.org/_static/readme_preview.png -Matplotlib produces publication-quality figures in a variety of hardcopy formats -and interactive environments across platforms. Matplotlib can be used in Python scripts, -the Python and IPython shell, web application servers, and various -graphical user interface toolkits. +Matplotlib produces publication-quality figures in a variety of hardcopy +formats and interactive environments across platforms. Matplotlib can be used +in Python scripts, the Python and IPython shell, web application servers, and +various graphical user interface toolkits. Install ======= For installation instructions and requirements, see `INSTALL.rst `_ or the -`install `_ documentation. - -Test -==== - -After installation, launch the test suite:: - - python -m pytest - -Read the `testing guide `_ for more information and alternatives. +`install `_ documentation. Contribute ========== + You've discovered a bug or something else you want to change - excellent! You've worked out a way to fix it – even better! You want to tell us about it – best of all! -Start at the `contributing guide `_! +Start at the `contributing guide +`_! Contact ======= -`Discourse `_ is the discussion forum for general questions and discussions and our recommended starting point. +`Discourse `_ is the discussion forum for +general questions and discussions and our recommended starting point. Our active mailing lists (which are mirrored on Discourse) are: -* `Users `_ mailing list: matplotlib-users@python.org -* `Announcement `_ mailing list: matplotlib-announce@python.org -* `Development `_ mailing list: matplotlib-devel@python.org +* `Users `_ mailing + list: matplotlib-users@python.org +* `Announcement + `_ mailing + list: matplotlib-announce@python.org +* `Development `_ + mailing list: matplotlib-devel@python.org Gitter_ is for coordinating development and asking questions directly related to contributing to matplotlib. @@ -99,21 +98,21 @@ Citing Matplotlib If Matplotlib contributes to a project that leads to publication, please acknowledge this by citing Matplotlib. -`A ready-made citation entry `_ is available. +`A ready-made citation entry `_ is +available. Research notice ~~~~~~~~~~~~~~~ Please note that this repository is participating in a study into sustainability of open source projects. Data will be gathered about this -repository for approximately the next 12 months, starting from June -2021. +repository for approximately the next 12 months, starting from June 2021. -Data collected will include number of contributors, number of PRs, time -taken to close/merge these PRs, and issues closed. +Data collected will include number of contributors, number of PRs, time taken +to close/merge these PRs, and issues closed. -For more information, please visit `the informational -page `__ or -download the `participant information -sheet `__. +For more information, please visit `the informational page +`__ or download the +`participant information sheet +`__. diff --git a/doc/_static/mpl.css b/doc/_static/mpl.css index 5f62cd3c95c7..370e2e5ac201 100644 --- a/doc/_static/mpl.css +++ b/doc/_static/mpl.css @@ -231,3 +231,17 @@ table.property-table td { /* fix width to width of cheatsheet */ width: 210px; } + +/* Two columns for install code blocks */ +div.twocol { + padding-left: 0; + padding-right: 0; + display: flex; + gap: 20px; +} + +div.twocol > div { + flex-grow: 1; + padding: 0; + margin: 0; +} diff --git a/doc/api/next_api_changes/behavior/14913-AL.rst b/doc/api/next_api_changes/behavior/14913-AL.rst deleted file mode 100644 index 9e98b7e6c8ec..000000000000 --- a/doc/api/next_api_changes/behavior/14913-AL.rst +++ /dev/null @@ -1,9 +0,0 @@ -The output of ``NonUniformImage`` and ``PcolorImage`` has changed -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Pixel-level differences may be observed in images generated using -`.NonUniformImage` or `.PcolorImage`, typically for pixels exactly at the -boundary between two data cells (no user-facing axes method currently generates -`.NonUniformImage`\s, and only `.pcolorfast` can generate `.PcolorImage`\s). -These artists are also now slower, normally by ~1.5x but sometimes more (in -particular for ``NonUniformImage(interpolation="bilinear")``. This slowdown -arises from fixing occasional floating point inaccuracies. diff --git a/doc/api/next_api_changes/behavior/18216-ES.rst b/doc/api/next_api_changes/behavior/18216-ES.rst deleted file mode 100644 index d6e6cae4b55d..000000000000 --- a/doc/api/next_api_changes/behavior/18216-ES.rst +++ /dev/null @@ -1,30 +0,0 @@ -.. _Behavioral API Changes 3.5 - Axes children combined: - -``Axes`` children are no longer separated by type -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Formerly, `.axes.Axes` children were separated by `.Artist` type, into sublists -such as ``Axes.lines``. For methods that produced multiple elements (such as -`.Axes.errorbar`), though individual parts would have similar *zorder*, this -separation might cause them to be drawn at different times, causing -inconsistent results when overlapping other Artists. - -Now, the children are no longer separated by type, and the sublist properties -are generated dynamically when accessed. Consequently, Artists will now always -appear in the correct sublist; e.g., if `.axes.Axes.add_line` is called on a -`.Patch`, it will be appear in the ``Axes.patches`` sublist, _not_ -``Axes.lines``. The ``Axes.add_*`` methods will now warn if passed an -unexpected type. - -Modification of the following sublists is still accepted, but deprecated: - -* ``Axes.artists`` -* ``Axes.collections`` -* ``Axes.images`` -* ``Axes.lines`` -* ``Axes.patches`` -* ``Axes.tables`` -* ``Axes.texts`` - -To remove an Artist, use its `.Artist.remove` method. To add an Artist, use the -corresponding ``Axes.add_*`` method. diff --git a/doc/api/next_api_changes/behavior/19375-AL.rst b/doc/api/next_api_changes/behavior/19375-AL.rst deleted file mode 100644 index c543f57d3818..000000000000 --- a/doc/api/next_api_changes/behavior/19375-AL.rst +++ /dev/null @@ -1,2 +0,0 @@ -``Figure.suppressComposite`` now also controls compositing of Axes images -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/api/next_api_changes/behavior/19515-GL.rst b/doc/api/next_api_changes/behavior/19515-GL.rst deleted file mode 100644 index cb6d925b797c..000000000000 --- a/doc/api/next_api_changes/behavior/19515-GL.rst +++ /dev/null @@ -1,10 +0,0 @@ -Colorbars now have pan and zoom functionality -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Interactive plots with colorbars can now be zoomed and panned on -the colorbar axis. This adjusts the *vmin* and *vmax* of the -``ScalarMappable`` associated with the colorbar. This is currently -only enabled for continuous norms. Norms used with contourf and -categoricals, such as ``BoundaryNorm`` and ``NoNorm``, have the -interactive capability disabled by default. ``cb.ax.set_navigate()`` -can be used to set whether a colorbar axes is interactive or not. diff --git a/doc/api/next_api_changes/behavior/20012-AL.rst b/doc/api/next_api_changes/behavior/20012-AL.rst deleted file mode 100644 index 78615239e17c..000000000000 --- a/doc/api/next_api_changes/behavior/20012-AL.rst +++ /dev/null @@ -1,5 +0,0 @@ -Default theta tick locations for non-full-circle polar plots have changed -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -For polar plots that don't cover a full circle, the default theta tick -locations are now at multiples of 10°, 15°, 30°, 45°, 90°, rather than using -values that mostly make sense for linear plots (20°, 25°, etc.). diff --git a/doc/api/next_api_changes/behavior/20027-AL.rst b/doc/api/next_api_changes/behavior/20027-AL.rst deleted file mode 100644 index 59e5063a6c1b..000000000000 --- a/doc/api/next_api_changes/behavior/20027-AL.rst +++ /dev/null @@ -1,3 +0,0 @@ -``axvspan`` now plots full wedges in polar plots -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -... rather than triangles. diff --git a/doc/api/next_api_changes/behavior/20046-BB.rst b/doc/api/next_api_changes/behavior/20046-BB.rst deleted file mode 100644 index 717a42b84603..000000000000 --- a/doc/api/next_api_changes/behavior/20046-BB.rst +++ /dev/null @@ -1,25 +0,0 @@ -``MatplotlibDeprecationWarning`` now subclasses ``DeprecationWarning`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Historically, it has not been possible to filter -``MatplotlibDeprecationWarning``\s by checking for ``DeprecationWarning``, -since we subclass ``UserWarning`` directly. - -The decision to not subclass DeprecationWarning has to do with a decision from -core Python in the 2.x days to not show DeprecationWarnings to users. However, -there is now a more sophisticated filter in place (see -https://www.python.org/dev/peps/pep-0565/). - -Users will now see ``MatplotlibDeprecationWarning`` only during interactive -sessions, and these can be silenced by the standard mechanism: - -.. code:: python - - warnings.filterwarnings("ignore", category=DeprecationWarning) - -Library authors must now enable ``DeprecationWarning``\s explicitly in order -for (non-interactive) CI/CD pipelines to report back these warnings, as is -standard for the rest of the Python ecosystem: - -.. code:: python - - warnings.filterwarnings("always", DeprecationWarning) diff --git a/doc/api/next_api_changes/behavior/20054-JMK.rst b/doc/api/next_api_changes/behavior/20054-JMK.rst deleted file mode 100644 index 0b9fb193df64..000000000000 --- a/doc/api/next_api_changes/behavior/20054-JMK.rst +++ /dev/null @@ -1,12 +0,0 @@ -Colorbar lines no longer clipped -================================ - -If a colorbar has lines added to it (e.g. for contour lines), these will -no longer be clipped. This is an improvement for lines on the edge of -the colorbar, but could lead to lines off the colorbar if the limits of -the colorbar are changed. - -``Colorbar.patch`` is deprecated -================================ - -This attribute is not correctly updated anymore. diff --git a/doc/api/next_api_changes/behavior/20064-AL.rst b/doc/api/next_api_changes/behavior/20064-AL.rst deleted file mode 100644 index 858d311a86c6..000000000000 --- a/doc/api/next_api_changes/behavior/20064-AL.rst +++ /dev/null @@ -1,5 +0,0 @@ -``AxesDivider`` now defaults to rcParams-specified pads -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -`.AxesDivider.append_axes`, `.AxesDivider.new_horizontal`, and -`.AxesDivider.new_vertical` now default to paddings specified by -:rc:`figure.subplot.wspace` and :rc:`figure.subplot.hspace` rather than zero. diff --git a/doc/api/next_api_changes/behavior/20077-TH.rst b/doc/api/next_api_changes/behavior/20077-TH.rst deleted file mode 100644 index 63dac85d795b..000000000000 --- a/doc/api/next_api_changes/behavior/20077-TH.rst +++ /dev/null @@ -1,7 +0,0 @@ -``Artist.set`` applies artist properties in the order in which they are given -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The change only affects the interaction between the *color*, *edgecolor*, -*facecolor*, and, for `.Collection`\s, *alpha* properties: the *color* property -now needs to be passed first in order not to override the other properties. -This is consistent with e.g. `.Artist.update`, which did not reorder the -properties passed to it. diff --git a/doc/api/next_api_changes/behavior/20150-TAC.rst b/doc/api/next_api_changes/behavior/20150-TAC.rst deleted file mode 100644 index e5109d9afa43..000000000000 --- a/doc/api/next_api_changes/behavior/20150-TAC.rst +++ /dev/null @@ -1,10 +0,0 @@ -Rename fist arg to subplot_mosaic -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Both `.FigureBase.subplot_mosaic`, and `.pyplot.subplot_mosaic` have had the -first position argument renamed from *layout* to *mosaic*. This is because we -are considering to consolidate *constrained_layout* and *tight_layout* keyword -arguments in the Figure creation functions of `.pyplot` into a single *layout* -keyword argument which would collide. - -As this API is provisional, we are changing this with no deprecation period. diff --git a/doc/api/next_api_changes/behavior/20199-AL.rst b/doc/api/next_api_changes/behavior/20199-AL.rst deleted file mode 100644 index deb1cb2893be..000000000000 --- a/doc/api/next_api_changes/behavior/20199-AL.rst +++ /dev/null @@ -1,5 +0,0 @@ -``Line2D.set_markeredgecolor(None)`` and ``Line2D.set_markerfacecolor(None)`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -... now set the line property using the corresponding rcParam -(:rc:`lines.markeredgecolor` and :rc:`lines.markerfacecolor`). This is -consistent with other `.Line2D` property setters. diff --git a/doc/api/next_api_changes/behavior/20268-JMK.rst b/doc/api/next_api_changes/behavior/20268-JMK.rst deleted file mode 100644 index e0ff20bfcc82..000000000000 --- a/doc/api/next_api_changes/behavior/20268-JMK.rst +++ /dev/null @@ -1,11 +0,0 @@ -pcolor(mesh) shading defaults to auto -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The *shading* kwarg for `.Axes.pcolormesh` and `.Axes.pcolor` default -has been changed to 'auto'. - -Passing ``Z(M, N)``, ``x(N)``, ``y(M)`` to ``pcolormesh`` with -``shading='flat'`` will now raise a ``TypeError``. Use -``shading='auto'`` or ``shading='nearest'`` for ``x`` and ``y`` -to be treated as cell centers, or drop the last column and row -of ``Z`` to get the old behavior with ``shading='flat'``. \ No newline at end of file diff --git a/doc/api/next_api_changes/behavior/20367-AG.rst b/doc/api/next_api_changes/behavior/20367-AG.rst deleted file mode 100644 index 38bc9ac05ad7..000000000000 --- a/doc/api/next_api_changes/behavior/20367-AG.rst +++ /dev/null @@ -1,12 +0,0 @@ -``Text`` allows a boolean *parse_math* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -`.Text` objects now allow a *parse_math* keyword-only argument -which defaults to True. When it is passed as False, that text object -will consider the string as a literal and will not try to parse it -as a mathtext object. - -``TextBox`` defaults it to False --------------------------------- -`.TextBox` now defaults its text object's *parse_math** argument -as False. diff --git a/doc/api/next_api_changes/behavior/20405-JMK.rst b/doc/api/next_api_changes/behavior/20405-JMK.rst deleted file mode 100644 index 30af063e61ad..000000000000 --- a/doc/api/next_api_changes/behavior/20405-JMK.rst +++ /dev/null @@ -1,8 +0,0 @@ -Make norm from scale now public -=============================== - -Downstream libraries can take advantage of `.colors.make_norm_from_scale` -to create a `~.colors.Normalize` subclass directly from an existing scale. -Usually norms have a scale, and the advantage of having a `~.scale.ScaleBase` -attached to a norm is to provide a scale, and associated tick locators and -formatters, for the colorbar. \ No newline at end of file diff --git a/doc/api/next_api_changes/behavior/20634-JKS.rst b/doc/api/next_api_changes/behavior/20634-JKS.rst deleted file mode 100644 index ff4046445e42..000000000000 --- a/doc/api/next_api_changes/behavior/20634-JKS.rst +++ /dev/null @@ -1,8 +0,0 @@ -``Type1Font`` objects now decrypt the encrypted part ----------------------------------------------------- - -Type 1 fonts have a large part of their code encrypted as an obsolete -copy-protection measure. This part is now available decrypted as the -``decrypted`` attribute of :class:`~matplotlib.type1font.Type1Font`. -This decrypted data is not yet parsed, but this is a prerequisite for -implementing subsetting. diff --git a/doc/api/next_api_changes/behavior/20692-AL.rst b/doc/api/next_api_changes/behavior/20692-AL.rst deleted file mode 100644 index 1070fb55c41e..000000000000 --- a/doc/api/next_api_changes/behavior/20692-AL.rst +++ /dev/null @@ -1,3 +0,0 @@ -``hatch.SmallFilledCircles`` now inherits from ``hatch.Circles`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -... rather than from ``hatch.SmallCircles``. diff --git a/doc/api/next_api_changes/behavior/20699-AFV.rst b/doc/api/next_api_changes/behavior/20699-AFV.rst deleted file mode 100644 index 9852290baa96..000000000000 --- a/doc/api/next_api_changes/behavior/20699-AFV.rst +++ /dev/null @@ -1,31 +0,0 @@ -Change of the (default) legend handler for Line2D instances ------------------------------------------------------------ - -The default legend handler for Line2D instances (`.HandlerLine2D`) now -consistently exposes all the attributes and methods related to the line -marker (:ghissue:`11358`). This makes easy to change the marker features -after instantiating a legend. - -.. code:: - - import matplotlib.pyplot as plt - - fig, ax = plt.subplots() - - ax.plot([1, 3, 2], marker="s", label="Line", color="pink", mec="red", ms=8) - leg = ax.legend() - - leg.legendHandles[0].set_color("lightgray") - leg.legendHandles[0].set_mec("black") # marker edge color - -The former legend handler for Line2D objects has been renamed -`.HandlerLine2DCompound`. To revert to the previous behavior, one can use - -.. code:: - - import matplotlib.legend as mlegend - from matplotlib.legend_handler import HandlerLine2DCompound - from matplotlib.lines import Line2D - - mlegend.Legend.update_default_handler_map({Line2D: HandlerLine2DCompound()}) - diff --git a/doc/api/next_api_changes/behavior/20715-JKS.rst b/doc/api/next_api_changes/behavior/20715-JKS.rst new file mode 100644 index 000000000000..f0ca1d707d3d --- /dev/null +++ b/doc/api/next_api_changes/behavior/20715-JKS.rst @@ -0,0 +1,8 @@ +``Type1Font`` objects include more properties +--------------------------------------------- + +The `.type1font.Type1Font.prop` dictionary now includes more keys, such +as ``CharStrings`` and ``Subrs``. The value of the ``Encoding`` key is +now a dictionary mapping codes to glyph names. The +`.type1font.Type1Font.transform` method now correctly removes +``UniqueID`` properties from the font. diff --git a/doc/api/next_api_changes/behavior/21031-AL.rst b/doc/api/next_api_changes/behavior/21031-AL.rst deleted file mode 100644 index ec1a80f9478a..000000000000 --- a/doc/api/next_api_changes/behavior/21031-AL.rst +++ /dev/null @@ -1,3 +0,0 @@ -Setting invalid ``rcParams["date.converter"]`` now raises ValueError -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Previously, invalid values would be ignored with a UserWarning. diff --git a/doc/api/next_api_changes/behavior/21038-DS.rst b/doc/api/next_api_changes/behavior/21038-DS.rst deleted file mode 100644 index 55ca3fb7597d..000000000000 --- a/doc/api/next_api_changes/behavior/21038-DS.rst +++ /dev/null @@ -1,4 +0,0 @@ -hexbin with a log norm ----------------------- -`~.axes.Axes.hexbin` no longer (incorrectly) adds 1 to every bin value if a -log norm is being used. diff --git a/doc/api/next_api_changes/deprecations/13860-AL.rst b/doc/api/next_api_changes/deprecations/13860-AL.rst deleted file mode 100644 index 98ca88e1a840..000000000000 --- a/doc/api/next_api_changes/deprecations/13860-AL.rst +++ /dev/null @@ -1,6 +0,0 @@ -Locator and Formatter wrapper methods -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The ``set_view_interval``, ``set_data_interval`` and ``set_bounds`` methods of -`.Locator`\s and `.Formatter`\s (and their common base class, TickHelper) are -deprecated. Directly manipulate the view and data intervals on the underlying -axis instead. diff --git a/doc/api/next_api_changes/deprecations/15604-AL.rst b/doc/api/next_api_changes/deprecations/15604-AL.rst deleted file mode 100644 index d87b4205fc93..000000000000 --- a/doc/api/next_api_changes/deprecations/15604-AL.rst +++ /dev/null @@ -1,5 +0,0 @@ -Auto-removal of grids by `~.Axes.pcolor` and `~.Axes.pcolormesh` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -`~.Axes.pcolor` and `~.Axes.pcolormesh` currently remove any visible axes major -grid. This behavior is deprecated; please explicitly call ``ax.grid(False)`` -to remove the grid. diff --git a/doc/api/next_api_changes/deprecations/18216-ES.rst b/doc/api/next_api_changes/deprecations/18216-ES.rst deleted file mode 100644 index 56fa58c65c39..000000000000 --- a/doc/api/next_api_changes/deprecations/18216-ES.rst +++ /dev/null @@ -1,22 +0,0 @@ -Modification of ``Axes`` children sublists -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -See :ref:`Behavioral API Changes 3.5 - Axes children combined` for more -information; modification of the following sublists is deprecated: - -* ``Axes.artists`` -* ``Axes.collections`` -* ``Axes.images`` -* ``Axes.lines`` -* ``Axes.patches`` -* ``Axes.tables`` -* ``Axes.texts`` - -To remove an Artist, use its `.Artist.remove` method. To add an Artist, use the -corresponding ``Axes.add_*`` method. - -Passing incorrect types to ``Axes.add_*`` methods -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The ``Axes.add_*`` methods will now warn if passed an unexpected type. See -their documentation for the types they expect. diff --git a/doc/api/next_api_changes/deprecations/18346-TH.rst b/doc/api/next_api_changes/deprecations/18346-TH.rst deleted file mode 100644 index c6348157e2b2..000000000000 --- a/doc/api/next_api_changes/deprecations/18346-TH.rst +++ /dev/null @@ -1,9 +0,0 @@ -``plot_date`` -~~~~~~~~~~~~~ -The use of `~.Axes.plot_date` is discouraged. This method exists for historic -reasons and may be deprecated in the future. - -- ``datetime``-like data should directly be plotted using `~.Axes.plot`. -- If you need to plot plain numeric data as :ref:`date-format` or - need to set a timezone, call ``ax.xaxis.axis_date`` / ``ax.yaxis.axis_date`` - before `~.Axes.plot`. See `.Axis.axis_date`. diff --git a/doc/api/next_api_changes/deprecations/19401-AL.rst b/doc/api/next_api_changes/deprecations/19401-AL.rst deleted file mode 100644 index 81d69076d510..000000000000 --- a/doc/api/next_api_changes/deprecations/19401-AL.rst +++ /dev/null @@ -1,3 +0,0 @@ -``mpl_toolkits.axisartist.clip_path`` is deprecated -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -... with no replacement. diff --git a/doc/api/next_api_changes/deprecations/19441-AL.rst b/doc/api/next_api_changes/deprecations/19441-AL.rst deleted file mode 100644 index 3f1f8822cd51..000000000000 --- a/doc/api/next_api_changes/deprecations/19441-AL.rst +++ /dev/null @@ -1,5 +0,0 @@ -``cursord`` in GTK, Qt, and wx backends -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The ``backend_gtk3.cursord``, ``backend_qt.cursord``, and -``backend_wx.cursord`` dictionaries are deprecated. This makes the GTK module -importable on headless environments. diff --git a/doc/api/next_api_changes/deprecations/19483-JMK.rst b/doc/api/next_api_changes/deprecations/19483-JMK.rst deleted file mode 100644 index ddd3c252ea0a..000000000000 --- a/doc/api/next_api_changes/deprecations/19483-JMK.rst +++ /dev/null @@ -1,7 +0,0 @@ -``epoch2num`` and ``num2epoch`` are deprecated -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -These methods convert from unix timestamps to matplotlib floats, but are not -used internally to matplotlib, and should not be needed by endusers. -To convert a unix timestamp to datetime, simply use -`datetime.datetime.utcfromtimestamp`, or to use numpy datetime64 -``dt = np.datetim64(e*1e6, 'us')``. diff --git a/doc/api/next_api_changes/deprecations/19487-AL.rst b/doc/api/next_api_changes/deprecations/19487-AL.rst deleted file mode 100644 index 6466e0516994..000000000000 --- a/doc/api/next_api_changes/deprecations/19487-AL.rst +++ /dev/null @@ -1,6 +0,0 @@ -Unused positional parameters to ``print_`` methods are deprecated -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -None of the ``print_`` methods implemented by canvas subclasses used -positional arguments other that the first (the output filename or file-like), -so these extra parameters are deprecated. diff --git a/doc/api/next_api_changes/deprecations/19517-AL.rst b/doc/api/next_api_changes/deprecations/19517-AL.rst deleted file mode 100644 index 8b937e82ba41..000000000000 --- a/doc/api/next_api_changes/deprecations/19517-AL.rst +++ /dev/null @@ -1,4 +0,0 @@ -``is_url`` and ``URL_REGEX`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -... are deprecated. (They were previously defined in the toplevel -:mod:`matplotlib` module.) diff --git a/doc/api/next_api_changes/deprecations/19558-AL.rst b/doc/api/next_api_changes/deprecations/19558-AL.rst deleted file mode 100644 index 394ce962bdd5..000000000000 --- a/doc/api/next_api_changes/deprecations/19558-AL.rst +++ /dev/null @@ -1,3 +0,0 @@ -The *format* parameter of ``dviread.find_tex_file`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -... is deprecated (with no replacement). diff --git a/doc/api/next_api_changes/deprecations/19575-AL.rst b/doc/api/next_api_changes/deprecations/19575-AL.rst deleted file mode 100644 index 0b7d638fc9c8..000000000000 --- a/doc/api/next_api_changes/deprecations/19575-AL.rst +++ /dev/null @@ -1,4 +0,0 @@ -``Text.get_prop_tup`` -~~~~~~~~~~~~~~~~~~~~~ -... is deprecated with no replacements (because the `.Text` class cannot know -whether a backend needs to update cache e.g. when the text's color changes). diff --git a/doc/api/next_api_changes/deprecations/19585-AL.rst b/doc/api/next_api_changes/deprecations/19585-AL.rst deleted file mode 100644 index af220b7f4e71..000000000000 --- a/doc/api/next_api_changes/deprecations/19585-AL.rst +++ /dev/null @@ -1,4 +0,0 @@ -matplotlib.style.core deprecations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -``STYLE_FILE_PATTERN``, ``load_base_library``, and ``iter_user_libraries`` are -deprecated. diff --git a/doc/api/next_api_changes/deprecations/19655-AL.rst b/doc/api/next_api_changes/deprecations/19655-AL.rst deleted file mode 100644 index aaa8969d1dc3..000000000000 --- a/doc/api/next_api_changes/deprecations/19655-AL.rst +++ /dev/null @@ -1,5 +0,0 @@ -``Tick.apply_tickdir`` is deprecated -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -``apply_tickdir`` didn't actually update the tick markers on the existing -Line2D objects used to draw the ticks; use `.Axis.set_tick_params` instead. diff --git a/doc/api/next_api_changes/deprecations/19795-AL.rst b/doc/api/next_api_changes/deprecations/19795-AL.rst deleted file mode 100644 index 70f165ef8705..000000000000 --- a/doc/api/next_api_changes/deprecations/19795-AL.rst +++ /dev/null @@ -1,3 +0,0 @@ -``Dvi.baseline`` -~~~~~~~~~~~~~~~~ -... is deprecated (with no replacement). diff --git a/doc/api/next_api_changes/deprecations/19858-AL.rst b/doc/api/next_api_changes/deprecations/19858-AL.rst deleted file mode 100644 index cbd81544e96b..000000000000 --- a/doc/api/next_api_changes/deprecations/19858-AL.rst +++ /dev/null @@ -1,4 +0,0 @@ -"units finalize" signal -~~~~~~~~~~~~~~~~~~~~~~~ -This signal (previously emitted by Axis instances) is deprecated. Connect to -"units" instead. diff --git a/doc/api/next_api_changes/deprecations/19892-TH.rst b/doc/api/next_api_changes/deprecations/19892-TH.rst deleted file mode 100644 index 7db621376d35..000000000000 --- a/doc/api/next_api_changes/deprecations/19892-TH.rst +++ /dev/null @@ -1,12 +0,0 @@ -Discouraged: ``Figure`` parameters *tight_layout* and *constrained_layout* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The ``Figure`` parameters *tight_layout* and *constrained_layout* are -triggering competing layout mechanisms and thus should not be used together. - -To make the API clearer, we've merged them under the new parameter *layout* -with values 'constrained' (equal to ``constrained_layout=True``), 'tight' -(equal to ``tight_layout=True``). If given *layout* takes precedence. - -The use of *tight_layout* and *constrained_layout* is discouraged in favor -of *layout*. However, these parameters will stay available for backward -compatibility. diff --git a/doc/api/next_api_changes/deprecations/19934-DS.rst b/doc/api/next_api_changes/deprecations/19934-DS.rst deleted file mode 100644 index 867eee378294..000000000000 --- a/doc/api/next_api_changes/deprecations/19934-DS.rst +++ /dev/null @@ -1,17 +0,0 @@ -*drawtype* argument to RectangleSelector -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The *drawtype* keyword argument to -`~matplotlib.widgets.RectangleSelector` is deprecated. In the future the -behaviour will be the default behaviour of ``drawtype='box'``. - -Support for ``drawtype=line`` will be removed altogether as it is not clear -which points are within and outside a selector that is just a line. -As a result, the ``lineprops`` keyword argument to -`~matplotlib.widgets.RectangleSelector` is also deprecated. - -To retain the behaviour of ``drawtype='none'``, use -``rectprops={'visible': False}`` to make the drawn -`~matplotlib.patches.Rectangle` invisible. - -These changes also apply to `~matplotlib.widgets.EllipseSelector`, which -is a sub-class of `~matplotlib.widgets.RectangleSelector`. diff --git a/doc/api/next_api_changes/deprecations/20063-AL.rst b/doc/api/next_api_changes/deprecations/20063-AL.rst deleted file mode 100644 index 1811896119ba..000000000000 --- a/doc/api/next_api_changes/deprecations/20063-AL.rst +++ /dev/null @@ -1,4 +0,0 @@ -``Divider.get_vsize_hsize`` and ``Grid.get_vsize_hsize`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -These ``axes_grid1`` methods are deprecated. Copy their implementations if -needed. diff --git a/doc/api/next_api_changes/deprecations/20065-AL.rst b/doc/api/next_api_changes/deprecations/20065-AL.rst deleted file mode 100644 index 3b894b7a5505..000000000000 --- a/doc/api/next_api_changes/deprecations/20065-AL.rst +++ /dev/null @@ -1,4 +0,0 @@ -``AxesDivider.append_axes(..., add_to_figure=False)`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -... is deprecated. Use ``ax.remove()`` to remove the Axes from the figure if -needed. diff --git a/doc/api/next_api_changes/deprecations/20079-AL.rst b/doc/api/next_api_changes/deprecations/20079-AL.rst deleted file mode 100644 index 1d76e309b512..000000000000 --- a/doc/api/next_api_changes/deprecations/20079-AL.rst +++ /dev/null @@ -1,10 +0,0 @@ -Support for ``nx1 = None`` or ``ny1 = None`` in ``AxesLocator`` and ``Divider.locate`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In :mod:`.axes_grid1.axes_divider`, various internal APIs will stop supporting -passing ``nx1 = None`` or ``ny1 = None`` to mean ``nx + 1`` or ``ny + 1``, in -preparation for a possible future API which allows indexing and slicing of -dividers (possibly ``divider[a:b] == divider.new_locator(a, b)``, but also -``divider[a:] == divider.new_locator(a, )``). The user-facing -`.Divider.new_locator` API is unaffected -- it correctly normalizes ``nx1 = None`` -and ``ny1 = None`` as needed. diff --git a/doc/api/next_api_changes/deprecations/20091-AL.rst b/doc/api/next_api_changes/deprecations/20091-AL.rst deleted file mode 100644 index 7368691e30b0..000000000000 --- a/doc/api/next_api_changes/deprecations/20091-AL.rst +++ /dev/null @@ -1,3 +0,0 @@ -``tight_layout.auto_adjust_subplotpars`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -... is deprecated. diff --git a/doc/api/next_api_changes/deprecations/20108-AL.rst b/doc/api/next_api_changes/deprecations/20108-AL.rst deleted file mode 100644 index 372e8c7bd20e..000000000000 --- a/doc/api/next_api_changes/deprecations/20108-AL.rst +++ /dev/null @@ -1,3 +0,0 @@ -``ContourLabeler.get_label_width`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -... is deprecated. diff --git a/doc/api/next_api_changes/deprecations/20109-AL.rst b/doc/api/next_api_changes/deprecations/20109-AL.rst deleted file mode 100644 index 8874fd147039..000000000000 --- a/doc/api/next_api_changes/deprecations/20109-AL.rst +++ /dev/null @@ -1,5 +0,0 @@ -``plot_directive`` internals deprecations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The following helpers in `matplotlib.sphinxext.plot_directive` are deprecated: -``unescape_doctest`` (use `doctest.script_from_examples` instead), -``split_code_at_show``, ``run_code``. diff --git a/doc/api/next_api_changes/deprecations/20113-EP.rst b/doc/api/next_api_changes/deprecations/20113-EP.rst deleted file mode 100644 index fe39447632c6..000000000000 --- a/doc/api/next_api_changes/deprecations/20113-EP.rst +++ /dev/null @@ -1,21 +0,0 @@ -SpanSelector -~~~~~~~~~~~~ -``span_stays`` is deprecated, use ``interactive`` argument instead -Several `~matplotlib.widgets.SpanSelector` class internals have been privatized -and deprecated: -- ``pressv`` -- ``prev`` -- ``rect`` -- ``rectprops`` -- ``active_handle`` -- ``span_stays`` - - -Several `~matplotlib.widgets.RectangleSelector` and -`~matplotlib.widgets.EllipseSelector` class internals have been privatized and -deprecated: -- ``to_draw`` -- ``drawtype`` -- ``rectprops`` -- ``active_handle`` -- ``interactive`` diff --git a/doc/api/next_api_changes/deprecations/20126-AL.rst b/doc/api/next_api_changes/deprecations/20126-AL.rst deleted file mode 100644 index 474089f3b81a..000000000000 --- a/doc/api/next_api_changes/deprecations/20126-AL.rst +++ /dev/null @@ -1,4 +0,0 @@ -``font_manager`` helpers -~~~~~~~~~~~~~~~~~~~~~~~~ -The following functions in `matplotlib.font_manager` have been deprecated: -``win32InstalledFonts``, ``get_fontconfig_fonts``. diff --git a/doc/api/next_api_changes/deprecations/20170-AL.rst b/doc/api/next_api_changes/deprecations/20170-AL.rst deleted file mode 100644 index a4fc29e18623..000000000000 --- a/doc/api/next_api_changes/deprecations/20170-AL.rst +++ /dev/null @@ -1,4 +0,0 @@ -``SubplotParams.validate`` -~~~~~~~~~~~~~~~~~~~~~~~~~~ -... is deprecated. Use `.SubplotParams.update` to change `.SubplotParams` -while always keeping it in a valid state. diff --git a/doc/api/next_api_changes/deprecations/20173-AL.rst b/doc/api/next_api_changes/deprecations/20173-AL.rst deleted file mode 100644 index d9bce337af9f..000000000000 --- a/doc/api/next_api_changes/deprecations/20173-AL.rst +++ /dev/null @@ -1,4 +0,0 @@ -The first parameter of ``Axes.grid`` and ``Axis.grid`` has been renamed to *visible* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The parameter was previously named *b*. This deprecation only matters if -that parameter was passed using a keyword argument, e.g. ``grid(b=False)``. diff --git a/doc/api/next_api_changes/deprecations/20193-AL.rst b/doc/api/next_api_changes/deprecations/20193-AL.rst deleted file mode 100644 index 837265c4ed92..000000000000 --- a/doc/api/next_api_changes/deprecations/20193-AL.rst +++ /dev/null @@ -1,3 +0,0 @@ -``ParasiteAxesBase.get_images_artists`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -... has been deprecated. diff --git a/doc/api/next_api_changes/deprecations/20206-AL.rst b/doc/api/next_api_changes/deprecations/20206-AL.rst deleted file mode 100644 index e313a30ceeed..000000000000 --- a/doc/api/next_api_changes/deprecations/20206-AL.rst +++ /dev/null @@ -1,3 +0,0 @@ -The ``grid_info`` attribute of ``axisartist`` classes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -... has been deprecated. diff --git a/doc/api/next_api_changes/deprecations/20208-AL.rst b/doc/api/next_api_changes/deprecations/20208-AL.rst deleted file mode 100644 index e3da7db95e59..000000000000 --- a/doc/api/next_api_changes/deprecations/20208-AL.rst +++ /dev/null @@ -1,4 +0,0 @@ -``matplotlib.blocking_input`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This module has been deprecated. Instead, use ``canvas.start_event_loop()`` -and ``canvas.stop_event_loop()`` while connecting event callbacks as needed. diff --git a/doc/api/next_api_changes/deprecations/20209-AL.rst b/doc/api/next_api_changes/deprecations/20209-AL.rst deleted file mode 100644 index 79c462cd048f..000000000000 --- a/doc/api/next_api_changes/deprecations/20209-AL.rst +++ /dev/null @@ -1,3 +0,0 @@ -``cbook.report_memory`` -~~~~~~~~~~~~~~~~~~~~~~~ -... is deprecated. Use ``psutil.virtual_memory`` instead. diff --git a/doc/api/next_api_changes/deprecations/20237-TH.rst b/doc/api/next_api_changes/deprecations/20237-TH.rst deleted file mode 100644 index 435986f94f0f..000000000000 --- a/doc/api/next_api_changes/deprecations/20237-TH.rst +++ /dev/null @@ -1,17 +0,0 @@ -QuadMesh signature -~~~~~~~~~~~~~~~~~~ -The ``QuadMesh`` signature :: - - def __init__(meshWidth, meshHeight, coordinates, - antialiased=True, shading='flat', **kwargs) - -is deprecated and replaced by the new signature :: - - def __init__(coordinates, *, antialiased=True, shading='flat', **kwargs) - -In particular: - -- *coordinates* must now be a (M, N, 2) array-like. Previously, the grid - shape was separately specified as (*meshHeight* + 1, *meshWidth* + 1) and - *coordinates* could be an array-like of any shape with M * N * 2 elements. -- all parameters except *coordinates* are keyword-only now. diff --git a/doc/api/next_api_changes/deprecations/20254-AL.rst b/doc/api/next_api_changes/deprecations/20254-AL.rst deleted file mode 100644 index 9fce2b7061a1..000000000000 --- a/doc/api/next_api_changes/deprecations/20254-AL.rst +++ /dev/null @@ -1,3 +0,0 @@ -``floating_axes.GridHelperCurveLinear.get_boundary`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -... is deprecated, with no replacement. diff --git a/doc/api/next_api_changes/deprecations/20278-AL.rst b/doc/api/next_api_changes/deprecations/20278-AL.rst deleted file mode 100644 index 396442042ad4..000000000000 --- a/doc/api/next_api_changes/deprecations/20278-AL.rst +++ /dev/null @@ -1,4 +0,0 @@ -``TexManager`` attributes -~~~~~~~~~~~~~~~~~~~~~~~~~ -The following attributes of `.TexManager` are deprecated: ``grey_arrayd``, -``font_family``, ``font_families``, ``font_info``. diff --git a/doc/api/next_api_changes/deprecations/20302-AL.rst b/doc/api/next_api_changes/deprecations/20302-AL.rst deleted file mode 100644 index 7530a063eb5c..000000000000 --- a/doc/api/next_api_changes/deprecations/20302-AL.rst +++ /dev/null @@ -1,4 +0,0 @@ -``QuadMesh.convert_mesh_to_paths`` and ``QuadMesh.convert_mesh_to_triangles`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -... are deprecated. ``QuadMesh.get_paths()`` can be used as an alternative for -the former; there is no replacement for the latter. diff --git a/doc/api/next_api_changes/deprecations/20311-AL.rst b/doc/api/next_api_changes/deprecations/20311-AL.rst deleted file mode 100644 index a56941e5253a..000000000000 --- a/doc/api/next_api_changes/deprecations/20311-AL.rst +++ /dev/null @@ -1,5 +0,0 @@ -rcParams will no longer cast inputs to str -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -After a deprecation period, rcParams that expect a (non-pathlike) str will no -longer cast non-str inputs using `str`. This will avoid confusing errors in -subsequent code if e.g. a list input gets implicitly cast to a str. diff --git a/doc/api/next_api_changes/deprecations/20334-AL.rst b/doc/api/next_api_changes/deprecations/20334-AL.rst deleted file mode 100644 index 425170e38f97..000000000000 --- a/doc/api/next_api_changes/deprecations/20334-AL.rst +++ /dev/null @@ -1,11 +0,0 @@ -``ConversionInterface.convert`` no longer needs to accept unitless values -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Previously, custom subclasses of `.units.ConversionInterface` needed to -implement a ``convert`` method that not only accepted instances of the -unit, but also unitless values (which are passed through as is). This is -no longer the case (``convert`` is never called with a unitless value), -and such support in `.StrCategoryConverter` is deprecated. Likewise, the -`.ConversionInterface.is_numlike` helper is deprecated. - -Consider calling `.Axis.convert_units` instead, which still supports unitless -values. diff --git a/doc/api/next_api_changes/deprecations/20428-AL.rst b/doc/api/next_api_changes/deprecations/20428-AL.rst deleted file mode 100644 index 3de56e880afc..000000000000 --- a/doc/api/next_api_changes/deprecations/20428-AL.rst +++ /dev/null @@ -1,4 +0,0 @@ -``FancyArrowPatch.get_path_in_displaycoord`` and ``ConnectionPath.get_path_in_displaycoord`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -... are deprecated. The path in display coordinates can still be obtained, as -for other patches, using ``patch.get_transform().transform_path(patch.get_path())``. diff --git a/doc/api/next_api_changes/deprecations/20466-AL.rst b/doc/api/next_api_changes/deprecations/20466-AL.rst deleted file mode 100644 index d986ab87273f..000000000000 --- a/doc/api/next_api_changes/deprecations/20466-AL.rst +++ /dev/null @@ -1,3 +0,0 @@ -``backend_pgf.LatexManager.str_cache`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -... is deprecated. diff --git a/doc/api/next_api_changes/deprecations/20474-AL.rst b/doc/api/next_api_changes/deprecations/20474-AL.rst deleted file mode 100644 index 89c627023ba3..000000000000 --- a/doc/api/next_api_changes/deprecations/20474-AL.rst +++ /dev/null @@ -1,2 +0,0 @@ -``dviread.PsfontsMap`` now raises LookupError instead of KeyError for missing fonts -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/api/next_api_changes/deprecations/20540-AL.rst b/doc/api/next_api_changes/deprecations/20540-AL.rst deleted file mode 100644 index 49caf2d3df8f..000000000000 --- a/doc/api/next_api_changes/deprecations/20540-AL.rst +++ /dev/null @@ -1,3 +0,0 @@ -``:encoding:`` option to ``.. plot`` directive -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This option has had no effect since Matplotlib 1.3.1, and is now deprecated. diff --git a/doc/api/next_api_changes/deprecations/20543-AL.rst b/doc/api/next_api_changes/deprecations/20543-AL.rst deleted file mode 100644 index 49f696172902..000000000000 --- a/doc/api/next_api_changes/deprecations/20543-AL.rst +++ /dev/null @@ -1,3 +0,0 @@ -``@pytest.mark.style`` is deprecated -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Use ``@mpl.style.context``, which has the same effect. diff --git a/doc/api/next_api_changes/deprecations/20585-EP.rst b/doc/api/next_api_changes/deprecations/20585-EP.rst deleted file mode 100644 index 7da4d42246ed..000000000000 --- a/doc/api/next_api_changes/deprecations/20585-EP.rst +++ /dev/null @@ -1,32 +0,0 @@ -Unification of Selector API -~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The API for Selector widgets has been unified to use - -- *props* for the properties of the Artist representing the selection. -- *handle_props* for the Artists representing handles for modifying the selection. -- *grab_range* for the maximal tolerance to grab a handle with the mouse. - -This affects the following parameters and attributes: - - -RectangleSelector and EllipseSelector -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The *maxdist* argument is deprecated, use *grab_range* instead. -The *rectprops* argument is deprecated, use *props* instead. -The *marker_props* argument is deprecated, use *handle_props* instead. - -PolygonSelector -^^^^^^^^^^^^^^^ -The *vertex_select_radius* argument and attribute is deprecated, use *grab_range* instead. -The *lineprops* argument is deprecated, use *props* instead. -The *markerprops* argument is deprecated, use *handle_props* instead. -The *maxdist* argument and attribute is deprecated, use *grab_range* instead. - -SpanSelector -^^^^^^^^^^^^ -The *rectprops* argument is deprecated, use *props* instead. -The *maxdist* argument and attribute is deprecated, use *grab_range* instead. - -LassoSelector -^^^^^^^^^^^^^ -The *lineprops* argument is deprecated, use *props* instead. \ No newline at end of file diff --git a/doc/api/next_api_changes/deprecations/20586-TH.rst b/doc/api/next_api_changes/deprecations/20586-TH.rst deleted file mode 100644 index 99cd1d0cd3bd..000000000000 --- a/doc/api/next_api_changes/deprecations/20586-TH.rst +++ /dev/null @@ -1,12 +0,0 @@ -``matplotlib.test()`` is deprecated -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Run tests using ``pytest`` from the commandline instead. The variable -``matplotlib.default_test_modules`` is only used for ``matplotlib.test()`` and -is thus deprecated as well. - -To test an installed copy, be sure to specify both ``matplotlib`` and -``mpl_toolkits`` with ``--pyargs``:: - - python -m pytest --pyargs matplotlib.tests mpl_toolkits.tests - -See :ref:`testing` for more details. diff --git a/doc/api/next_api_changes/deprecations/20601-ES.rst b/doc/api/next_api_changes/deprecations/20601-ES.rst deleted file mode 100644 index 4d30bdd10a1c..000000000000 --- a/doc/api/next_api_changes/deprecations/20601-ES.rst +++ /dev/null @@ -1,9 +0,0 @@ -Selector widget state internals -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Several `~matplotlib.widgets.EllipseSelector`, -`~matplotlib.widgets.RectangleSelector` and `~matplotlib.widgets.SpanSelector` -class internals have been privatized and deprecated: - -- ``eventpress`` -- ``eventrelease`` -- ``state`` diff --git a/doc/api/next_api_changes/deprecations/20603-AL.rst b/doc/api/next_api_changes/deprecations/20603-AL.rst deleted file mode 100644 index d64b4f6c9001..000000000000 --- a/doc/api/next_api_changes/deprecations/20603-AL.rst +++ /dev/null @@ -1,3 +0,0 @@ -``LassoSelector.onpress`` and ``LassoSelector.onrelease`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -... are deprecated (they are straight aliases for ``press`` and ``release``). diff --git a/doc/api/next_api_changes/deprecations/20606-AL.rst b/doc/api/next_api_changes/deprecations/20606-AL.rst deleted file mode 100644 index 63a728510e4d..000000000000 --- a/doc/api/next_api_changes/deprecations/20606-AL.rst +++ /dev/null @@ -1,4 +0,0 @@ -``axes_grid1.axes_grid.CbarAxes`` and ``axes_grid1.axisartist.CbarAxes`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -These classes are deprecated (they are now dynamically generated based on the -owning axes class). diff --git a/doc/api/next_api_changes/deprecations/20620-ES.rst b/doc/api/next_api_changes/deprecations/20620-ES.rst deleted file mode 100644 index 02ac7e574603..000000000000 --- a/doc/api/next_api_changes/deprecations/20620-ES.rst +++ /dev/null @@ -1,18 +0,0 @@ -``NavigationToolbar2.set_cursor`` and ``backend_tools.SetCursorBase.set_cursor`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Instead, use the `.FigureCanvasBase.set_cursor` method on the canvas (available -as the ``canvas`` attribute on the toolbar or the Figure.) - -``backend_tools.SetCursorBase`` and subclasses -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -``backend_tools.SetCursorBase`` was subclassed to provide backend-specific -implementations of ``set_cursor``. As that is now deprecated, the subclassing -is no longer necessary. Consequently, the following subclasses are also -deprecated: - -- ``matplotlib.backends.backend_gtk3.SetCursorGTK3`` -- ``matplotlib.backends.backend_qt5.SetCursorQt`` -- ``matplotlib.backends._backend_tk.SetCursorTk`` -- ``matplotlib.backends.backend_wx.SetCursorWx`` - -Instead, use the `.backend_tools.ToolSetCursor` class. diff --git a/doc/api/next_api_changes/deprecations/20638-AL.rst b/doc/api/next_api_changes/deprecations/20638-AL.rst deleted file mode 100644 index cd9f5f738eb2..000000000000 --- a/doc/api/next_api_changes/deprecations/20638-AL.rst +++ /dev/null @@ -1,3 +0,0 @@ -``FixedAxisArtistHelper.change_tick_coord`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -... is deprecated with no replacement. diff --git a/doc/api/next_api_changes/deprecations/20686-AL.rst b/doc/api/next_api_changes/deprecations/20686-AL.rst deleted file mode 100644 index 929d3eaeaa6f..000000000000 --- a/doc/api/next_api_changes/deprecations/20686-AL.rst +++ /dev/null @@ -1,2 +0,0 @@ -All parameters of ``imshow`` starting from *aspect* will become keyword-only -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/api/next_api_changes/deprecations/20693-EP.rst b/doc/api/next_api_changes/deprecations/20693-EP.rst deleted file mode 100644 index 7d63e27fdd4f..000000000000 --- a/doc/api/next_api_changes/deprecations/20693-EP.rst +++ /dev/null @@ -1,4 +0,0 @@ -PolygonSelector -^^^^^^^^^^^^^^^ -The *line* attribute is deprecated. If you want to change the selector -artist properties, use the ``set_props`` or ``set_handle_props`` methods. diff --git a/doc/api/next_api_changes/deprecations/20753-AL.rst b/doc/api/next_api_changes/deprecations/20753-AL.rst deleted file mode 100644 index 75b989f5c658..000000000000 --- a/doc/api/next_api_changes/deprecations/20753-AL.rst +++ /dev/null @@ -1,4 +0,0 @@ -Case-insensitive scales -~~~~~~~~~~~~~~~~~~~~~~~ -Previously, scales could be set case-insensitively (e.g. ``set_xscale("LoG")``). -This is deprecated; all builtin scales use lowercase names. diff --git a/doc/api/next_api_changes/deprecations/20795-TAC.rst b/doc/api/next_api_changes/deprecations/20795-TAC.rst deleted file mode 100644 index ada6992f3483..000000000000 --- a/doc/api/next_api_changes/deprecations/20795-TAC.rst +++ /dev/null @@ -1,5 +0,0 @@ -Remove unused pytest fixture -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The fixture ``matplotlib.testing.conftest.mpl_image_comparison_parameters`` -is not used internally by Matplotlib. If you use this please copy it -into your code base. diff --git a/doc/api/next_api_changes/deprecations/20799-GL.rst b/doc/api/next_api_changes/deprecations/20799-GL.rst deleted file mode 100644 index 5c713cfa2ed3..000000000000 --- a/doc/api/next_api_changes/deprecations/20799-GL.rst +++ /dev/null @@ -1,3 +0,0 @@ -``ScalarMappable.callbacksSM`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -is deprecated. Use ``ScalarMappable.callbacks`` instead. diff --git a/doc/api/next_api_changes/deprecations/20806-TH.rst b/doc/api/next_api_changes/deprecations/20806-TH.rst deleted file mode 100644 index 2a9008e75265..000000000000 --- a/doc/api/next_api_changes/deprecations/20806-TH.rst +++ /dev/null @@ -1,4 +0,0 @@ -``matplotlib.cm.LUTSIZE`` -~~~~~~~~~~~~~~~~~~~~~~~~~ -is deprecated. Use :rc:`image.lut` instead. This value only affects colormap -quantization levels for default colormaps generated at module import time. diff --git a/doc/api/next_api_changes/deprecations/20881-AL.rst b/doc/api/next_api_changes/deprecations/20881-AL.rst deleted file mode 100644 index 71c1223a5d55..000000000000 --- a/doc/api/next_api_changes/deprecations/20881-AL.rst +++ /dev/null @@ -1,3 +0,0 @@ -``matplotlib.backends.qt_compat.ETS`` and ``matplotlib.backends.qt_compat.QT_RC_MAJOR_VERSION`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -... are deprecated, with no replacement. diff --git a/doc/api/next_api_changes/deprecations/21126-TH.rst b/doc/api/next_api_changes/deprecations/21126-TH.rst deleted file mode 100644 index 43df16287319..000000000000 --- a/doc/api/next_api_changes/deprecations/21126-TH.rst +++ /dev/null @@ -1,2 +0,0 @@ -Passing formatting parameters positionally to ``stem()`` is deprecated -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/api/next_api_changes/deprecations/21356-AL.rst b/doc/api/next_api_changes/deprecations/21356-AL.rst new file mode 100644 index 000000000000..de6cb79dd908 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/21356-AL.rst @@ -0,0 +1,5 @@ +In the future, ``dviread.find_tex_file`` will raise a ``FileNotFoundError`` for missing files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Previously, it would return an empty string in such cases. Raising an +exception allows attaching a user-friendly message instead. During the +transition period, a warning is raised. diff --git a/doc/api/next_api_changes/deprecations/XXXXX-AL.rst b/doc/api/next_api_changes/deprecations/XXXXX-AL.rst deleted file mode 100644 index bd15c20af312..000000000000 --- a/doc/api/next_api_changes/deprecations/XXXXX-AL.rst +++ /dev/null @@ -1,3 +0,0 @@ -``matplotlib.streamplot.get_integrator`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -... is deprecated. diff --git a/doc/api/next_api_changes/development/20003-ES.rst b/doc/api/next_api_changes/development/20003-ES.rst deleted file mode 100644 index 48cd21bf4878..000000000000 --- a/doc/api/next_api_changes/development/20003-ES.rst +++ /dev/null @@ -1,15 +0,0 @@ -Increase to minimum supported versions of dependencies -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For Maptlotlib 3.5, the :ref:`minimum supported versions ` are -being bumped: - -+------------+-----------------+---------------+ -| Dependency | min in mpl3.4 | min in mpl3.5 | -+============+=================+===============+ -| NumPy | 1.16 | 1.17 | -+------------+-----------------+---------------+ - -This is consistent with our :ref:`min_deps_policy` and `NEP29 -`__ - diff --git a/doc/api/next_api_changes/development/20391-AG.rst b/doc/api/next_api_changes/development/20391-AG.rst deleted file mode 100644 index 37cc539c5ad2..000000000000 --- a/doc/api/next_api_changes/development/20391-AG.rst +++ /dev/null @@ -1,8 +0,0 @@ -fontTools for type 42 subsetting -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A new dependency known as `fontTools `_ -is integrated in with Maptlotlib 3.5 - -It is designed to be used with PS/EPS and PDF documents; and handles -Type 42 font subsetting. diff --git a/doc/api/next_api_changes/development/20706-AP.rst b/doc/api/next_api_changes/development/20706-AP.rst deleted file mode 100644 index 4c46c020f2cb..000000000000 --- a/doc/api/next_api_changes/development/20706-AP.rst +++ /dev/null @@ -1,4 +0,0 @@ -Improve underscore support in LaTeX -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The `underscore `_ package is now a requirement to improve support for underscores in LaTeX. diff --git a/doc/api/next_api_changes/development/20840-RJS.rst b/doc/api/next_api_changes/development/20840-RJS.rst deleted file mode 100644 index 443331d7c887..000000000000 --- a/doc/api/next_api_changes/development/20840-RJS.rst +++ /dev/null @@ -1,13 +0,0 @@ -Increase to minimum supported optional dependencies -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For Matplotlib 3.5, the :ref:`minimum supported versions of optional dependencies -` are being bumped: - -+------------+-----------------+---------------+ -| Dependency | min in mpl3.4 | min in mpl3.5 | -+============+=================+===============+ -| Tk | 8.3 | 8.4 | -+------------+-----------------+---------------+ - -This is consistent with our :ref:`min_deps_policy` diff --git a/doc/api/next_api_changes/development/XXXXX-AL.rst b/doc/api/next_api_changes/development/XXXXX-AL.rst deleted file mode 100644 index 330dfc9e7300..000000000000 --- a/doc/api/next_api_changes/development/XXXXX-AL.rst +++ /dev/null @@ -1,8 +0,0 @@ -Matplotlib-specific build options have moved from ``setup.cfg`` to ``mplsetup.cfg`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -... in order to avoid conflicting with the use of :file:`setup.cfg` by -``setuptools``. - -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. diff --git a/doc/api/next_api_changes/removals/19033-AL.rst b/doc/api/next_api_changes/removals/19033-AL.rst deleted file mode 100644 index 258950c1b86a..000000000000 --- a/doc/api/next_api_changes/removals/19033-AL.rst +++ /dev/null @@ -1,6 +0,0 @@ -The private ``matplotlib.axes._subplots._subplot_classes`` dict has been removed -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Support for passing ``None`` to ``subplot_class_factory`` has been removed -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Explicitly pass in the base `~matplotlib.axes.Axes` class instead. diff --git a/doc/api/next_api_changes/removals/19348-OE.rst b/doc/api/next_api_changes/removals/19348-OE.rst deleted file mode 100644 index 4045b7e3d02b..000000000000 --- a/doc/api/next_api_changes/removals/19348-OE.rst +++ /dev/null @@ -1,6 +0,0 @@ -Removed ``dates.YearLocator.replaced`` attribute -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -`.YearLocator` is now a subclass of `.RRuleLocator`, and the attribute -``YearLocator.replaced`` has been removed. For tick locations that -required modifying this, a custom rrule and `.RRuleLocator` can be used instead. \ No newline at end of file diff --git a/doc/api/next_api_changes/removals/19552-GL.rst b/doc/api/next_api_changes/removals/19552-GL.rst deleted file mode 100644 index be0bebd3d89e..000000000000 --- a/doc/api/next_api_changes/removals/19552-GL.rst +++ /dev/null @@ -1,5 +0,0 @@ -ScalarMappable update checkers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -``ScalarMappable.update_dict``, ``ScalarMappable.add_checker()``, and -``ScalarMappable.check_update()`` have been removed. A callback can -be registered in ``ScalarMappable.callbacks`` to be notified of updates. \ No newline at end of file diff --git a/doc/api/next_api_changes/removals/19795-AL.rst b/doc/api/next_api_changes/removals/19795-AL.rst deleted file mode 100644 index dba29fabdcfc..000000000000 --- a/doc/api/next_api_changes/removals/19795-AL.rst +++ /dev/null @@ -1,11 +0,0 @@ -usetex-related removals -~~~~~~~~~~~~~~~~~~~~~~~ - -The ``text.latex.preview`` rcParam and associated methods -(``TexManager.make_tex_preview``, ``TexManager.make_dvi_preview``) have been -removed. - -The ``cachedir``, ``rgba_arrayd``, ``serif``, ``sans_serif``, ``cursive``, and -``monospace`` attributes of ``TexManager`` have been removed. - -``dviread.Encoding`` has been removed. diff --git a/doc/api/next_api_changes/removals/19796-AL.rst b/doc/api/next_api_changes/removals/19796-AL.rst deleted file mode 100644 index d2e51427093a..000000000000 --- a/doc/api/next_api_changes/removals/19796-AL.rst +++ /dev/null @@ -1,3 +0,0 @@ -Keymaps toggling ``Axes.get_navigate`` have been removed -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This includes numeric key events and the ``keymap.all_axes`` rcParam. diff --git a/doc/api/next_api_changes/removals/19801-AL.rst b/doc/api/next_api_changes/removals/19801-AL.rst deleted file mode 100644 index 6451e335309a..000000000000 --- a/doc/api/next_api_changes/removals/19801-AL.rst +++ /dev/null @@ -1,3 +0,0 @@ -The ``mathtext.fallback_to_cm`` rcParams -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -... has been removed. Use :rc:`mathtext.fallback` instead. diff --git a/doc/api/next_api_changes/removals/19810-AL.rst b/doc/api/next_api_changes/removals/19810-AL.rst deleted file mode 100644 index 2b641a2e622f..000000000000 --- a/doc/api/next_api_changes/removals/19810-AL.rst +++ /dev/null @@ -1,10 +0,0 @@ -jpeg-related keywords and rcParams -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Support has been removed for the *quality*, *optimize*, and *progressive* -parameters of `.Figure.savefig` (which only affected jpeg output), as well as -:rc:`savefig.jpeg_quality`. This support has also been removed from the -corresponding ``print_jpg`` methods. - -JPEG output options can be set by directly passing the relevant parameters in -*pil_kwargs*. diff --git a/doc/api/next_api_changes/removals/19894-ES.rst b/doc/api/next_api_changes/removals/19894-ES.rst deleted file mode 100644 index a18fb1cb8206..000000000000 --- a/doc/api/next_api_changes/removals/19894-ES.rst +++ /dev/null @@ -1,15 +0,0 @@ -Qt4-based backends -~~~~~~~~~~~~~~~~~~ -The qt4agg and qt4cairo backends have been removed. Qt4 has reached its -end-of-life in 2015 and there are no releases of either PyQt4 or PySide for -recent versions of Python. Please use one of the Qt5 or Qt6 backends. - -``qt_compat.is_pyqt5`` -~~~~~~~~~~~~~~~~~~~~~~ -This function has been removed due to the release of PyQt6. The Qt version can -be checked using ``QtCore.qVersion()``. - -``matplotlib.backends.qt_editor.formsubplottool`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This module has been removed. Use -``matplotlib.backends.backend_qt5.SubplotToolQt`` instead. diff --git a/doc/api/next_api_changes/removals/19898-ES.rst b/doc/api/next_api_changes/removals/19898-ES.rst deleted file mode 100644 index 9ae0a64c1cca..000000000000 --- a/doc/api/next_api_changes/removals/19898-ES.rst +++ /dev/null @@ -1,70 +0,0 @@ -NavigationToolbar2._init_toolbar -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Overriding this method to initialize third-party toolbars is no longer -supported. Instead, the toolbar should be initialized in the ``__init__`` -method of the subclass (which should call the base-class' ``__init__`` as -appropriate). - -NavigationToolbar2.press and .release -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -These methods were called when pressing or releasing a mouse button, but *only* -when an interactive pan or zoom was occurring (contrary to what the docs -stated). They are no longer called; if you write a backend which needs to -customize such events, please directly override -``press_pan``/``press_zoom``/``release_pan``/``release_zoom`` instead. - -NavigationToolbar2QT.parent and .basedir -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -These attributes have been removed. In order to access the parent window, use -``toolbar.canvas.parent()`` or ``toolbar.parent()``. The base directory to the -icons is ``os.path.join(mpl.get_data_path(), "images")``. - -NavigationToolbar2QT.ctx -~~~~~~~~~~~~~~~~~~~~~~~~ -This attribute has been removed. - -NavigationToolbar2Wx attributes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The ``prevZoomRect``, ``retinaFix``, ``savedRetinaImage``, ``wxoverlay``, -``zoomAxes``, ``zoomStartX``, and ``zoomStartY`` attributes have been removed. - -Statusbar classes and attributes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The ``statusbar`` attribute of `.FigureManagerBase`, as well as -``backend_bases.StatusbarBase`` and all its subclasses, and ``StatusBarWx``, -have been removed, as messages are now displayed in the toolbar instead. - -Backend-specific internals -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The following module-level classes/variables have been removed: - -* ``MODIFIER_KEYS``, ``SUPER``, ``ALT``, ``CTRL``, and ``SHIFT`` of - :mod:`matplotlib.backends.backend_qt5agg` and - :mod:`matplotlib.backends.backend_qt5cairo` -* ``backend_pgf.GraphicsContextPgf`` -* ``backend_wx.DEBUG_MSG`` - -The following parameters have been removed: - -* The *dummy* parameter of `.RendererPgf` - -The following class attributes have been removed: - -* ``RendererCairo.fontweights``, ``RendererCairo.fontangles`` -* ``backend_pgf.LatexManager.latex_stdin_utf8`` -* ``backend_pgf.PdfPages.metadata`` -* ``used_characters`` of `.RendererPdf`, `.PdfFile`, and `.RendererPS` - -The following class methods have been removed: - -* ``FigureCanvasGTK3._renderer_init`` -* ``track_characters`` and ``merge_used_characters`` of `.RendererPdf`, - `.PdfFile`, and `.RendererPS` -* ``RendererWx.get_gc`` - -Stricter PDF metadata keys in PGF -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Saving metadata in PDF with the PGF backend no longer changes keys to -lowercase. Only the canonically cased keys listed in the PDF specification (and -the `~.backend_pgf.PdfPages` documentation) are accepted. diff --git a/doc/api/next_api_changes/removals/19900-ES.rst b/doc/api/next_api_changes/removals/19900-ES.rst deleted file mode 100644 index 7cb4bc13c5a6..000000000000 --- a/doc/api/next_api_changes/removals/19900-ES.rst +++ /dev/null @@ -1,27 +0,0 @@ -The ``TTFPATH`` and ``AFMPATH`` environment variables -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Support for the (undocumented) ``TTFPATH`` and ``AFMPATH`` environment -variables has been removed. Register additional fonts using -``matplotlib.font_manager.fontManager.addfont()``. - -mathtext ``Glue`` classes -~~~~~~~~~~~~~~~~~~~~~~~~~ -The following have been removed from `matplotlib.mathtext`: - -* *copy* parameter of ``mathtext.Glue`` -* ``mathtext.Glue.glue_subtype`` -* ``mathtext.GlueSpec`` -* ``Fil``, ``Fill``, ``Filll``, ``NegFil``, ``NegFill``, ``NegFilll``, and - ``SsGlue``; directly construct glue instances with ``Glue("fil")``, etc. - -*ismath* parameter of ``draw_tex`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The *ismath* parameter of the ``draw_tex`` method of all renderer classes has -been removed (as a call to ``draw_tex`` -- not to be confused with -``draw_text``! -- means that the entire string should be passed to the -``usetex`` machinery anyways). Likewise, the text machinery will no longer pass -the *ismath* parameter when calling ``draw_tex`` (this should only matter for -backend implementers). - -Passing ``ismath="TeX!"`` to `.RendererAgg.get_text_width_height_descent` is no -longer supported; pass ``ismath="TeX"`` instead, diff --git a/doc/api/next_api_changes/removals/19901-ES.rst b/doc/api/next_api_changes/removals/19901-ES.rst deleted file mode 100644 index 523f8fa0e304..000000000000 --- a/doc/api/next_api_changes/removals/19901-ES.rst +++ /dev/null @@ -1,22 +0,0 @@ -Deprecated rcParams validators -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The following validators, defined in `.rcsetup`, have been removed: -``validate_alignment``, ``validate_axes_titlelocation``, -``validate_axis_locator``, ``validate_bool_maybe_none``, ``validate_fontset``, -``validate_grid_axis``, ``validate_hinting``, ``validate_legend_loc``, -``validate_mathtext_default``, ``validate_movie_frame_fmt``, -``validate_movie_html_fmt``, ``validate_movie_writer``, -``validate_nseq_float``, ``validate_nseq_int``, ``validate_orientation``, -``validate_pgf_texsystem``, ``validate_ps_papersize``, -``validate_svg_fontset``, ``validate_toolbar``, ``validate_webagg_address``. - -Stricter rcParam validation -~~~~~~~~~~~~~~~~~~~~~~~~~~~ -:rc:`axes.axisbelow` no longer accepts strings starting with "line" -(case-insensitive) as "line"; use "line" (case-sensitive) instead. - -The :rc:`text.latex.preamble` and :rc:`pdf.preamble` no longer accept -non-string values. - -All ``*.linestyle`` rcParams no longer accept ``offset = None``; set the offset -to 0 instead. diff --git a/doc/api/next_api_changes/removals/19922-ES.rst b/doc/api/next_api_changes/removals/19922-ES.rst deleted file mode 100644 index 9b73570c2cbd..000000000000 --- a/doc/api/next_api_changes/removals/19922-ES.rst +++ /dev/null @@ -1,7 +0,0 @@ -Removed modules -~~~~~~~~~~~~~~~ - -The following deprecated modules have been removed: - -* ``matplotlib.compat`` -* ``matplotlib.ttconv`` diff --git a/doc/api/next_api_changes/removals/20051-AL.rst b/doc/api/next_api_changes/removals/20051-AL.rst deleted file mode 100644 index c3679a3aaf2d..000000000000 --- a/doc/api/next_api_changes/removals/20051-AL.rst +++ /dev/null @@ -1,8 +0,0 @@ -Removal of deprecated ``offsetbox`` APIs -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The following APIs were removed: - -- the *s* kwarg to `.AnnotationBbox.get_fontsize` (which had no effect), -- the ``DraggableBase.artist_picker`` method (set the artist's picker instead), -- the ``DraggableBase.on_motion_blit`` method (use `.DraggableBase.on_motion` - instead). diff --git a/doc/api/next_api_changes/removals/20052-AL.rst b/doc/api/next_api_changes/removals/20052-AL.rst deleted file mode 100644 index a250cb2bdc9a..000000000000 --- a/doc/api/next_api_changes/removals/20052-AL.rst +++ /dev/null @@ -1,20 +0,0 @@ -Removal of ``axes_grid``/``axisartist`` APIs deprecated in Matplotlib 3.3 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The following deprecated APIs have been removed: - -- ``CbarAxesBase.cbid`` and ``CbarAxesBase.locator`` (use - ``mappable.colorbar_cid`` and ``colorbar.locator`` instead), -- the ``add_all`` parameter to ``axes_grid.Grid``, ``axes_grid.ImageGrid``, - ``axes_rgb.make_rgb_axes``, ``axes_rgb.RGBAxes`` (the APIs always behave as - if ``add_all=True``), -- ``axes_rgb.imshow_rgb`` (use ``imshow(np.dstack([r, g, b]))`` instead), -- ``RGBAxes.add_RGB_to_figure`` (no replacement), -- ``RGBAxesBase`` (use ``RGBAxes`` instead), -- passing ``None``, or no argument, to ``parasite_axes_class_factory``, - ``parasite_axes_auxtrans_class_factory``, ``host_axes_class_factory`` - (pass an explicit base class instead), -- the ``den`` parameter and attribute of ``angle_helper.LocatorBase`` (it has - been renamed to ``nbins``), -- ``AxisArtist.dpi_transform`` (no replacement), -- ``grid_finder.MaxNLocator.set_factor`` and ``grid_finder.FixedLocator.set_factor`` - (the factor is always 1 now). diff --git a/doc/api/next_api_changes/removals/20095-ES.rst b/doc/api/next_api_changes/removals/20095-ES.rst deleted file mode 100644 index 4338ded19bdb..000000000000 --- a/doc/api/next_api_changes/removals/20095-ES.rst +++ /dev/null @@ -1,20 +0,0 @@ -Axis and Locator ``pan`` and ``zoom`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The unused ``pan`` and ``zoom`` methods of `~.axis.Axis` and `~.ticker.Locator` -have been removed. Panning and zooming are now implemented using the -``start_pan``, ``drag_pan``, and ``end_pan`` methods of `~.axes.Axes`. - -Ticker Locators and Formatters -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The following deprecated `.Locator`\s and `.Formatter`\s have been removed: - -* ``OldScalarFormatter``, ``IndexFormatter`` and ``IndexDateFormatter``; use - `.FuncFormatter` instead. -* ``OldAutoLocator`` - -The following deprecated properties and methods have been removed: - -* ``DateFormatter.illegal_s`` -* ``Locator.refresh()`` and the associated helper methods - ``NavigationToolbar2.draw()`` and ``ToolViewsPositions.refresh_locators()`` diff --git a/doc/api/next_api_changes/removals/20188-ES.rst b/doc/api/next_api_changes/removals/20188-ES.rst deleted file mode 100644 index 577e5dcb56ce..000000000000 --- a/doc/api/next_api_changes/removals/20188-ES.rst +++ /dev/null @@ -1,10 +0,0 @@ -Removal of ``cbook`` deprecations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* ``local_over_kwdict`` has been removed; use `.cbook.normalize_kwargs` - instead. -* *required*, *forbidden* and *allowed* parameters of `.cbook.normalize_kwargs` - have been removed. -* Flags containing "U" passed to `.cbook.to_filehandle` and - `.cbook.open_file_cm` are no longer accepted. This is consistent with their - removal from `open` in Python 3.9. diff --git a/doc/api/next_api_changes/removals/20245-GL.rst b/doc/api/next_api_changes/removals/20245-GL.rst deleted file mode 100644 index 6b4fa862aca6..000000000000 --- a/doc/api/next_api_changes/removals/20245-GL.rst +++ /dev/null @@ -1,7 +0,0 @@ -Collection's offset_position has been removed -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The deprecated keyword argument *offset_position* has been removed from -the Collection class, along with the setter and getter -``Collection.set_offset_position()`` and ``Collection.get_offset_position()``. -The ``offset_position`` of the Collection class is now "screen". diff --git a/doc/api/next_api_changes/removals/20314-GL.rst b/doc/api/next_api_changes/removals/20314-GL.rst deleted file mode 100644 index 042708d6adf0..000000000000 --- a/doc/api/next_api_changes/removals/20314-GL.rst +++ /dev/null @@ -1,11 +0,0 @@ -Colorbar related removals -~~~~~~~~~~~~~~~~~~~~~~~~~ - -``Colorbar.on_mappable_changed()`` and ``Colorbar.update_bruteforce()`` have -been removed, you can use ``Colorbar.update_normal()`` instead. - -``ColorbarBase`` only takes a single positional argument now, the ``Axes`` to -create it in, with all other options required to be keyword arguments. - -The warning for keyword arguments that were overridden by the mappable -is now removed. diff --git a/doc/api/next_api_changes/removals/20331-ES.rst b/doc/api/next_api_changes/removals/20331-ES.rst deleted file mode 100644 index d68a567c342e..000000000000 --- a/doc/api/next_api_changes/removals/20331-ES.rst +++ /dev/null @@ -1,63 +0,0 @@ -``Axes.pie`` *radius*, *startangle*, and *normalize* parameters -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Passing ``None`` as either the *radius* or *startangle* arguments of an -`.Axes.pie` is no longer accepted; use the explicit defaults of 1 and 0, -respectively, instead. - -Passing ``None`` as the *normalize* argument of `.Axes.pie` (the former -default) is no longer accepted, and the pie will always be normalized by -default. If you wish to plot an incomplete pie, explicitly pass -``normalize=False``. - -Signatures of `.Artist.draw` and `.Axes.draw` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The *inframe* parameter to `.Axes.draw` has been removed. Use -`.Axes.redraw_in_frame` instead. - -Omitting the *renderer* parameter to `.Axes.draw` is no longer supported. -Use ``axes.draw_artist(axes)`` instead. - -These changes make the signature of the ``draw`` (``artist.draw(renderer)``) -method consistent across all artists; thus, additional parameters to -`.Artist.draw` have also been removed. - -``Axes.update_datalim_bounds`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This method has been removed. Use -``ax.dataLim.set(Bbox.union([ax.dataLim, bounds]))`` instead. - -``{,Symmetrical}LogScale.{,Inverted}LogTransform`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -``LogScale.LogTransform``, ``LogScale.InvertedLogTransform``, -``SymmetricalScale.SymmetricalTransform`` and -``SymmetricalScale.InvertedSymmetricalTransform`` have been removed. Directly -access the transform classes from the `matplotlib.scale` module. - -log/symlog scale base, ticks, and nonpos specification -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -`~.Axes.semilogx`, `~.Axes.semilogy`, `~.Axes.loglog`, `.LogScale`, and -`.SymmetricalLogScale` used to take keyword arguments that depends on the axis -orientation ("basex" vs "basey", "subsx" vs "subsy", "nonposx" vs "nonposy"); -these parameter names have been removed in favor of "base", "subs", -"nonpositive". This removal also affects e.g. ``ax.set_yscale("log", -basey=...)`` which must now be spelled ``ax.set_yscale("log", base=...)``. - -The change from "nonpos" to "nonpositive" also affects `~.scale.LogTransform`, -`~.scale.InvertedLogTransform`, `~.scale.SymmetricalLogTransform`, etc. - -To use *different* bases for the x-axis and y-axis of a `~.Axes.loglog` plot, -use e.g. ``ax.set_xscale("log", base=10); ax.set_yscale("log", base=2)``. - -Implicit initialization of ``Tick`` attributes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The `.Tick` constructor no longer initializes the attributes ``tick1line``, -``tick2line``, ``gridline``, ``label1``, and ``label2`` via ``_get_tick1line``, -``_get_tick2line``, ``_get_gridline``, ``_get_text1``, and ``_get_text2``. -Please directly set the attribute in the subclass' ``__init__`` instead. - -Unused parameters -~~~~~~~~~~~~~~~~~ -The following parameters do not have any effect and have been removed: - -- parameter *label* of `.Tick` diff --git a/doc/api/next_api_changes/removals/20444-CE.rst b/doc/api/next_api_changes/removals/20444-CE.rst deleted file mode 100644 index 7ae2e4242a39..000000000000 --- a/doc/api/next_api_changes/removals/20444-CE.rst +++ /dev/null @@ -1,4 +0,0 @@ -Change the drawing parameter in ArrowStyle -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -For drawing arrows heads or brackets at the arrow, use the ``arrow`` -parameter with the drawing of the desired arrow. diff --git a/doc/api/next_api_changes/removals/20447-ES.rst b/doc/api/next_api_changes/removals/20447-ES.rst deleted file mode 100644 index 20e4aae8fd0d..000000000000 --- a/doc/api/next_api_changes/removals/20447-ES.rst +++ /dev/null @@ -1,58 +0,0 @@ -``figure.add_axes()`` without arguments -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Calling ``fig.add_axes()`` with no arguments will raise an error. Adding a -free-floating axes needs a position rectangle. If you want a figure-filling -single axes, use ``add_subplot()`` instead. - -All parameters of `.Figure.subplots` except *nrows* and *ncols* are now keyword-only -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This avoids typing e.g. ``subplots(1, 1, 1)`` when meaning ``subplot(1, 1, 1)``, -but actually getting ``subplots(1, 1, sharex=1)``. - -``add_subplot()`` validates its inputs -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In particular, for ``add_subplot(rows, cols, index)``, all parameters must -be integral. Previously strings and floats were accepted and converted to -int. - -``SubplotSpec.get_rows_columns`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Use the ``GridSpec.nrows``, ``GridSpec.ncols``, ``SubplotSpec.rowspan``, and -``SubplotSpec.colspan`` properties instead. - -``autofmt_xdate(which=None)`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Use its more explicit synonym, ``which="major"``, instead. - -`.pyplot.tight_layout` parameters are now keyword-only -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -All parameters of `.pyplot.tight_layout` are now keyword-only, to be consistent -with `.Figure.tight_layout`. - -Parameters *norm* and *vmin*/*vmax* may not be used simultaneously -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Passing parameters *norm* and *vmin*/*vmax* simultaneously to functions using -colormapping such as ``scatter()`` and ``imshow()`` is no longer supported. -Instead of ``norm=LogNorm(), vmin=min_val, vmax=max_val`` pass -``norm=LogNorm(min_val, max_val)``. *vmin* and *vmax* should only be used -without setting *norm*. - -`.Axes.annotate` and `.pyplot.annotate` parameter name changed -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The parameter *s* to `.Axes.annotate` and `.pyplot.annotate` has previously -been renamed to *text*, matching `.Annotation`. The old parameter name is no -longer supported. - -*orientation* of ``eventplot()`` and `.EventCollection` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Setting the *orientation* of an ``eventplot()`` or `.EventCollection` to "none" -or None is no longer supported; set it to "horizontal" instead. Moreover, the two -orientations ("horizontal" and "vertical") are now case-sensitive. - -``ContourSet.ax``, ``Quiver.ax`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -These attributes have been removed in favor of ``ContourSet.axes`` and -``Quiver.axes``, for consistency with other artists. - -*fontdict* and *minor* parameters of `.Axes.set_xticklabels` / `.Axes.set_yticklabels` are now keyword-only -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/api/next_api_changes/removals/20465-ES.rst b/doc/api/next_api_changes/removals/20465-ES.rst deleted file mode 100644 index abe773d5b2a2..000000000000 --- a/doc/api/next_api_changes/removals/20465-ES.rst +++ /dev/null @@ -1,93 +0,0 @@ -Case-insensitive properties -~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Upper or mixed-case property names are no longer normalized to lowercase in -`.Artist.set` and `.Artist.update`. This allows one to pass names such as -*patchA* or *UVC*. - -Case-insensitive capstyles and joinstyles -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Please pass capstyles ("miter", "round", "bevel") and joinstyles ("butt", -"round", "projecting") as lowercase. - -AVConv animation writers removed -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The ``AVConvBase``, ``AVConvWriter`` and ``AVConvFileWriter`` classes, and the -associated ``animation.avconv_path`` and ``animation.avconv_args`` rcParams -have been removed. - -Debian 8 (2015, EOL 06/2020) and Ubuntu 14.04 (EOL 04/2019) were the -last versions of Debian and Ubuntu to ship avconv. It remains possible -to force the use of avconv by using the FFmpeg-based writers with -:rc:`animation.ffmpeg_path` set to "avconv". - -``MovieWriter`` attributes -~~~~~~~~~~~~~~~~~~~~~~~~~~ -* ``animation.html_args`` rcParam -* ``HTMLWriter.args_key`` attribute -* ``MovieWriter.args_key`` and ``MovieWriter.exec_key`` attributes -* *clear_temp* parameter and attribute of `.FileMovieWriter`; files placed in a - temporary directory (using ``frame_prefix=None``, the default) will be - cleared; files placed elsewhere will not. - -Artist-specific removals -~~~~~~~~~~~~~~~~~~~~~~~~ -* Setting a custom method overriding `.Artist.contains` using - ``Artist.set_contains`` has been removed, as has ``Artist.get_contains``. - There is no replacement, but you may still customize pick events using - `.Artist.set_picker`. -* Passing the dash offset as ``None`` is no longer accepted, as this was never - universally implemented, e.g. for vector output. Set the offset to 0 instead. -* The parameter *props* of `.Shadow` has been removed. Use keyword arguments - instead. -* Arbitrary keyword arguments to ``StreamplotSet`` have no effect and have been - removed. -* ``NonUniformImage.is_grayscale`` and ``PcolorImage.is_grayscale`` attributes - have been removed, for consistency with ``AxesImage.is_grayscale``. (Note - that previously, these attributes were only available *after rendering the - image*). - -Path helpers -~~~~~~~~~~~~ -* ``bezier.make_path_regular``; use ``Path.cleaned()`` (or - ``Path.cleaned(curves=True)``, etc.) instead, but note that these methods add - a ``STOP`` code at the end of the path. -* ``bezier.concatenate_paths``; use ``Path.make_compound_path()`` instead. -* *quantize* parameter of `.Path.cleaned()` - -``BboxBase.inverse_transformed`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -``.BboxBase.inverse_transformed`` has been removed (call `.BboxBase.transformed` -on the `~.Transform.inverted()` transform instead). - -``matplotlib.test(recursionlimit=...)`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The *recursionlimit* parameter of `matplotlib.test` has been removed. - -``testing.compare.make_external_conversion_command`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -... has been removed. - -``docstring.Substitution.from_params`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This method has been removed. If needed, directly assign to the ``params`` -attribute of the Substitution object. - -``widgets.TextBox.params_to_disable`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This attribute has been removed. - -`.widgets.SubplotTool` callbacks and axes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The ``funcleft``, ``funcright``, ``funcbottom``, ``functop``, ``funcwspace``, -and ``funchspace`` methods of `.widgets.SubplotTool` have been removed. - -The ``axleft``, ``axright``, ``axbottom``, ``axtop``, ``axwspace``, and -``axhspace`` attributes of `.widgets.SubplotTool` have been removed. Access -the ``ax`` attribute of the corresponding slider, if needed. - -Variants on ``ToolManager.update_keymap`` calls -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Passing multiple keys as a single comma-separated string or multiple arguments -to `.ToolManager.update_keymap` is no longer supported; pass keys as a list of -strings instead. diff --git a/doc/api/next_api_changes/removals/20563-TH.rst b/doc/api/next_api_changes/removals/20563-TH.rst deleted file mode 100644 index 2908b9ddab5e..000000000000 --- a/doc/api/next_api_changes/removals/20563-TH.rst +++ /dev/null @@ -1,4 +0,0 @@ -MaxNLocator passing *nbins* in two ways -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -`~.ticker.MaxNLocator` does not accept a positional parameter and the keyword -argument *nbins* simultaneously because they specify the same quantity. \ No newline at end of file diff --git a/doc/api/prev_api_changes/api_changes_3.5.0.rst b/doc/api/prev_api_changes/api_changes_3.5.0.rst new file mode 100644 index 000000000000..890484bcd19a --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.5.0.rst @@ -0,0 +1,14 @@ +API Changes for 3.5.0 +===================== + +.. contents:: + :local: + :depth: 1 + +.. include:: /api/prev_api_changes/api_changes_3.5.0/behaviour.rst + +.. include:: /api/prev_api_changes/api_changes_3.5.0/deprecations.rst + +.. include:: /api/prev_api_changes/api_changes_3.5.0/removals.rst + +.. include:: /api/prev_api_changes/api_changes_3.5.0/development.rst diff --git a/doc/api/prev_api_changes/api_changes_3.5.0/behaviour.rst b/doc/api/prev_api_changes/api_changes_3.5.0/behaviour.rst new file mode 100644 index 000000000000..62cdb0a32854 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.5.0/behaviour.rst @@ -0,0 +1,247 @@ +Behaviour changes +----------------- + +First argument to ``subplot_mosaic`` renamed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Both `.FigureBase.subplot_mosaic`, and `.pyplot.subplot_mosaic` have had the +first positional argument renamed from *layout* to *mosaic*. As we have +consolidated the *constrained_layout* and *tight_layout* keyword arguments in +the Figure creation functions of `.pyplot` into a single *layout* keyword +argument, the original ``subplot_mosaic`` argument name would collide. + +As this API is provisional, we are changing this argument name with no +deprecation period. + +.. _Behavioural API Changes 3.5 - Axes children combined: + +``Axes`` children are no longer separated by type +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Formerly, `.axes.Axes` children were separated by `.Artist` type, into sublists +such as ``Axes.lines``. For methods that produced multiple elements (such as +`.Axes.errorbar`), though individual parts would have similar *zorder*, this +separation might cause them to be drawn at different times, causing +inconsistent results when overlapping other Artists. + +Now, the children are no longer separated by type, and the sublist properties +are generated dynamically when accessed. Consequently, Artists will now always +appear in the correct sublist; e.g., if `.axes.Axes.add_line` is called on a +`.Patch`, it will appear in the ``Axes.patches`` sublist, *not* ``Axes.lines``. +The ``Axes.add_*`` methods will now warn if passed an unexpected type. + +Modification of the following sublists is still accepted, but deprecated: + +* ``Axes.artists`` +* ``Axes.collections`` +* ``Axes.images`` +* ``Axes.lines`` +* ``Axes.patches`` +* ``Axes.tables`` +* ``Axes.texts`` + +To remove an Artist, use its `.Artist.remove` method. To add an Artist, use the +corresponding ``Axes.add_*`` method. + +``MatplotlibDeprecationWarning`` now subclasses ``DeprecationWarning`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Historically, it has not been possible to filter +`.MatplotlibDeprecationWarning`\s by checking for `DeprecationWarning`, since we +subclass `UserWarning` directly. + +The decision to not subclass `DeprecationWarning` has to do with a decision +from core Python in the 2.x days to not show `DeprecationWarning`\s to users. +However, there is now a more sophisticated filter in place (see +https://www.python.org/dev/peps/pep-0565/). + +Users will now see `.MatplotlibDeprecationWarning` only during interactive +sessions, and these can be silenced by the standard mechanism: + +.. code:: python + + warnings.filterwarnings("ignore", category=DeprecationWarning) + +Library authors must now enable `DeprecationWarning`\s explicitly in order for +(non-interactive) CI/CD pipelines to report back these warnings, as is standard +for the rest of the Python ecosystem: + +.. code:: python + + warnings.filterwarnings("always", DeprecationWarning) + +``Artist.set`` applies artist properties in the order in which they are given +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The change only affects the interaction between the *color*, *edgecolor*, +*facecolor*, and (for `.Collection`\s) *alpha* properties: the *color* property +now needs to be passed first in order not to override the other properties. +This is consistent with e.g. `.Artist.update`, which did not reorder the +properties passed to it. + +``pcolor(mesh)`` shading defaults to auto +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The *shading* keyword argument for `.Axes.pcolormesh` and `.Axes.pcolor` +default has been changed to 'auto'. + +Passing ``Z(M, N)``, ``x(N)``, ``y(M)`` to ``pcolormesh`` with +``shading='flat'`` will now raise a `TypeError`. Use ``shading='auto'`` or +``shading='nearest'`` for ``x`` and ``y`` to be treated as cell centers, or +drop the last column and row of ``Z`` to get the old behaviour with +``shading='flat'``. + +Colorbars now have pan and zoom functionality +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Interactive plots with colorbars can now be zoomed and panned on the colorbar +axis. This adjusts the *vmin* and *vmax* of the `.ScalarMappable` associated +with the colorbar. This is currently only enabled for continuous norms. Norms +used with ``contourf`` and categoricals, such as `.BoundaryNorm` and `.NoNorm`, +have the interactive capability disabled by default. `cb.ax.set_navigate() +<.Axes.set_navigate>` can be used to set whether a colorbar axes is interactive +or not. + +Colorbar lines no longer clipped +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If a colorbar has lines added to it (e.g. for contour lines), these will no +longer be clipped. This is an improvement for lines on the edge of the +colorbar, but could lead to lines off the colorbar if the limits of the +colorbar are changed. + +``Figure.suppressComposite`` now also controls compositing of Axes images +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The output of ``NonUniformImage`` and ``PcolorImage`` has changed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Pixel-level differences may be observed in images generated using +`.NonUniformImage` or `.PcolorImage`, typically for pixels exactly at the +boundary between two data cells (no user-facing axes method currently generates +`.NonUniformImage`\s, and only `.pcolorfast` can generate `.PcolorImage`\s). +These artists are also now slower, normally by ~1.5x but sometimes more (in +particular for ``NonUniformImage(interpolation="bilinear")``. This slowdown +arises from fixing occasional floating point inaccuracies. + +Change of the (default) legend handler for ``Line2D`` instances +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The default legend handler for Line2D instances (`.HandlerLine2D`) now +consistently exposes all the attributes and methods related to the line marker +(:ghissue:`11358`). This makes it easy to change the marker features after +instantiating a legend. + +.. code:: + + import matplotlib.pyplot as plt + + fig, ax = plt.subplots() + + ax.plot([1, 3, 2], marker="s", label="Line", color="pink", mec="red", ms=8) + leg = ax.legend() + + leg.legendHandles[0].set_color("lightgray") + leg.legendHandles[0].set_mec("black") # marker edge color + +The former legend handler for Line2D objects has been renamed +`.HandlerLine2DCompound`. To revert to the previous behaviour, one can use + +.. code:: + + import matplotlib.legend as mlegend + from matplotlib.legend_handler import HandlerLine2DCompound + from matplotlib.lines import Line2D + + mlegend.Legend.update_default_handler_map({Line2D: HandlerLine2DCompound()}) + +Setting ``Line2D`` marker edge/face color to *None* use rcParams +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``Line2D.set_markeredgecolor(None)`` and ``Line2D.set_markerfacecolor(None)`` +now set the line property using the corresponding rcParam +(:rc:`lines.markeredgecolor` and :rc:`lines.markerfacecolor`). This is +consistent with other `.Line2D` property setters. + +Default theta tick locations for wedge polar plots have changed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For polar plots that don't cover a full circle, the default theta tick +locations are now at multiples of 10°, 15°, 30°, 45°, 90°, rather than using +values that mostly make sense for linear plots (20°, 25°, etc.). + +``axvspan`` now plots full wedges in polar plots +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... rather than triangles. + +Convenience converter from ``Scale`` to ``Normalize`` now public +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Downstream libraries can take advantage of `.colors.make_norm_from_scale` to +create a `~.colors.Normalize` subclass directly from an existing scale. +Usually norms have a scale, and the advantage of having a `~.scale.ScaleBase` +attached to a norm is to provide a scale, and associated tick locators and +formatters, for the colorbar. + +``ContourSet`` always use ``PathCollection`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In order to correct rendering issues with closed loops, the `.ContourSet` now +creates a `.PathCollection` instead of a `.LineCollection` for line contours. +This type matches the artist used for filled contours. + +This affects `.ContourSet` itself and its subclasses, `.QuadContourSet` +(returned by `.Axes.contour`), and `.TriContourSet` (returned by +`.Axes.tricontour`). + +``hatch.SmallFilledCircles`` inherits from ``hatch.Circles`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``hatch.SmallFilledCircles`` class now inherits from ``hatch.Circles`` +rather than from ``hatch.SmallCircles``. + +hexbin with a log norm +~~~~~~~~~~~~~~~~~~~~~~ + +`~.axes.Axes.hexbin` no longer (incorrectly) adds 1 to every bin value if a log +norm is being used. + +Setting invalid ``rcParams["date.converter"]`` now raises ValueError +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, invalid values passed to :rc:`date.converter` would be ignored with +a `UserWarning`, but now raise `ValueError`. + +``Text`` and ``TextBox`` added *parse_math* option +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`.Text` and `.TextBox` objects now allow a *parse_math* keyword-only argument +which controls whether math should be parsed from the displayed string. If +*True*, the string will be parsed as a math text object. If *False*, the string +will be considered a literal and no parsing will occur. + +For `.Text`, this argument defaults to *True*. For `.TextBox` this argument +defaults to *False*. + +``Type1Font`` objects now decrypt the encrypted part +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Type 1 fonts have a large part of their code encrypted as an obsolete +copy-protection measure. This part is now available decrypted as the +``decrypted`` attribute of `~.type1font.Type1Font`. This decrypted data is not +yet parsed, but this is a prerequisite for implementing subsetting. + +3D contourf polygons placed between levels +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The polygons used in a 3D `~mpl_toolkits.mplot3d.Axes3D.contourf` plot are now +placed halfway between the contour levels, as each polygon represents the +location of values that lie between two levels. + +``AxesDivider`` now defaults to rcParams-specified pads +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`.AxesDivider.append_axes`, `.AxesDivider.new_horizontal`, and +`.AxesDivider.new_vertical` now default to paddings specified by +:rc:`figure.subplot.wspace` and :rc:`figure.subplot.hspace` rather than zero. diff --git a/doc/api/prev_api_changes/api_changes_3.5.0/deprecations.rst b/doc/api/prev_api_changes/api_changes_3.5.0/deprecations.rst new file mode 100644 index 000000000000..3118c2bca0e6 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.5.0/deprecations.rst @@ -0,0 +1,375 @@ +Deprecations +------------ + +Discouraged: ``Figure`` parameters *tight_layout* and *constrained_layout* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``Figure`` parameters *tight_layout* and *constrained_layout* are +triggering competing layout mechanisms and thus should not be used together. + +To make the API clearer, we've merged them under the new parameter *layout* +with values 'constrained' (equal to ``constrained_layout=True``), 'tight' +(equal to ``tight_layout=True``). If given, *layout* takes precedence. + +The use of *tight_layout* and *constrained_layout* is discouraged in favor of +*layout*. However, these parameters will stay available for backward +compatibility. + +Modification of ``Axes`` children sublists +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +See :ref:`Behavioural API Changes 3.5 - Axes children combined` for more +information; modification of the following sublists is deprecated: + +* ``Axes.artists`` +* ``Axes.collections`` +* ``Axes.images`` +* ``Axes.lines`` +* ``Axes.patches`` +* ``Axes.tables`` +* ``Axes.texts`` + +To remove an Artist, use its `.Artist.remove` method. To add an Artist, use the +corresponding ``Axes.add_*`` method. + +Passing incorrect types to ``Axes.add_*`` methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following ``Axes.add_*`` methods will now warn if passed an unexpected +type. See their documentation for the types they expect. + +- `.Axes.add_collection` +- `.Axes.add_image` +- `.Axes.add_line` +- `.Axes.add_patch` +- `.Axes.add_table` + +Discouraged: ``plot_date`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The use of `~.Axes.plot_date` is discouraged. This method exists for historic +reasons and may be deprecated in the future. + +- ``datetime``-like data should directly be plotted using `~.Axes.plot`. +- If you need to plot plain numeric data as :ref:`date-format` or + need to set a timezone, call ``ax.xaxis.axis_date`` / ``ax.yaxis.axis_date`` + before `~.Axes.plot`. See `.Axis.axis_date`. + +``epoch2num`` and ``num2epoch`` are deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +These methods convert from unix timestamps to matplotlib floats, but are not +used internally to matplotlib, and should not be needed by end users. To +convert a unix timestamp to datetime, simply use +`datetime.datetime.utcfromtimestamp`, or to use NumPy `~numpy.datetime64` +``dt = np.datetim64(e*1e6, 'us')``. + +Auto-removal of grids by `~.Axes.pcolor` and `~.Axes.pcolormesh` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`~.Axes.pcolor` and `~.Axes.pcolormesh` currently remove any visible axes major +grid. This behavior is deprecated; please explicitly call ``ax.grid(False)`` to +remove the grid. + +The first parameter of ``Axes.grid`` and ``Axis.grid`` has been renamed to *visible* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The parameter was previously named *b*. This deprecation only matters if that +parameter was passed using a keyword argument, e.g. ``grid(b=False)``. + +Unification and cleanup of Selector widget API +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The API for Selector widgets has been unified to use: + +- *props* for the properties of the Artist representing the selection. +- *handle_props* for the Artists representing handles for modifying the + selection. +- *grab_range* for the maximal tolerance to grab a handle with the mouse. + +Additionally, several internal parameters and attribute have been deprecated +with the intention of keeping them private. + +RectangleSelector and EllipseSelector +..................................... + +The *drawtype* keyword argument to `~matplotlib.widgets.RectangleSelector` is +deprecated. In the future the only behaviour will be the default behaviour of +``drawtype='box'``. + +Support for ``drawtype=line`` will be removed altogether as it is not clear +which points are within and outside a selector that is just a line. As a +result, the *lineprops* keyword argument to +`~matplotlib.widgets.RectangleSelector` is also deprecated. + +To retain the behaviour of ``drawtype='none'``, use ``rectprops={'visible': +False}`` to make the drawn `~matplotlib.patches.Rectangle` invisible. + +Cleaned up attributes and arguments are: + +- The ``active_handle`` attribute has been privatized and deprecated. +- The ``drawtype`` attribute has been privatized and deprecated. +- The ``eventpress`` attribute has been privatized and deprecated. +- The ``eventrelease`` attribute has been privatized and deprecated. +- The ``interactive`` attribute has been privatized and deprecated. +- The *marker_props* argument is deprecated, use *handle_props* instead. +- The *maxdist* argument is deprecated, use *grab_range* instead. +- The *rectprops* argument is deprecated, use *props* instead. +- The ``rectprops`` attribute has been privatized and deprecated. +- The ``state`` attribute has been privatized and deprecated. +- The ``to_draw`` attribute has been privatized and deprecated. + +PolygonSelector +............... + +- The *line* attribute is deprecated. If you want to change the selector artist + properties, use the ``set_props`` or ``set_handle_props`` methods. +- The *lineprops* argument is deprecated, use *props* instead. +- The *markerprops* argument is deprecated, use *handle_props* instead. +- The *maxdist* argument and attribute is deprecated, use *grab_range* instead. +- The *vertex_select_radius* argument and attribute is deprecated, use + *grab_range* instead. + +SpanSelector +............ + +- The ``active_handle`` attribute has been privatized and deprecated. +- The ``eventpress`` attribute has been privatized and deprecated. +- The ``eventrelease`` attribute has been privatized and deprecated. +- The *maxdist* argument and attribute is deprecated, use *grab_range* instead. +- The ``pressv`` attribute has been privatized and deprecated. +- The ``prev`` attribute has been privatized and deprecated. +- The ``rect`` attribute has been privatized and deprecated. +- The *rectprops* argument is deprecated, use *props* instead. +- The ``rectprops`` attribute has been privatized and deprecated. +- The *span_stays* argument is deprecated, use the *interactive* argument + instead. +- The ``span_stays`` attribute has been privatized and deprecated. +- The ``state`` attribute has been privatized and deprecated. + +LassoSelector +............. + +- The *lineprops* argument is deprecated, use *props* instead. +- The ``onpress`` and ``onrelease`` methods are deprecated. They are straight + aliases for ``press`` and ``release``. + +``ConversionInterface.convert`` no longer needs to accept unitless values +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, custom subclasses of `.units.ConversionInterface` needed to +implement a ``convert`` method that not only accepted instances of the unit, +but also unitless values (which are passed through as is). This is no longer +the case (``convert`` is never called with a unitless value), and such support +in `.StrCategoryConverter` is deprecated. Likewise, the +`.ConversionInterface.is_numlike` helper is deprecated. + +Consider calling `.Axis.convert_units` instead, which still supports unitless +values. + +Locator and Formatter wrapper methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``set_view_interval``, ``set_data_interval`` and ``set_bounds`` methods of +`.Locator`\s and `.Formatter`\s (and their common base class, TickHelper) are +deprecated. Directly manipulate the view and data intervals on the underlying +axis instead. + +Unused positional parameters to ``print_`` methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +None of the ``print_`` methods implemented by canvas subclasses used +positional arguments other that the first (the output filename or file-like), +so these extra parameters are deprecated. + +``QuadMesh`` signature +~~~~~~~~~~~~~~~~~~~~~~ + +The `.QuadMesh` signature :: + + def __init__(meshWidth, meshHeight, coordinates, + antialiased=True, shading='flat', **kwargs) + +is deprecated and replaced by the new signature :: + + def __init__(coordinates, *, antialiased=True, shading='flat', **kwargs) + +In particular: + +- The *coordinates* argument must now be a (M, N, 2) array-like. Previously, + the grid shape was separately specified as (*meshHeight* + 1, *meshWidth* + + 1) and *coordinates* could be an array-like of any shape with M * N * 2 + elements. +- All parameters except *coordinates* are keyword-only now. + +rcParams will no longer cast inputs to str +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +After a deprecation period, rcParams that expect a (non-pathlike) str will no +longer cast non-str inputs using `str`. This will avoid confusing errors in +subsequent code if e.g. a list input gets implicitly cast to a str. + +Case-insensitive scales +~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, scales could be set case-insensitively (e.g., +``set_xscale("LoG")``). This is deprecated; all builtin scales use lowercase +names. + +Interactive cursor details +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Setting a mouse cursor on a window has been moved from the toolbar to the +canvas. Consequently, several implementation details on toolbars and within +backends have been deprecated. + +``NavigationToolbar2.set_cursor`` and ``backend_tools.SetCursorBase.set_cursor`` +................................................................................ + +Instead, use the `.FigureCanvasBase.set_cursor` method on the canvas (available +as the ``canvas`` attribute on the toolbar or the Figure.) + +``backend_tools.SetCursorBase`` and subclasses +.............................................. + +``backend_tools.SetCursorBase`` was subclassed to provide backend-specific +implementations of ``set_cursor``. As that is now deprecated, the subclassing +is no longer necessary. Consequently, the following subclasses are also +deprecated: + +- ``matplotlib.backends.backend_gtk3.SetCursorGTK3`` +- ``matplotlib.backends.backend_qt5.SetCursorQt`` +- ``matplotlib.backends._backend_tk.SetCursorTk`` +- ``matplotlib.backends.backend_wx.SetCursorWx`` + +Instead, use the `.backend_tools.ToolSetCursor` class. + +``cursord`` in GTK, Qt, and wx backends +....................................... + +The ``backend_gtk3.cursord``, ``backend_qt.cursord``, and +``backend_wx.cursord`` dictionaries are deprecated. This makes the GTK module +importable on headless environments. + +Miscellaneous deprecations +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- ``is_url`` and ``URL_REGEX`` are deprecated. (They were previously defined in + the toplevel :mod:`matplotlib` module.) +- The ``ArrowStyle.beginarrow`` and ``ArrowStyle.endarrow`` attributes are + deprecated; use the ``arrow`` attribute to define the desired heads and tails + of the arrow. +- ``backend_pgf.LatexManager.str_cache`` is deprecated. +- ``backends.qt_compat.ETS`` and ``backends.qt_compat.QT_RC_MAJOR_VERSION`` are + deprecated, with no replacement. +- The ``blocking_input`` module has been deprecated. Instead, use + ``canvas.start_event_loop()`` and ``canvas.stop_event_loop()`` while + connecting event callbacks as needed. +- ``cbook.report_memory`` is deprecated; use ``psutil.virtual_memory`` instead. +- ``cm.LUTSIZE`` is deprecated. Use :rc:`image.lut` instead. This value only + affects colormap quantization levels for default colormaps generated at + module import time. +- ``Colorbar.patch`` is deprecated; this attribute is not correctly updated + anymore. +- ``ContourLabeler.get_label_width`` is deprecated. +- ``dviread.PsfontsMap`` now raises LookupError instead of KeyError for missing + fonts. +- ``Dvi.baseline`` is deprecated (with no replacement). +- The *format* parameter of ``dviread.find_tex_file`` is deprecated (with no + replacement). +- ``FancyArrowPatch.get_path_in_displaycoord`` and + ``ConnectionPath.get_path_in_displaycoord`` are deprecated. The path in + display coordinates can still be obtained, as for other patches, using + ``patch.get_transform().transform_path(patch.get_path())``. +- The ``font_manager.win32InstalledFonts`` and + ``font_manager.get_fontconfig_fonts`` helper functions have been deprecated. +- All parameters of ``imshow`` starting from *aspect* will become keyword-only. +- ``QuadMesh.convert_mesh_to_paths`` and ``QuadMesh.convert_mesh_to_triangles`` + are deprecated. ``QuadMesh.get_paths()`` can be used as an alternative for + the former; there is no replacement for the latter. +- ``ScalarMappable.callbacksSM`` is deprecated. Use + ``ScalarMappable.callbacks`` instead. +- ``streamplot.get_integrator`` is deprecated. +- ``style.core.STYLE_FILE_PATTERN``, ``style.core.load_base_library``, and + ``style.core.iter_user_libraries`` are deprecated. +- ``SubplotParams.validate`` is deprecated. Use `.SubplotParams.update` to + change `.SubplotParams` while always keeping it in a valid state. +- The ``grey_arrayd``, ``font_family``, ``font_families``, and ``font_info`` + attributes of `.TexManager` are deprecated. +- ``Text.get_prop_tup`` is deprecated with no replacements (because the `.Text` + class cannot know whether a backend needs to update cache e.g. when the + text's color changes). +- ``Tick.apply_tickdir`` didn't actually update the tick markers on the + existing Line2D objects used to draw the ticks and is deprecated; use + `.Axis.set_tick_params` instead. +- ``tight_layout.auto_adjust_subplotpars`` is deprecated. + +- The ``grid_info`` attribute of ``axisartist`` classes has been deprecated. +- ``axisartist.clip_path`` is deprecated with no replacement. +- ``axes_grid1.axes_grid.CbarAxes`` and ``axes_grid1.axisartist.CbarAxes`` are + deprecated (they are now dynamically generated based on the owning axes + class). +- The ``axes_grid1.Divider.get_vsize_hsize`` and + ``axes_grid1.Grid.get_vsize_hsize`` methods are deprecated. Copy their + implementations if needed. +- ``AxesDivider.append_axes(..., add_to_figure=False)`` is deprecated. Use + ``ax.remove()`` to remove the Axes from the figure if needed. +- ``FixedAxisArtistHelper.change_tick_coord`` is deprecated with no + replacement. +- ``floating_axes.GridHelperCurveLinear.get_boundary`` is deprecated, with no + replacement. +- ``ParasiteAxesBase.get_images_artists`` has been deprecated. + +- The "units finalize" signal (previously emitted by Axis instances) is + deprecated. Connect to "units" instead. +- Passing formatting parameters positionally to ``stem()`` is deprecated + +``plot_directive`` deprecations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``:encoding:`` option to ``.. plot`` directive has had no effect since +Matplotlib 1.3.1, and is now deprecated. + +The following helpers in `matplotlib.sphinxext.plot_directive` are deprecated: + +- ``unescape_doctest`` (use `doctest.script_from_examples` instead), +- ``split_code_at_show``, +- ``run_code``. + +Testing support +~~~~~~~~~~~~~~~ + +``matplotlib.test()`` is deprecated +................................... + +Run tests using ``pytest`` from the commandline instead. The variable +``matplotlib.default_test_modules`` is only used for ``matplotlib.test()`` and +is thus deprecated as well. + +To test an installed copy, be sure to specify both ``matplotlib`` and +``mpl_toolkits`` with ``--pyargs``:: + + python -m pytest --pyargs matplotlib.tests mpl_toolkits.tests + +See :ref:`testing` for more details. + +Unused pytest fixtures and markers +.................................. + +The fixture ``matplotlib.testing.conftest.mpl_image_comparison_parameters`` is +not used internally by Matplotlib. If you use this please copy it into your +code base. + +The ``@pytest.mark.style`` marker is deprecated; use ``@mpl.style.context``, +which has the same effect. + +Support for ``nx1 = None`` or ``ny1 = None`` in ``AxesLocator`` and ``Divider.locate`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In `.axes_grid1.axes_divider`, various internal APIs will stop supporting +passing ``nx1 = None`` or ``ny1 = None`` to mean ``nx + 1`` or ``ny + 1``, in +preparation for a possible future API which allows indexing and slicing of +dividers (possibly ``divider[a:b] == divider.new_locator(a, b)``, but also +``divider[a:] == divider.new_locator(a, )``). The user-facing +`.Divider.new_locator` API is unaffected -- it correctly normalizes ``nx1 = +None`` and ``ny1 = None`` as needed. 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 new file mode 100644 index 000000000000..d86bc5e344a9 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.5.0/development.rst @@ -0,0 +1,48 @@ +Development changes +------------------- + +Increase to minimum supported versions of dependencies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For Matplotlib 3.5, the :ref:`minimum supported versions ` and +some :ref:`optional dependencies ` are being bumped: + ++---------------+---------------+---------------+ +| Dependency | min in mpl3.4 | min in mpl3.5 | ++===============+===============+===============+ +| NumPy | 1.16 | 1.17 | ++---------------+---------------+---------------+ +| Tk (optional) | 8.3 | 8.4 | ++---------------+---------------+---------------+ + +This is consistent with our :ref:`min_deps_policy` and `NEP29 +`__ + +New runtime dependencies +~~~~~~~~~~~~~~~~~~~~~~~~ + +fontTools for type 42 subsetting +................................ + +A new dependency `fontTools `_ is integrated +into Matplotlib 3.5. It is designed to be used with PS/EPS and PDF documents; +and handles Type 42 font subsetting. + +Underscore support in LaTeX +........................... + +The `underscore `_ package is now a +requirement to improve support for underscores in LaTeX. + +This is consistent with our :ref:`min_deps_policy`. + +Matplotlib-specific build options moved from ``setup.cfg`` to ``mplsetup.cfg`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In order to avoid conflicting with the use of :file:`setup.cfg` by +``setuptools``, the Matplotlib-specific build options have moved from +``setup.cfg`` to ``mplsetup.cfg`` + +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. diff --git a/doc/api/prev_api_changes/api_changes_3.5.0/removals.rst b/doc/api/prev_api_changes/api_changes_3.5.0/removals.rst new file mode 100644 index 000000000000..cc81bd702918 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.5.0/removals.rst @@ -0,0 +1,365 @@ +Removals +-------- + +The following deprecated APIs have been removed: + +Removed behaviour +~~~~~~~~~~~~~~~~~ + +Stricter validation of function parameters +.......................................... + +- Calling `.Figure.add_axes` with no arguments will raise an error. Adding a + free-floating axes needs a position rectangle. If you want a figure-filling + single axes, use `.Figure.add_subplot` instead. +- `.Figure.add_subplot` validates its inputs; in particular, for + ``add_subplot(rows, cols, index)``, all parameters must be integral. + Previously strings and floats were accepted and converted to int. +- Passing *None* as the *which* argument to ``autofmt_xdate`` is no longer + supported; use its more explicit synonym, ``which="major"``, instead. +- Setting the *orientation* of an ``eventplot()`` or `.EventCollection` to + "none" or *None* is no longer supported; set it to "horizontal" instead. + Moreover, the two orientations ("horizontal" and "vertical") are now + case-sensitive. +- Passing parameters *norm* and *vmin*/*vmax* simultaneously to functions using + colormapping such as ``scatter()`` and ``imshow()`` is no longer supported. + Instead of ``norm=LogNorm(), vmin=min_val, vmax=max_val`` pass + ``norm=LogNorm(min_val, max_val)``. *vmin* and *vmax* should only be used + without setting *norm*. +- Passing *None* as either the *radius* or *startangle* arguments of an + `.Axes.pie` is no longer accepted; use the explicit defaults of 1 and 0, + respectively, instead. +- Passing *None* as the *normalize* argument of `.Axes.pie` (the former + default) is no longer accepted, and the pie will always be normalized by + default. If you wish to plot an incomplete pie, explicitly pass + ``normalize=False``. +- Support for passing *None* to ``subplot_class_factory`` has been removed. + Explicitly pass in the base `~matplotlib.axes.Axes` class instead. +- Passing multiple keys as a single comma-separated string or multiple + arguments to `.ToolManager.update_keymap` is no longer supported; pass keys + as a list of strings instead. +- Passing the dash offset as *None* is no longer accepted, as this was never + universally implemented, e.g. for vector output. Set the offset to 0 instead. +- Setting a custom method overriding `.Artist.contains` using + ``Artist.set_contains`` has been removed, as has ``Artist.get_contains``. + There is no replacement, but you may still customize pick events using + `.Artist.set_picker`. +- `~.Axes.semilogx`, `~.Axes.semilogy`, `~.Axes.loglog`, `.LogScale`, and + `.SymmetricalLogScale` used to take keyword arguments that depends on the + axis orientation ("basex" vs "basey", "subsx" vs "subsy", "nonposx" vs + "nonposy"); these parameter names have been removed in favor of "base", + "subs", "nonpositive". This removal also affects e.g. ``ax.set_yscale("log", + basey=...)`` which must now be spelled ``ax.set_yscale("log", base=...)``. + + The change from "nonpos" to "nonpositive" also affects + `~.scale.LogTransform`, `~.scale.InvertedLogTransform`, + `~.scale.SymmetricalLogTransform`, etc. + + To use *different* bases for the x-axis and y-axis of a `~.Axes.loglog` plot, + use e.g. ``ax.set_xscale("log", base=10); ax.set_yscale("log", base=2)``. +- Passing *None*, or no argument, to ``parasite_axes_class_factory``, + ``parasite_axes_auxtrans_class_factory``, ``host_axes_class_factory`` is no + longer accepted; pass an explicit base class instead. + +Case-sensitivity is now enforced more +...................................... + +- Upper or mixed-case property names are no longer normalized to lowercase in + `.Artist.set` and `.Artist.update`. This allows one to pass names such as + *patchA* or *UVC*. +- Case-insensitive capstyles and joinstyles are no longer lower-cased; please + pass capstyles ("miter", "round", "bevel") and joinstyles ("butt", "round", + "projecting") as lowercase. +- Saving metadata in PDF with the PGF backend no longer changes keys to + lowercase. Only the canonically cased keys listed in the PDF specification + (and the `~.backend_pgf.PdfPages` documentation) are accepted. + +No implicit initialization of ``Tick`` attributes +................................................. + +The `.Tick` constructor no longer initializes the attributes ``tick1line``, +``tick2line``, ``gridline``, ``label1``, and ``label2`` via ``_get_tick1line``, +``_get_tick2line``, ``_get_gridline``, ``_get_text1``, and ``_get_text2``. +Please directly set the attribute in the subclass' ``__init__`` instead. + +``NavigationToolbar2`` subclass changes +....................................... + +Overriding the ``_init_toolbar`` method of `.NavigationToolbar2` to initialize +third-party toolbars is no longer supported. Instead, the toolbar should be +initialized in the ``__init__`` method of the subclass (which should call the +base-class' ``__init__`` as appropriate). + +The ``press`` and ``release`` methods of `.NavigationToolbar2` were called when +pressing or releasing a mouse button, but *only* when an interactive pan or +zoom was occurring (contrary to what the docs stated). They are no longer +called; if you write a backend which needs to customize such events, please +directly override ``press_pan``/``press_zoom``/``release_pan``/``release_zoom`` +instead. + +Removal of old file mode flag +............................. + +Flags containing "U" passed to `.cbook.to_filehandle` and `.cbook.open_file_cm` +are no longer accepted. This is consistent with their removal from `open` in +Python 3.9. + +Keymaps toggling ``Axes.get_navigate`` have been removed +........................................................ + +This includes numeric key events and rcParams. + +The ``TTFPATH`` and ``AFMPATH`` environment variables +..................................................... + +Support for the (undocumented) ``TTFPATH`` and ``AFMPATH`` environment +variables has been removed. Register additional fonts using +``matplotlib.font_manager.fontManager.addfont()``. + +Modules +~~~~~~~ + +- ``matplotlib.backends.qt_editor.formsubplottool``; use + ``matplotlib.backends.backend_qt.SubplotToolQt`` instead. +- ``matplotlib.compat`` +- ``matplotlib.ttconv`` +- The Qt4-based backends, ``qt4agg`` and ``qt4cairo``, have been removed. Qt4 + has reached its end-of-life in 2015 and there are no releases of either PyQt4 + or PySide for recent versions of Python. Please use one of the Qt5 or Qt6 + backends. + +Classes, methods and attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following module-level classes/variables have been removed: + +- ``backend_bases.StatusbarBase`` and all its subclasses, and ``StatusBarWx``; + messages are displayed in the toolbar +- ``backend_pgf.GraphicsContextPgf`` +- ``MODIFIER_KEYS``, ``SUPER``, ``ALT``, ``CTRL``, and ``SHIFT`` of + `matplotlib.backends.backend_qt5agg` and + `matplotlib.backends.backend_qt5cairo` +- ``backend_wx.DEBUG_MSG`` +- ``dviread.Encoding`` +- ``Fil``, ``Fill``, ``Filll``, ``NegFil``, ``NegFill``, ``NegFilll``, and + ``SsGlue`` from `.mathtext`; directly construct glue instances with + ``Glue("fil")``, etc. +- ``mathtext.GlueSpec`` +- ``OldScalarFormatter``, ``IndexFormatter`` and ``IndexDateFormatter``; use + `.FuncFormatter` instead +- ``OldAutoLocator`` +- ``AVConvBase``, ``AVConvWriter`` and ``AVConvFileWriter``. Debian 8 (2015, + EOL 06/2020) and Ubuntu 14.04 (EOL 04/2019) were the last versions of Debian + and Ubuntu to ship avconv. It remains possible to force the use of avconv by + using the FFmpeg-based writers with :rc:`animation.ffmpeg_path` set to + "avconv". +- ``matplotlib.axes._subplots._subplot_classes`` +- ``axes_grid1.axes_rgb.RGBAxesBase``; use ``RGBAxes`` instead + +The following class attributes have been removed: + +- ``backend_pgf.LatexManager.latex_stdin_utf8`` +- ``backend_pgf.PdfPages.metadata`` +- ``ContourSet.ax`` and ``Quiver.ax``; use ``ContourSet.axes`` or + ``Quiver.axes`` as with other artists +- ``DateFormatter.illegal_s`` +- ``dates.YearLocator.replaced``; `.YearLocator` is now a subclass of + `.RRuleLocator`, and the attribute ``YearLocator.replaced`` has been removed. + For tick locations that required modifying this, a custom rrule and + `.RRuleLocator` can be used instead. +- ``FigureManagerBase.statusbar``; messages are displayed in the toolbar +- ``FileMovieWriter.clear_temp`` +- ``mathtext.Glue.glue_subtype`` +- ``MovieWriter.args_key``, ``MovieWriter.exec_key``, and + ``HTMLWriter.args_key`` +- ``NavigationToolbar2QT.basedir``; the base directory to the icons is + ``os.path.join(mpl.get_data_path(), "images")`` +- ``NavigationToolbar2QT.ctx`` +- ``NavigationToolbar2QT.parent``; to access the parent window, use + ``toolbar.canvas.parent()`` or ``toolbar.parent()`` +- ``prevZoomRect``, ``retinaFix``, ``savedRetinaImage``, ``wxoverlay``, + ``zoomAxes``, ``zoomStartX``, and ``zoomStartY`` attributes of + ``NavigationToolbar2Wx`` +- ``NonUniformImage.is_grayscale``, ``PcolorImage.is_grayscale``, for + consistency with ``AxesImage.is_grayscale``. (Note that previously, these + attributes were only available *after rendering the image*). +- ``RendererCairo.fontweights``, ``RendererCairo.fontangles`` +- ``used_characters`` of `.RendererPdf`, `.PdfFile`, and `.RendererPS` +- ``LogScale.LogTransform``, ``LogScale.InvertedLogTransform``, + ``SymmetricalScale.SymmetricalTransform``, and + ``SymmetricalScale.InvertedSymmetricalTransform``; directly access the + transform classes from `matplotlib.scale` +- ``cachedir``, ``rgba_arrayd``, ``serif``, ``sans_serif``, ``cursive``, and + ``monospace`` attributes of `.TexManager` +- ``axleft``, ``axright``, ``axbottom``, ``axtop``, ``axwspace``, and + ``axhspace`` attributes of `.widgets.SubplotTool`; access the ``ax`` + attribute of the corresponding slider +- ``widgets.TextBox.params_to_disable`` +- ``angle_helper.LocatorBase.den``; it has been renamed to *nbins* +- ``axes_grid.CbarAxesBase.cbid`` and ``axes_grid.CbarAxesBase.locator``; use + ``mappable.colorbar_cid`` or ``colorbar.locator`` instead + +The following class methods have been removed: + +- ``Axes.update_datalim_bounds``; use ``ax.dataLim.set(Bbox.union([ax.dataLim, + bounds]))`` +- ``pan`` and ``zoom`` methods of `~.axis.Axis` and `~.ticker.Locator` have + been removed; panning and zooming are now implemented using the + ``start_pan``, ``drag_pan``, and ``end_pan`` methods of `~.axes.Axes` +- ``.BboxBase.inverse_transformed``; call `.BboxBase.transformed` on the + `~.Transform.inverted()` transform +- ``Collection.set_offset_position`` and ``Collection.get_offset_position`` + have been removed; the ``offset_position`` of the `.Collection` class is now + "screen" +- ``Colorbar.on_mappable_changed`` and ``Colorbar.update_bruteforce``; use + ``Colorbar.update_normal()`` instead +- ``docstring.Substitution.from_params`` has been removed; directly assign to + ``params`` of `.Substitution` instead +- ``DraggableBase.artist_picker``; set the artist's picker instead +- ``DraggableBase.on_motion_blit``; use `.DraggableBase.on_motion` instead +- ``FigureCanvasGTK3._renderer_init`` +- ``Locator.refresh()`` and the associated helper methods + ``NavigationToolbar2.draw()`` and ``ToolViewsPositions.refresh_locators()`` +- ``track_characters`` and ``merge_used_characters`` of `.RendererPdf`, + `.PdfFile`, and `.RendererPS` +- ``RendererWx.get_gc`` +- ``SubplotSpec.get_rows_columns``; use the ``GridSpec.nrows``, + ``GridSpec.ncols``, ``SubplotSpec.rowspan``, and ``SubplotSpec.colspan`` + properties instead. +- ``ScalarMappable.update_dict``, ``ScalarMappable.add_checker()``, and + ``ScalarMappable.check_update()``; register a callback in + ``ScalarMappable.callbacks`` to be notified of updates +- ``TexManager.make_tex_preview`` and ``TexManager.make_dvi_preview`` +- ``funcleft``, ``funcright``, ``funcbottom``, ``functop``, ``funcwspace``, and + ``funchspace`` methods of `.widgets.SubplotTool` + +- ``axes_grid1.axes_rgb.RGBAxes.add_RGB_to_figure`` +- ``axisartist.axis_artist.AxisArtist.dpi_transform`` +- ``axisartist.grid_finder.MaxNLocator.set_factor`` and + ``axisartist.grid_finder.FixedLocator.set_factor``; the factor is always 1 + now + +Functions +~~~~~~~~~ + +- ``bezier.make_path_regular`` has been removed; use ``Path.cleaned()`` (or + ``Path.cleaned(curves=True)``, etc.) instead, but note that these methods add + a ``STOP`` code at the end of the path. +- ``bezier.concatenate_paths`` has been removed; use + ``Path.make_compound_path()`` instead. +- ``cbook.local_over_kwdict`` has been removed; use `.cbook.normalize_kwargs` + instead. +- ``qt_compat.is_pyqt5`` has been removed due to the release of PyQt6. The Qt + version can be checked using ``QtCore.qVersion()``. +- ``testing.compare.make_external_conversion_command`` has been removed. +- ``axes_grid1.axes_rgb.imshow_rgb`` has been removed; use + ``imshow(np.dstack([r, g, b]))`` instead. + +Arguments +~~~~~~~~~ + +- The *s* parameter to `.Axes.annotate` and `.pyplot.annotate` is no longer + supported; use the new name *text*. +- The *inframe* parameter to `.Axes.draw` has been removed; use + `.Axes.redraw_in_frame` instead. +- The *required*, *forbidden* and *allowed* parameters of + `.cbook.normalize_kwargs` have been removed. +- The *ismath* parameter of the ``draw_tex`` method of all renderer classes has + been removed (as a call to ``draw_tex`` — not to be confused with + ``draw_text``! — means that the entire string should be passed to the + ``usetex`` machinery anyways). Likewise, the text machinery will no longer + pass the *ismath* parameter when calling ``draw_tex`` (this should only + matter for backend implementers). +- The *quality*, *optimize*, and *progressive* parameters of `.Figure.savefig` + (which only affected JPEG output) have been removed, as well as from the + corresponding ``print_jpg`` methods. JPEG output options can be set by + directly passing the relevant parameters in *pil_kwargs*. +- The *clear_temp* parameter of `.FileMovieWriter` has been removed; files + placed in a temporary directory (using ``frame_prefix=None``, the default) + will be cleared; files placed elsewhere will not. +- The *copy* parameter of ``mathtext.Glue`` has been removed. +- The *quantize* parameter of `.Path.cleaned()` has been removed. +- The *dummy* parameter of `.RendererPgf` has been removed. +- The *props* parameter of `.Shadow` has been removed; use keyword arguments + instead. +- The *recursionlimit* parameter of `matplotlib.test` has been removed. +- The *label* parameter of `.Tick` has no effect and has been removed. +- `~.ticker.MaxNLocator` no longer accepts a positional parameter and the + keyword argument *nbins* simultaneously because they specify the same + quantity. +- The *add_all* parameter to ``axes_grid.Grid``, ``axes_grid.ImageGrid``, + ``axes_rgb.make_rgb_axes``, and ``axes_rgb.RGBAxes`` have been removed; the + APIs always behave as if ``add_all=True``. +- The *den* parameter of ``axisartist.angle_helper.LocatorBase`` has been + removed; use *nbins* instead. + +- The *s* keyword argument to `.AnnotationBbox.get_fontsize` has no effect and + has been removed. +- The *offset_position* keyword argument of the `.Collection` class has been + removed; the ``offset_position`` now "screen". +- Arbitrary keyword arguments to ``StreamplotSet`` have no effect and have been + removed. + +- The *fontdict* and *minor* parameters of `.Axes.set_xticklabels` / + `.Axes.set_yticklabels` are now keyword-only. +- All parameters of `.Figure.subplots` except *nrows* and *ncols* are now + keyword-only; this avoids typing e.g. ``subplots(1, 1, 1)`` when meaning + ``subplot(1, 1, 1)``, but actually getting ``subplots(1, 1, sharex=1)``. +- All parameters of `.pyplot.tight_layout` are now keyword-only, to be + consistent with `.Figure.tight_layout`. +- ``ColorbarBase`` only takes a single positional argument now, the ``Axes`` to + create it in, with all other options required to be keyword arguments. The + warning for keyword arguments that were overridden by the mappable is now + removed. + +- Omitting the *renderer* parameter to `.Axes.draw` is no longer supported; use + ``axes.draw_artist(axes)`` instead. +- Passing ``ismath="TeX!"`` to `.RendererAgg.get_text_width_height_descent` is + no longer supported; pass ``ismath="TeX"`` instead, +- Changes to the signature of the `.Axes.draw` method make it consistent with + all other artists; thus additional parameters to `.Artist.draw` have also + been removed. + +rcParams +~~~~~~~~ + +- The ``animation.avconv_path`` and ``animation.avconv_args`` rcParams have + been removed. +- The ``animation.html_args`` rcParam has been removed. +- The ``keymap.all_axes`` rcParam has been removed. +- The ``mathtext.fallback_to_cm`` rcParam has been removed. Use + :rc:`mathtext.fallback` instead. +- The ``savefig.jpeg_quality`` rcParam has been removed. +- The ``text.latex.preview`` rcParam has been removed. +- The following deprecated rcParams validators, defined in `.rcsetup`, have + been removed: + + - ``validate_alignment`` + - ``validate_axes_titlelocation`` + - ``validate_axis_locator`` + - ``validate_bool_maybe_none`` + - ``validate_fontset`` + - ``validate_grid_axis`` + - ``validate_hinting`` + - ``validate_legend_loc`` + - ``validate_mathtext_default`` + - ``validate_movie_frame_fmt`` + - ``validate_movie_html_fmt`` + - ``validate_movie_writer`` + - ``validate_nseq_float`` + - ``validate_nseq_int`` + - ``validate_orientation`` + - ``validate_pgf_texsystem`` + - ``validate_ps_papersize`` + - ``validate_svg_fontset`` + - ``validate_toolbar`` + - ``validate_webagg_address`` + +- Some rcParam validation has become stricter: + + - :rc:`axes.axisbelow` no longer accepts strings starting with "line" + (case-insensitive) as "line"; use "line" (case-sensitive) instead. + - :rc:`text.latex.preamble` and :rc:`pdf.preamble` no longer accept + non-string values. + - All ``*.linestyle`` rcParams no longer accept ``offset = None``; set the + offset to 0 instead. diff --git a/doc/conf.py b/doc/conf.py index 8c010bd71cba..d8e554269e62 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -1,10 +1,12 @@ # Matplotlib documentation build configuration file, created by # sphinx-quickstart on Fri May 2 12:33:25 2008. # -# This file is execfile()d with the current directory set to its containing dir. +# This file is execfile()d with the current directory set to its containing +# dir. # # The contents of this file are pickled, so don't put values in the namespace -# that aren't pickleable (module imports are okay, they're removed automatically). +# that aren't pickleable (module imports are okay, they're removed +# automatically). # # All configuration values have a default value; values that are commented out # serve to show the default value. @@ -22,12 +24,16 @@ from datetime import datetime import time +# Release mode enables optimizations and other related options. +is_release_build = tags.has('release') # noqa + # are we running circle CI? CIRCLECI = 'CIRCLECI' in os.environ # Parse year using SOURCE_DATE_EPOCH, falling back to current time. # https://reproducible-builds.org/specs/source-date-epoch/ -sourceyear = datetime.utcfromtimestamp(int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))).year +sourceyear = datetime.utcfromtimestamp( + int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))).year # If your extensions are in another directory, add it here. If the directory # is relative to the documentation root, use os.path.abspath to make it @@ -88,6 +94,7 @@ def _check_dependencies(): "matplotlib": 'matplotlib', "numpydoc": 'numpydoc', "PIL.Image": 'pillow', + "pydata_sphinx_theme": 'pydata_sphinx_theme', "sphinx_copybutton": 'sphinx_copybutton', "sphinx_gallery": 'sphinx_gallery', "sphinxcontrib.inkscapeconverter": 'sphinxcontrib-svg2pdfconverter', @@ -175,14 +182,13 @@ def _check_dependencies(): 'remove_config_comments': True, 'min_reported_time': 1, 'thumbnail_size': (320, 224), - 'compress_images': () if CIRCLECI else ('thumbnails', 'images'), + # Compression is a significant effort that we skip for local and CI builds. + 'compress_images': ('thumbnails', 'images') if is_release_build else (), 'matplotlib_animations': True, - # 3.7 CI doc build should not use hidpi images during the testing phase - 'image_srcset': [] if sys.version_info[:2] == (3, 7) else ["2x"], + 'image_srcset': ["2x"], 'junit': '../test-results/sphinx-gallery/junit.xml' if CIRCLECI else '', } -plot_gallery = 'True' mathmpl_fontsize = 11.0 mathmpl_srcset = ['2x'] @@ -222,9 +228,11 @@ def _check_dependencies(): } project = 'Matplotlib' -copyright = ('2002 - 2012 John Hunter, Darren Dale, Eric Firing, ' - 'Michael Droettboom and the Matplotlib development ' - f'team; 2012 - {sourceyear} The Matplotlib development team') +copyright = ( + '2002 - 2012 John Hunter, Darren Dale, Eric Firing, Michael Droettboom ' + 'and the Matplotlib development team; ' + f'2012 - {sourceyear} The Matplotlib development team' +) # The default replacements for |version| and |release|, also used in various @@ -293,7 +301,9 @@ def _check_dependencies(): html_logo = "_static/logo2.svg" html_theme_options = { "logo_link": "index", - "collapse_navigation": True if CIRCLECI else False, + # collapse_navigation in pydata-sphinx-theme is slow, so skipped for local + # and CI builds https://github.com/pydata/pydata-sphinx-theme/pull/386 + "collapse_navigation": not is_release_build, "icon_links": [ { "name": "gitter", @@ -319,7 +329,7 @@ def _check_dependencies(): "show_prev_next": False, "navbar_center": ["mpl_nav_bar.html"], } -include_analytics = False +include_analytics = is_release_build if include_analytics: html_theme_options["google_analytics_id"] = "UA-55954603-1" @@ -383,8 +393,10 @@ def _check_dependencies(): # The paper size ('letter' or 'a4'). latex_paper_size = 'letter' -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, document class [howto/manual]). +# Grouping the document tree into LaTeX files. +# List of tuples: +# (source start file, target name, title, author, +# document class [howto/manual]) latex_documents = [ ('contents', 'Matplotlib.tex', 'Matplotlib', @@ -538,6 +550,20 @@ def _check_dependencies(): # graphviz_output_format = 'svg' +def reduce_plot_formats(app): + # Fox CI and local builds, we don't need all the default plot formats, so + # only generate the directly useful one for the current builder. + if app.builder.name == 'html': + keep = 'png' + elif app.builder.name == 'latex': + keep = 'pdf' + else: + return + app.config.plot_formats = [entry + for entry in app.config.plot_formats + if entry[0] == keep] + + def setup(app): if any(st in version for st in ('post', 'alpha', 'beta')): bld_type = 'dev' @@ -545,6 +571,9 @@ def setup(app): bld_type = 'rel' app.add_config_value('releaselevel', bld_type, 'env') + if not is_release_build: + app.connect('builder-inited', reduce_plot_formats) + # ----------------------------------------------------------------------------- # Source code links # ----------------------------------------------------------------------------- @@ -552,8 +581,8 @@ def setup(app): # You can add build old with link_github = False if link_github: - import re import inspect + from packaging.version import parse extensions.append('sphinx.ext.linkcode') @@ -595,10 +624,8 @@ def linkcode_resolve(domain, info): except (OSError, TypeError): lineno = None - if lineno: - linespec = "#L%d-L%d" % (lineno, lineno + len(source) - 1) - else: - linespec = "" + linespec = (f"#L{lineno:d}-L{lineno + len(source) - 1:d}" + if lineno else "") startdir = Path(matplotlib.__file__).parent.parent fn = os.path.relpath(fn, start=startdir).replace(os.path.sep, '/') @@ -606,12 +633,9 @@ def linkcode_resolve(domain, info): if not fn.startswith(('matplotlib/', 'mpl_toolkits/')): return None - m = re.match(r'^.*post[0-9]+\+\w([a-z0-9]+).\w+$', matplotlib.__version__) - if m: - return "https://github.com/matplotlib/matplotlib/blob/%s/lib/%s%s" % ( - m.group(1), fn, linespec) - else: - return "https://github.com/matplotlib/matplotlib/blob/v%s/lib/%s%s" % ( - matplotlib.__version__, fn, linespec) + version = parse(matplotlib.__version__) + tag = 'master' if version.is_devrelease else f'v{version.public}' + return ("https://github.com/matplotlib/matplotlib/blob" + f"/{tag}/lib/{fn}{linespec}") else: extensions.append('sphinx.ext.viewcode') diff --git a/doc/devel/coding_guide.rst b/doc/devel/coding_guide.rst index 8ac786e7c031..68d8f9cf7d1f 100644 --- a/doc/devel/coding_guide.rst +++ b/doc/devel/coding_guide.rst @@ -45,10 +45,12 @@ When making a PR, pay attention to: on GitHub. * When updating your PR, instead of adding new commits to fix something, please consider amending your initial commit(s) to keep the history clean. - You can achieve this using:: + You can achieve this using - git commit --amend --no-edit - git push [your-remote-repo] [your-branch] --force-with-lease + .. code-block:: bash + + git commit --amend --no-edit + git push [your-remote-repo] [your-branch] --force-with-lease See also :ref:`contributing` for how to make a PR. @@ -352,22 +354,26 @@ the merge notification) or through the git CLI tools. Assuming that you already have a local branch ``v2.2.x`` (if not, then ``git checkout -b v2.2.x``), and that your remote pointing to -``https://github.com/matplotlib/matplotlib`` is called ``upstream``:: +``https://github.com/matplotlib/matplotlib`` is called ``upstream``: + +.. code-block:: bash - git fetch upstream - git checkout v2.2.x # or include -b if you don't already have this. - git reset --hard upstream/v2.2.x - git cherry-pick -m 1 TARGET_SHA - # resolve conflicts and commit if required + git fetch upstream + git checkout v2.2.x # or include -b if you don't already have this. + git reset --hard upstream/v2.2.x + git cherry-pick -m 1 TARGET_SHA + # resolve conflicts and commit if required Files with conflicts can be listed by ``git status``, and will have to be fixed by hand (search on ``>>>>>``). Once the conflict is resolved, you will have to re-add the file(s) to the branch -and then continue the cherry pick:: +and then continue the cherry pick: + +.. code-block:: bash - git add lib/matplotlib/conflicted_file.py - git add lib/matplotlib/conflicted_file2.py - git cherry-pick --continue + git add lib/matplotlib/conflicted_file.py + git add lib/matplotlib/conflicted_file2.py + git cherry-pick --continue Use your discretion to push directly to upstream or to open a PR; be sure to push or PR against the ``v2.2.x`` upstream branch, not ``master``! diff --git a/doc/devel/contributing.rst b/doc/devel/contributing.rst index 943110385f03..8e67e4393dba 100644 --- a/doc/devel/contributing.rst +++ b/doc/devel/contributing.rst @@ -40,6 +40,16 @@ difficulty. ``Difficulty: Easy`` is suited for people with little Python experie ``Difficulty: Medium`` and ``Difficulty: Hard`` are not trivial to solve and require more thought and programming experience. +In general, the Matplotlib project does not assign issues. Issues are +"assigned" or "claimed" by opening a PR; there is no other assignment +mechanism. If you have opened such a PR, please comment on the issue thread to +avoid duplication of work. Please check if there is an existing PR for the +issue you are addressing. If there is, try to work with the author by +submitting reviews of their code or commenting on the PR rather than opening +a new PR; duplicate PRs are subject to being closed. However, if the existing +PR is an outline, unlikely to work, or stalled, and the original author is +unresponsive, feel free to open a new PR referencing the old one. + .. _submitting-a-bug-report: Submitting a bug report @@ -73,7 +83,7 @@ If you are reporting a bug, please do your best to include the following: >>> platform.python_version() '3.9.2' -We have preloaded the issue creation page with a Markdown template that you can +We have preloaded the issue creation page with a Markdown form that you can use to organize this information. Thank you for your help in keeping bug reports complete, targeted and descriptive. @@ -440,11 +450,13 @@ or manually with :: logging.basicConfig(level=logging.DEBUG) import matplotlib.pyplot as plt -Then they will receive messages like:: +Then they will receive messages like + +.. code-block:: none - DEBUG:matplotlib.backends:backend MacOSX version unknown - DEBUG:matplotlib.yourmodulename:Here is some information - DEBUG:matplotlib.yourmodulename:Here is some more detailed information + DEBUG:matplotlib.backends:backend MacOSX version unknown + DEBUG:matplotlib.yourmodulename:Here is some information + DEBUG:matplotlib.yourmodulename:Here is some more detailed information Which logging level to use? ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -489,12 +501,10 @@ Matplotlib. For example, for the module:: if bottom == top: warnings.warn('Attempting to set identical bottom==top') - running the script:: from matplotlib import my_matplotlib_module - my_matplotlib_module.set_range(0, 0) #set range - + my_matplotlib_module.set_range(0, 0) # set range will display:: @@ -509,10 +519,12 @@ Modifying the module to use `._api.warn_external`:: if bottom == top: _api.warn_external('Attempting to set identical bottom==top') -and running the same script will display:: +and running the same script will display + +.. code-block:: none - UserWarning: Attempting to set identical bottom==top - my_matplotlib_module.set_range(0, 0) #set range + UserWarning: Attempting to set identical bottom==top + my_matplotlib_module.set_range(0, 0) # set range .. _logging tutorial: https://docs.python.org/3/howto/logging.html#logging-basic-tutorial diff --git a/doc/devel/release_guide.rst b/doc/devel/release_guide.rst index 612ee423590e..423b2ba51b2c 100644 --- a/doc/devel/release_guide.rst +++ b/doc/devel/release_guide.rst @@ -345,7 +345,7 @@ build the docs from the ``ver-doc`` branch. An easy way to arrange this is:: pip install -r requirements/doc/doc-requirements.txt git checkout v2.0.0-doc git clean -xfd - make -Cdoc O="-Ainclude_analytics=True -j$(nproc)" html latexpdf LATEXMKOPTS="-silent -f" + make -Cdoc O="-t release -j$(nproc)" html latexpdf LATEXMKOPTS="-silent -f" which will build both the html and pdf version of the documentation. diff --git a/doc/faq/environment_variables_faq.rst b/doc/faq/environment_variables_faq.rst index 9af0420c835a..dc26fa9a53f7 100644 --- a/doc/faq/environment_variables_faq.rst +++ b/doc/faq/environment_variables_faq.rst @@ -80,7 +80,7 @@ searched first or last? To append to an existing environment variable:: setenv PATH ${PATH}:~/bin # csh/tcsh To make your changes available in the future, add the commands to your -:file:`~/.bashrc`/:file:`.cshrc` file. +:file:`~/.bashrc` or :file:`~/.cshrc` file. .. _setting-windows-environment-variables: diff --git a/doc/faq/howto_faq.rst b/doc/faq/howto_faq.rst index 13d5412c985b..533e7d2b3141 100644 --- a/doc/faq/howto_faq.rst +++ b/doc/faq/howto_faq.rst @@ -110,33 +110,10 @@ on individual elements, e.g.:: Save multiple plots to one pdf file ----------------------------------- -Many image file formats can only have one image per file, but some -formats support multi-page files. Currently only the pdf backend has -support for this. To make a multi-page pdf file, first initialize the -file:: - - from matplotlib.backends.backend_pdf import PdfPages - pp = PdfPages('multipage.pdf') - -You can give the :class:`~matplotlib.backends.backend_pdf.PdfPages` -object to :func:`~matplotlib.pyplot.savefig`, but you have to specify -the format:: - - plt.savefig(pp, format='pdf') - -An easier way is to call -:meth:`PdfPages.savefig `:: - - pp.savefig() - -Finally, the multipage pdf object has to be closed:: - - pp.close() - -The same can be done using the pgf backend:: - - from matplotlib.backends.backend_pgf import PdfPages - +Many image file formats can only have one image per file, but some formats +support multi-page files. Currently, Matplotlib only provides multi-page +output to pdf files, using either the pdf or pgf backends, via the +`.backend_pdf.PdfPages` and `.backend_pgf.PdfPages` classes. .. _howto-auto-adjust: diff --git a/doc/faq/index.rst b/doc/faq/index.rst index bb477de21f0e..24e9112465d3 100644 --- a/doc/faq/index.rst +++ b/doc/faq/index.rst @@ -9,7 +9,7 @@ How-to :Release: |version| :Date: |today| - Frequently asked questions about matplotlib + Frequently asked questions about Matplotlib: .. toctree:: :maxdepth: 2 diff --git a/doc/faq/troubleshooting_faq.rst b/doc/faq/troubleshooting_faq.rst index ba5a7735729c..cf9e3b5cf8b9 100644 --- a/doc/faq/troubleshooting_faq.rst +++ b/doc/faq/troubleshooting_faq.rst @@ -46,10 +46,10 @@ locate your :file:`matplotlib/` configuration directory, use >>> mpl.get_configdir() '/home/darren/.config/matplotlib' -On unix-like systems, this directory is generally located in your +On Unix-like systems, this directory is generally located in your :envvar:`HOME` directory under the :file:`.config/` directory. -In addition, users have a cache directory. On unix-like systems, this is +In addition, users have a cache directory. On Unix-like systems, this is separate from the configuration directory by default. To locate your :file:`.cache/` directory, use :func:`matplotlib.get_cachedir`:: @@ -57,7 +57,7 @@ separate from the configuration directory by default. To locate your >>> mpl.get_cachedir() '/home/darren/.cache/matplotlib' -On windows, both the config directory and the cache directory are +On Windows, both the config directory and the cache directory are the same and are in your :file:`Documents and Settings` or :file:`Users` directory by default:: @@ -93,7 +93,7 @@ If you are unable to find an answer to your question through search, please provide the following information in your e-mail to the `mailing list `_: -* Your operating system (Linux/UNIX users: post the output of ``uname -a``). +* Your operating system (Linux/Unix users: post the output of ``uname -a``). * Matplotlib version:: diff --git a/doc/index.rst b/doc/index.rst index 42bb4b2a8f5d..7b88cd7374d6 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -78,7 +78,7 @@ Learning resources ^^^^^^^^^ - :doc:`API Reference ` - - :doc:`Axes API ` for *most* plotting methods + - :doc:`Axes API ` for most plotting methods - :doc:`Figure API ` for figure-level methods - Top-level interfaces to create: @@ -98,7 +98,7 @@ Matplotlib. Contributing ------------ -Matplotlib is a community project maitained for and by its users. There are many ways +Matplotlib is a community project maintained for and by its users. There are many ways you can help! - Help other users `on discourse `__ diff --git a/doc/sphinxext/gallery_order.py b/doc/sphinxext/gallery_order.py index 8705d473c81e..589fed453f06 100644 --- a/doc/sphinxext/gallery_order.py +++ b/doc/sphinxext/gallery_order.py @@ -68,12 +68,13 @@ def __call__(self, item): # **Plot Types # Basic - "plot", "scatter_plot", "bar", "stem", "step", "pie", "fill_between", + "plot", "scatter_plot", "bar", "stem", "step", "fill_between", # Arrays - "imshow", "pcolormesh", "contour", "contourf", "quiver", "streamplot", + "imshow", "pcolormesh", "contour", "contourf", + "barbs", "quiver", "streamplot", # Stats "hist_plot", "boxplot_plot", "errorbar_plot", "violin", - "barbs", "eventplot", "hist2d", "hexbin", + "eventplot", "hist2d", "hexbin", "pie", # Unstructured "tricontour", "tricontourf", "tripcolor", "triplot", ] diff --git a/doc/users/backends.rst b/doc/users/backends.rst new file mode 100644 index 000000000000..24fe007173f1 --- /dev/null +++ b/doc/users/backends.rst @@ -0,0 +1,250 @@ +.. _backends: + +======== +Backends +======== + +.. _what-is-a-backend: + +What is a backend? +------------------ + +A lot of documentation on the website and in the mailing lists refers +to the "backend" and many new users are confused by this term. +Matplotlib targets many different use cases and output formats. Some +people use Matplotlib interactively from the Python shell and have +plotting windows pop up when they type commands. Some people run +`Jupyter `_ notebooks and draw inline plots for +quick data analysis. Others embed Matplotlib into graphical user +interfaces like PyQt or PyGObject to build rich applications. Some +people use Matplotlib in batch scripts to generate postscript images +from numerical simulations, and still others run web application +servers to dynamically serve up graphs. + +To support all of these use cases, Matplotlib can target different +outputs, and each of these capabilities is called a backend; the +"frontend" is the user facing code, i.e., the plotting code, whereas the +"backend" does all the hard work behind-the-scenes to make the figure. +There are two types of backends: user interface backends (for use in +PyQt/PySide, PyGObject, Tkinter, wxPython, or macOS/Cocoa); also referred to +as "interactive backends") and hardcopy backends to make image files +(PNG, SVG, PDF, PS; also referred to as "non-interactive backends"). + +Selecting a backend +------------------- + +There are three ways to configure your backend: + +- The :rc:`backend` parameter in your :file:`matplotlibrc` file +- The :envvar:`MPLBACKEND` environment variable +- The function :func:`matplotlib.use` + +Below is a more detailed description. + +If there is more than one configuration present, the last one from the +list takes precedence; e.g. calling :func:`matplotlib.use()` will override +the setting in your :file:`matplotlibrc`. + +Without a backend explicitly set, Matplotlib automatically detects a usable +backend based on what is available on your system and on whether a GUI event +loop is already running. The first usable backend in the following list is +selected: MacOSX, QtAgg, GTK4Agg, Gtk3Agg, TkAgg, WxAgg, Agg. The last, Agg, +is a non-interactive backend that can only write to files. It is used on +Linux, if Matplotlib cannot connect to either an X display or a Wayland +display. + +Here is a detailed description of the configuration methods: + +#. Setting :rc:`backend` in your :file:`matplotlibrc` file:: + + backend : qtagg # use pyqt with antigrain (agg) rendering + + See also :doc:`/tutorials/introductory/customizing`. + +#. Setting the :envvar:`MPLBACKEND` environment variable: + + You can set the environment variable either for your current shell or for + a single script. + + On Unix:: + + > export MPLBACKEND=qtagg + > python simple_plot.py + + > MPLBACKEND=qtagg python simple_plot.py + + On Windows, only the former is possible:: + + > set MPLBACKEND=qtagg + > python simple_plot.py + + Setting this environment variable will override the ``backend`` parameter + in *any* :file:`matplotlibrc`, even if there is a :file:`matplotlibrc` in + your current working directory. Therefore, setting :envvar:`MPLBACKEND` + globally, e.g. in your :file:`.bashrc` or :file:`.profile`, is discouraged + as it might lead to counter-intuitive behavior. + +#. If your script depends on a specific backend you can use the function + :func:`matplotlib.use`:: + + import matplotlib + matplotlib.use('qtagg') + + This should be done before any figure is created, otherwise Matplotlib may + fail to switch the backend and raise an ImportError. + + Using `~matplotlib.use` will require changes in your code if users want to + use a different backend. Therefore, you should avoid explicitly calling + `~matplotlib.use` unless absolutely necessary. + +.. _the-builtin-backends: + +The builtin backends +-------------------- + +By default, Matplotlib should automatically select a default backend which +allows both interactive work and plotting from scripts, with output to the +screen and/or to a file, so at least initially, you will not need to worry +about the backend. The most common exception is if your Python distribution +comes without :mod:`tkinter` and you have no other GUI toolkit installed. +This happens on certain Linux distributions, where you need to install a +Linux package named ``python-tk`` (or similar). + +If, however, you want to write graphical user interfaces, or a web +application server +(:doc:`/gallery/user_interfaces/web_application_server_sgskip`), or need a +better understanding of what is going on, read on. To make things easily +more customizable for graphical user interfaces, Matplotlib separates +the concept of the renderer (the thing that actually does the drawing) +from the canvas (the place where the drawing goes). The canonical +renderer for user interfaces is ``Agg`` which uses the `Anti-Grain +Geometry`_ C++ library to make a raster (pixel) image of the figure; it +is used by the ``QtAgg``, ``GTK4Agg``, ``GTK3Agg``, ``wxAgg``, ``TkAgg``, and +``macosx`` backends. An alternative renderer is based on the Cairo library, +used by ``QtCairo``, etc. + +For the rendering engines, users can also distinguish between `vector +`_ or `raster +`_ renderers. Vector +graphics languages issue drawing commands like "draw a line from this +point to this point" and hence are scale free. Raster backends +generate a pixel representation of the line whose accuracy depends on a +DPI setting. + +Here is a summary of the Matplotlib renderers (there is an eponymous +backend for each; these are *non-interactive backends*, capable of +writing to a file): + +======== ========= ======================================================= +Renderer Filetypes Description +======== ========= ======================================================= +AGG png raster_ graphics -- high quality images using the + `Anti-Grain Geometry`_ engine +PDF pdf vector_ graphics -- `Portable Document Format`_ +PS ps, eps vector_ graphics -- Postscript_ output +SVG svg vector_ graphics -- `Scalable Vector Graphics`_ +PGF pgf, pdf vector_ graphics -- using the pgf_ package +Cairo png, ps, raster_ or vector_ graphics -- using the Cairo_ library + pdf, svg +======== ========= ======================================================= + +To save plots using the non-interactive backends, use the +``matplotlib.pyplot.savefig('filename')`` method. + +These are the user interfaces and renderer combinations supported; +these are *interactive backends*, capable of displaying to the screen +and using appropriate renderers from the table above to write to +a file: + +========= ================================================================ +Backend Description +========= ================================================================ +QtAgg Agg rendering in a Qt_ canvas (requires PyQt_ or `Qt for Python`_, + a.k.a. PySide). This backend can be activated in IPython with + ``%matplotlib qt``. +ipympl Agg rendering embedded in a Jupyter widget. (requires ipympl). + This backend can be enabled in a Jupyter notebook with + ``%matplotlib ipympl``. +GTK3Agg Agg rendering to a GTK_ 3.x canvas (requires PyGObject_, + and pycairo_ or cairocffi_). This backend can be activated in + IPython with ``%matplotlib gtk3``. +GTK4Agg Agg rendering to a GTK_ 4.x canvas (requires PyGObject_, + and pycairo_ or cairocffi_). This backend can be activated in + IPython with ``%matplotlib gtk4``. +macosx Agg rendering into a Cocoa canvas in OSX. This backend can be + activated in IPython with ``%matplotlib osx``. +TkAgg Agg rendering to a Tk_ canvas (requires TkInter_). This + backend can be activated in IPython with ``%matplotlib tk``. +nbAgg Embed an interactive figure in a Jupyter classic notebook. This + backend can be enabled in Jupyter notebooks via + ``%matplotlib notebook``. +WebAgg On ``show()`` will start a tornado server with an interactive + figure. +GTK3Cairo Cairo rendering to a GTK_ 3.x canvas (requires PyGObject_, + and pycairo_ or cairocffi_). +GTK4Cairo Cairo rendering to a GTK_ 4.x canvas (requires PyGObject_, + and pycairo_ or cairocffi_). +wxAgg Agg rendering to a wxWidgets_ canvas (requires wxPython_ 4). + This backend can be activated in IPython with ``%matplotlib wx``. +========= ================================================================ + +.. note:: + The names of builtin backends case-insensitive; e.g., 'QtAgg' and + 'qtagg' are equivalent. + +.. _`Anti-Grain Geometry`: http://agg.sourceforge.net/antigrain.com/ +.. _`Portable Document Format`: https://en.wikipedia.org/wiki/Portable_Document_Format +.. _Postscript: https://en.wikipedia.org/wiki/PostScript +.. _`Scalable Vector Graphics`: https://en.wikipedia.org/wiki/Scalable_Vector_Graphics +.. _pgf: https://ctan.org/pkg/pgf +.. _Cairo: https://www.cairographics.org +.. _PyGObject: https://wiki.gnome.org/action/show/Projects/PyGObject +.. _pycairo: https://www.cairographics.org/pycairo/ +.. _cairocffi: https://pythonhosted.org/cairocffi/ +.. _wxPython: https://www.wxpython.org/ +.. _TkInter: https://docs.python.org/3/library/tk.html +.. _PyQt: https://riverbankcomputing.com/software/pyqt/intro +.. _`Qt for Python`: https://doc.qt.io/qtforpython/ +.. _Qt: https://qt.io/ +.. _GTK: https://www.gtk.org/ +.. _Tk: https://www.tcl.tk/ +.. _wxWidgets: https://www.wxwidgets.org/ + +ipympl +^^^^^^ + +The Jupyter widget ecosystem is moving too fast to support directly in +Matplotlib. To install ipympl: + +.. code-block:: bash + + pip install ipympl + jupyter nbextension enable --py --sys-prefix ipympl + +or + +.. code-block:: bash + + conda install ipympl -c conda-forge + +See `jupyter-matplotlib `__ +for more details. + +.. _QT_API-usage: + +How do I select PyQt5 or PySide2? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The :envvar:`QT_API` environment variable can be set to either ``pyqt5`` or +``pyside2`` to use ``PyQt5`` or ``PySide2``, respectively. + +Since the default value for the bindings to be used is ``PyQt5``, Matplotlib +first tries to import it. If the import fails, it tries to import +``PySide2``. + +Using non-builtin backends +-------------------------- +More generally, any importable backend can be selected by using any of the +methods above. If ``name.of.the.backend`` is the module containing the +backend, use ``module://name.of.the.backend`` as the backend name, e.g. +``matplotlib.use('module://name.of.the.backend')``. \ No newline at end of file diff --git a/doc/users/getting_started.rst b/doc/users/getting_started.rst new file mode 100644 index 000000000000..1cb7c97952da --- /dev/null +++ b/doc/users/getting_started.rst @@ -0,0 +1,54 @@ +Getting started +=============== + +Installation +------------ + +.. container:: twocol + + .. container:: + + Install using pip: + + .. code-block:: bash + + pip install matplotlib + + .. container:: + + Install using conda: + + .. code-block:: bash + + conda install matplotlib + +Further details are available in the :doc:`Installation Guide `. + +Draw a first plot +----------------- + +Here is a minimal example plot you can try out: + +.. plot:: + :include-source: + :align: center + + import matplotlib.pyplot as plt + import numpy as np + + x = np.linspace(0, 2 * np.pi, 200) + y = np.sin(x) + + fig, ax = plt.subplots() + ax.plot(x, y) + plt.show() + +If a plot does not show up please check :ref:`troubleshooting-faq`. + +Where to go next +---------------- + +- Check out :doc:`Plot types ` to get an overview of the + types of plots you can create with Matplotlib. +- Learn Matplotlib from the ground up in the + :doc:`Quick-start guide `. diff --git a/doc/users/history.rst b/doc/users/history.rst index 78c5d7cd0af9..19ba9c551ec2 100644 --- a/doc/users/history.rst +++ b/doc/users/history.rst @@ -75,7 +75,7 @@ backends: PS creates `PostScript® `_ hardcopy, SVG creates `Scalable Vector Graphics `_ hardcopy, Agg creates PNG output using the high quality `Anti-Grain -Geometry `_ +Geometry `_ library that ships with Matplotlib, GTK embeds Matplotlib in a `Gtk+ `_ application, GTKAgg uses the Anti-Grain renderer to create a figure diff --git a/doc/users/index.rst b/doc/users/index.rst index fd3bec7dfcdd..ac5af88b418a 100644 --- a/doc/users/index.rst +++ b/doc/users/index.rst @@ -10,7 +10,11 @@ Usage guide ../plot_types/index.rst ../tutorials/index.rst ../gallery/index.rst + getting_started.rst + backends.rst + performance.rst explain.rst ../faq/index.rst ../api/index.rst ../resources/index.rst + \ No newline at end of file diff --git a/doc/users/next_whats_new/allow_changing_the_vertical_axis_in_3d_plots.rst b/doc/users/next_whats_new/allow_changing_the_vertical_axis_in_3d_plots.rst deleted file mode 100644 index fead9f359f56..000000000000 --- a/doc/users/next_whats_new/allow_changing_the_vertical_axis_in_3d_plots.rst +++ /dev/null @@ -1,5 +0,0 @@ -Allow changing the vertical axis in 3d plots ----------------------------------------------- - -`~mpl_toolkits.mplot3d.axes3d.Axes3D.view_init` now has the parameter -*vertical_axis* which allows switching which axis is aligned vertically. diff --git a/doc/users/next_whats_new/animatable_FancyArrow.rst b/doc/users/next_whats_new/animatable_FancyArrow.rst deleted file mode 100644 index 55513fba6db0..000000000000 --- a/doc/users/next_whats_new/animatable_FancyArrow.rst +++ /dev/null @@ -1,5 +0,0 @@ -``set_data`` method for ``FancyArrow`` patch --------------------------------------------- - -`.FancyArrow`, the patch returned by ``ax.arrow``, now has a ``set_data`` -method that allows for animating the arrow. diff --git a/doc/users/next_whats_new/annulus.rst b/doc/users/next_whats_new/annulus.rst deleted file mode 100644 index cdfa875bba2b..000000000000 --- a/doc/users/next_whats_new/annulus.rst +++ /dev/null @@ -1,17 +0,0 @@ -Add ``Annulus`` patch ---------------------- - -`.Annulus` is a new class for drawing elliptical annuli. - -.. plot:: - - import matplotlib.pyplot as plt - from matplotlib.patches import Annulus - - fig, ax = plt.subplots() - cir = Annulus((0.5, 0.5), 0.2, 0.05, fc='g') # circular annulus - ell = Annulus((0.5, 0.5), (0.5, 0.3), 0.1, 45, # elliptical - fc='m', ec='b', alpha=0.5, hatch='xxx') - ax.add_patch(cir) - ax.add_patch(ell) - ax.set_aspect('equal') diff --git a/doc/users/next_whats_new/arrow_param.rst b/doc/users/next_whats_new/arrow_param.rst deleted file mode 100644 index 7e544feb0380..000000000000 --- a/doc/users/next_whats_new/arrow_param.rst +++ /dev/null @@ -1,6 +0,0 @@ -New param ``arrow`` for the ``ArrowStyle`` arrows -------------------------------------------------- - -The new ``arrow`` substitutes the use of ``beginarrow``, ``beginarrow`` -params in the creation of arrows. It receives arrows strings like '<-', -']-[' and ']->'. \ No newline at end of file diff --git a/doc/users/next_whats_new/axes3d_computed_zorder.rst b/doc/users/next_whats_new/axes3d_computed_zorder.rst deleted file mode 100644 index d40d9334988b..000000000000 --- a/doc/users/next_whats_new/axes3d_computed_zorder.rst +++ /dev/null @@ -1,6 +0,0 @@ -Axes3D now allows manual control of draw order ----------------------------------------------- - -The :class:`~mpl_toolkits.mplot3d.axes3d.Axes3D` class now has -``computed_zorder`` parameter. When set to False, Artists are drawn using their -``zorder`` attribute. diff --git a/doc/users/next_whats_new/callback_blocking.rst b/doc/users/next_whats_new/callback_blocking.rst deleted file mode 100644 index 090e06c61bbf..000000000000 --- a/doc/users/next_whats_new/callback_blocking.rst +++ /dev/null @@ -1,25 +0,0 @@ -``CallbackRegistry`` objects gain a method to temporarily block signals ------------------------------------------------------------------------ - -The context manager `~matplotlib.cbook.CallbackRegistry.blocked` can be used -to block callback signals from being processed by the ``CallbackRegistry``. -The optional keyword, *signal*, can be used to block a specific signal -from being processed and let all other signals pass. - -.. code-block:: - - import matplotlib.pyplot as plt - - fig, ax = plt.subplots() - ax.imshow([[0, 1], [2, 3]]) - - # Block all interactivity through the canvas callbacks - with fig.canvas.callbacks.blocked(): - plt.show() - - fig, ax = plt.subplots() - ax.imshow([[0, 1], [2, 3]]) - - # Only block key press events - with fig.canvas.callbacks.blocked(signal="key_press_event"): - plt.show() diff --git a/doc/users/next_whats_new/callbacks_on_norms.rst b/doc/users/next_whats_new/callbacks_on_norms.rst deleted file mode 100644 index 1904a92d2fba..000000000000 --- a/doc/users/next_whats_new/callbacks_on_norms.rst +++ /dev/null @@ -1,8 +0,0 @@ -A callback registry has been added to Normalize objects -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -`.colors.Normalize` objects now have a callback registry, ``callbacks``, -that can be connected to by other objects to be notified when the norm is -updated. The callback emits the key ``changed`` when the norm is modified. -`.cm.ScalarMappable` is now a listener and will register a change -when the norm's vmin, vmax or other attributes are changed. diff --git a/doc/users/next_whats_new/collection_offsets.rst b/doc/users/next_whats_new/collection_offsets.rst deleted file mode 100644 index d53d349a38fb..000000000000 --- a/doc/users/next_whats_new/collection_offsets.rst +++ /dev/null @@ -1,5 +0,0 @@ -Setting collection offset transform after initialization --------------------------------------------------------- -`.collections.Collection.set_offset_transform()` was added. - -Previously the offset transform could not be set after initialization. This can be helpful when creating a `.collections.Collection` outside an axes object and later adding it with `.Axes.add_collection()` and settings the offset transform to `.Axes.transData`. diff --git a/doc/users/next_whats_new/colormaps.rst b/doc/users/next_whats_new/colormaps.rst deleted file mode 100644 index b4e92877b1f0..000000000000 --- a/doc/users/next_whats_new/colormaps.rst +++ /dev/null @@ -1,22 +0,0 @@ -Colormap registry (experimental) --------------------------------- - -Colormaps are now managed via `matplotlib.colormaps` (or `.pyplot.colormaps`), -which is a `.ColormapRegistry`. While we are confident that the API is final, -we formally mark it as experimental for 3.5 because we want to keep the option -to still adapt the API for 3.6 should the need arise. - -Colormaps can be obtained using item access:: - - import matplotlib.pyplot as plt - cmap = plt.colormaps['viridis'] - -To register new colormaps use:: - - plt.colormaps.register(my_colormap) - -We recommend to use the new API instead of the `~.cm.get_cmap` and -`~.cm.register_cmap` functions for new code. `matplotlib.cm.get_cmap` and -`matplotlib.cm.register_cmap` will eventually be deprecated and removed. -Within `.pyplot` ``plt.get_cmap()`` and ``plt.register_cmap()`` will continue -to be supported for backward compatibility. \ No newline at end of file diff --git a/doc/users/next_whats_new/extending_MarkerStyle.rst b/doc/users/next_whats_new/extending_MarkerStyle.rst new file mode 100644 index 000000000000..6e970d0738fe --- /dev/null +++ b/doc/users/next_whats_new/extending_MarkerStyle.rst @@ -0,0 +1,19 @@ +New customization of MarkerStyle +-------------------------------- + +New MarkerStyle parameters allow control of join style and cap style, and for +the user to supply a transformation to be applied to the marker (e.g. a rotation). + +.. plot:: + :include-source: true + + import matplotlib.pyplot as plt + from matplotlib.markers import MarkerStyle + from matplotlib.transforms import Affine2D + fig, ax = plt.subplots(figsize=(6, 1)) + fig.suptitle('New markers', fontsize=14) + for col, (size, rot) in enumerate(zip([2, 5, 10], [0, 45, 90])): + t = Affine2D().rotate_deg(rot).scale(size) + ax.plot(col, 0, marker=MarkerStyle("*", transform=t)) + ax.axis("off") + ax.set_xlim(-0.1, 2.4) diff --git a/doc/users/next_whats_new/fig_draw_without_rendering.rst b/doc/users/next_whats_new/fig_draw_without_rendering.rst deleted file mode 100644 index 8b9e3147bf07..000000000000 --- a/doc/users/next_whats_new/fig_draw_without_rendering.rst +++ /dev/null @@ -1,12 +0,0 @@ -Figure now has ``draw_without_rendering`` method ------------------------------------------------- - -Some aspects of a figure are only determined at draw-time, such as the exact -position of text artists or deferred computation like automatic data limits. -If you need these values, you can use ``figure.canvas.draw()`` to force a full -draw. However, this has side effects, sometimes requires an open file, and is -doing more work than is needed. - -The new `.Figure.draw_without_rendering` method runs all the updates that -``draw()`` does, but skips rendering the figure. It's thus more efficient if you -need the updated values to configure further aspects of the figure. diff --git a/doc/users/next_whats_new/figure_kwargs.rst b/doc/users/next_whats_new/figure_kwargs.rst deleted file mode 100644 index 738cb65f972d..000000000000 --- a/doc/users/next_whats_new/figure_kwargs.rst +++ /dev/null @@ -1,11 +0,0 @@ - -Figure init passes keyword arguments through to set ---------------------------------------------------- - -Similar to many other sub-classes of `~.Artist`, `~.FigureBase`, `~.SubFigure`, -and `~.Figure` will now pass any additional keyword arguments to `~.Artist.set` -to allow properties of the newly created object to be set at init time. For -example :: - - from matplotlib.figure import Figure - fig = Figure(label='my figure') diff --git a/doc/users/next_whats_new/image_interpolation_rgbastage.rst b/doc/users/next_whats_new/image_interpolation_rgbastage.rst deleted file mode 100644 index 5b95f368e382..000000000000 --- a/doc/users/next_whats_new/image_interpolation_rgbastage.rst +++ /dev/null @@ -1,21 +0,0 @@ -Image interpolation now possible at RGBA stage ----------------------------------------------- - -Images in Matplotlib created via `~.axes.Axes.imshow` are resampled to match -the resolution of the current canvas. It is useful to apply an anto-aliasing -filter when downsampling to reduce Moire effects. By default, interpolation -is done on the data, a norm applied, and then the colormapping performed. - -However, it is often desireable for the anti-aliasing interpolation to happen -in RGBA space, where the colors are interpolated rather than the data. This -usually leads to colors outside the colormap, but visually blends adjacent -colors, and is what browsers and other image processing software does. - -A new keyword argument *interpolation_stage* is provided for -`~.axes.Axes.imshow` to set the stage at which the anti-aliasing interpolation -happens. The default is the current behaviour of "data", with the alternative -being "rgba" for the newly-available behavior. - -For more details see the discussion of the new keyword argument in -:doc:`/gallery/images_contours_and_fields/image_antialiasing`. - diff --git a/doc/users/next_whats_new/legend_title_fontproperties_kwarg.rst b/doc/users/next_whats_new/legend_title_fontproperties_kwarg.rst deleted file mode 100644 index fca6c8b4da8f..000000000000 --- a/doc/users/next_whats_new/legend_title_fontproperties_kwarg.rst +++ /dev/null @@ -1,11 +0,0 @@ -Font properties of legend title are configurable ------------------------------------------------- - -Title's font properties can be set via the *title_fontproperties* keyword -argument, for example: - -.. plot:: - - fig, ax = plt.subplots(figsize=(4, 3)) - ax.plot(range(10),label='point') - ax.legend(title='Points', title_fontproperties={'family': 'serif', 'size': 20}) diff --git a/doc/users/next_whats_new/mathmpl_hidpi.rst b/doc/users/next_whats_new/mathmpl_hidpi.rst deleted file mode 100644 index e90708d5e35d..000000000000 --- a/doc/users/next_whats_new/mathmpl_hidpi.rst +++ /dev/null @@ -1,13 +0,0 @@ -More configuration of ``mathmpl:`` sphinx extension ---------------------------------------------------- - -The `matplotlib.sphinxext.mathmpl` sphinx extension supports two new -configuration options that may be specified in your ``conf.py``: - -- ``mathmpl_fontsize`` (float), which sets the font size of the math text in - points; -- ``mathmpl_srcset`` (list of str), which provides a list of sizes to support - `responsive resolution images - `__ - The list should contain additional x-descriptors (``'1.5x'``, ``'2x'``, etc.) - to generate (1x is the default and always included.) diff --git a/doc/users/next_whats_new/new_arrow_styles.rst b/doc/users/next_whats_new/new_arrow_styles.rst deleted file mode 100644 index d2c73cfb584c..000000000000 --- a/doc/users/next_whats_new/new_arrow_styles.rst +++ /dev/null @@ -1,5 +0,0 @@ -New arrow styles for ``ConnectionPatch`` ----------------------------------------- - -The new ``]->`` and ``<-[`` can be passed as the ``arrowstyle`` -params to the ``ConnectionPatch``. \ No newline at end of file diff --git a/doc/users/next_whats_new/polygons_selector_remove_points.rst b/doc/users/next_whats_new/polygons_selector_remove_points.rst deleted file mode 100644 index 267031d62e96..000000000000 --- a/doc/users/next_whats_new/polygons_selector_remove_points.rst +++ /dev/null @@ -1,4 +0,0 @@ -Removing points on a PolygonSelector ------------------------------------- -After completing a `~matplotlib.widgets.PolygonSelector`, individual -points can now be removed by right-clicking on them. diff --git a/doc/users/next_whats_new/positioning_of_text_inside_TextBox.rst b/doc/users/next_whats_new/positioning_of_text_inside_TextBox.rst deleted file mode 100644 index c52c57efee19..000000000000 --- a/doc/users/next_whats_new/positioning_of_text_inside_TextBox.rst +++ /dev/null @@ -1,13 +0,0 @@ -Text can be positioned inside TextBox widget --------------------------------------------- - -A new parameter called *textalignment* can be used to control for the position of the text inside the Axes of the TextBox widget. - -.. plot:: - - from matplotlib import pyplot as plt - from matplotlib.widgets import TextBox - - box_input = plt.axes([0.2, 0.2, 0.1, 0.075]) - text_box = TextBox(ax=box_input, initial="text", label="", textalignment="center") - diff --git a/doc/users/next_whats_new/rcparams_legend.rst b/doc/users/next_whats_new/rcparams_legend.rst deleted file mode 100644 index bd9c6b3db8c5..000000000000 --- a/doc/users/next_whats_new/rcparams_legend.rst +++ /dev/null @@ -1,25 +0,0 @@ -New rcParams for legend: set legend labelcolor globally -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A new :rc:`legend.labelcolor` sets the default *labelcolor* argument for -`.Figure.legend`. The special values 'linecolor', 'markerfacecolor' -(or 'mfc'), or 'markeredgecolor' (or 'mec') will cause the legend text to match -the corresponding color of marker. - - -.. plot:: - - plt.rcParams['legend.labelcolor'] = 'linecolor' - - # Make some fake data. - a = np.arange(0, 3, .02) - c = np.exp(a) - d = c[::-1] - - fig, ax = plt.subplots() - ax.plot(a, c, 'g--', label='Model length') - ax.plot(a, d, 'r:', label='Data length') - - ax.legend() - - plt.show() \ No newline at end of file diff --git a/doc/users/next_whats_new/set_ticks_labels.rst b/doc/users/next_whats_new/set_ticks_labels.rst deleted file mode 100644 index 7cb6692d5260..000000000000 --- a/doc/users/next_whats_new/set_ticks_labels.rst +++ /dev/null @@ -1,16 +0,0 @@ -Settings tick positions and labels simultaneously in ``set_ticks`` ------------------------------------------------------------------- -`.Axis.set_ticks` (and the corresponding `.Axes.set_xticks` / -`.Axes.set_yticks`) got a new parameter *labels* allowing to set tick positions -and labels simultaneously. - -Previously, setting tick labels was done using `.Axis.set_ticklabels` (or -the corresponding `.Axes.set_xticklabels` / `.Axes.set_yticklabels`). This -usually only makes sense if you previously fix the position with -`~.Axis.set_ticks`. Both functionality is now available in `~.Axis.set_ticks`. -The use of `.Axis.set_ticklabels` is discouraged, but it will stay available -for backward compatibility. - -Note: This addition makes the API of `~.Axis.set_ticks` also more similar to -`.pyplot.xticks` / `.pyplot.yticks`, which already had the additional *labels* -parameter. diff --git a/doc/users/next_whats_new/setting_artists_properties_selector.rst b/doc/users/next_whats_new/setting_artists_properties_selector.rst deleted file mode 100644 index dbf1a4333f0e..000000000000 --- a/doc/users/next_whats_new/setting_artists_properties_selector.rst +++ /dev/null @@ -1,5 +0,0 @@ -Setting artist properties of selectors --------------------------------------- - -The artist properties of selectors can be changed using the ``set_props`` and -``set_handle_props`` methods. diff --git a/doc/users/next_whats_new/simplify_font_setting_usetex.rst b/doc/users/next_whats_new/simplify_font_setting_usetex.rst deleted file mode 100644 index 85618de24416..000000000000 --- a/doc/users/next_whats_new/simplify_font_setting_usetex.rst +++ /dev/null @@ -1,13 +0,0 @@ - -Simplifying the font setting for usetex mode -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Now the :rc:`font.family` accepts some font names as value for a more -user-friendly setup. - -.. code-block:: - - plt.rcParams.update({ - "text.usetex": True, - "font.family": "Helvetica" - }) \ No newline at end of file diff --git a/doc/users/next_whats_new/slider_styling.rst b/doc/users/next_whats_new/slider_styling.rst deleted file mode 100644 index f007954b4806..000000000000 --- a/doc/users/next_whats_new/slider_styling.rst +++ /dev/null @@ -1,42 +0,0 @@ -Updated the appearance of Slider widgets ----------------------------------------- - -The appearance of `~.Slider` and `~.RangeSlider` widgets -were updated and given new styling parameters for the -added handles. - -.. plot:: - - import matplotlib.pyplot as plt - from matplotlib.widgets import Slider - - plt.figure(figsize=(4, 2)) - ax_old = plt.axes([0.2, 0.65, 0.65, 0.1]) - ax_new = plt.axes([0.2, 0.25, 0.65, 0.1]) - Slider(ax_new, "New", 0, 1) - - ax = ax_old - valmin = 0 - valinit = 0.5 - ax.set_xlim([0, 1]) - ax_old.axvspan(valmin, valinit, 0, 1) - ax.axvline(valinit, 0, 1, color="r", lw=1) - ax.set_xticks([]) - ax.set_yticks([]) - ax.text( - -0.02, - 0.5, - "Old", - transform=ax.transAxes, - verticalalignment="center", - horizontalalignment="right", - ) - - ax.text( - 1.02, - 0.5, - "0.5", - transform=ax.transAxes, - verticalalignment="center", - horizontalalignment="left", - ) diff --git a/doc/users/next_whats_new/subsetting.rst b/doc/users/next_whats_new/subsetting.rst deleted file mode 100644 index 89c33f58d371..000000000000 --- a/doc/users/next_whats_new/subsetting.rst +++ /dev/null @@ -1,22 +0,0 @@ -Type 42 Subsetting is now enabled for PDF/PS backends ------------------------------------------------------ - -`~matplotlib.backends.backend_pdf` and `~matplotlib.backends.backend_ps` now use -a unified Type 42 font subsetting interface, with the help of `fontTools `_ - -Set `~matplotlib.RcParams`'s *fonttype* value as ``42`` to trigger this workflow: - -.. code-block:: - - # for PDF backend - plt.rcParams['pdf.fonttype'] = 42 - - # for PS backend - plt.rcParams['ps.fonttype'] = 42 - - - fig, ax = plt.subplots() - ax.text(0.4, 0.5, 'subsetted document is smaller in size!') - - fig.savefig("document.pdf") - fig.savefig("document.ps") diff --git a/doc/users/next_whats_new/version_info.rst b/doc/users/next_whats_new/version_info.rst deleted file mode 100644 index 5c51f9fe7332..000000000000 --- a/doc/users/next_whats_new/version_info.rst +++ /dev/null @@ -1,15 +0,0 @@ -Version information -------------------- -We switched to the `release-branch-semver`_ version scheme. This only affects, -the version information for development builds. Their version number now -describes the targeted release, i.e. 3.5.0.dev820+g6768ef8c4c.d20210520 -is 820 commits after the previous release and is scheduled to be officially -released as 3.5.0 later. - -In addition to the string ``__version__``, there is now a namedtuple -``__version_info__`` as well, which is modelled after `sys.version_info`_. -Its primary use is safely comparing version information, e.g. -``if __version_info__ >= (3, 4, 2)``. - -.. _release-branch-semver: https://github.com/pypa/setuptools_scm#version-number-construction -.. _sys.version_info: https://docs.python.org/3/library/sys.html#sys.version_info \ No newline at end of file diff --git a/doc/users/next_whats_new/widget_clear.rst b/doc/users/next_whats_new/widget_clear.rst deleted file mode 100644 index 3c72dd60cc52..000000000000 --- a/doc/users/next_whats_new/widget_clear.rst +++ /dev/null @@ -1,7 +0,0 @@ -Clear selector --------------- -The selectors (`~matplotlib.widgets.SpanSelector`, `~matplotlib.widgets.RectangleSelector`, -`~matplotlib.widgets.EllipseSelector`, `~matplotlib.widgets.PolygonSelector` and -`~matplotlib.widgets.LassoSelector` have a new method *clear*, which will clear -the current selection and get the selector ready to make a new selection. This -is equivalent to press the *escape* key. diff --git a/doc/users/next_whats_new/widget_dragging.rst b/doc/users/next_whats_new/widget_dragging.rst deleted file mode 100644 index 174a9ced77d4..000000000000 --- a/doc/users/next_whats_new/widget_dragging.rst +++ /dev/null @@ -1,12 +0,0 @@ -Dragging selectors ------------------- - -The `~matplotlib.widgets.SpanSelector`, `~matplotlib.widgets.RectangleSelector` -and `~matplotlib.widgets.EllipseSelector` have a new keyword argument, -*drag_from_anywhere*, which when set to `True` allows you to click and drag -from anywhere inside the selector to move it. Previously it was only possible -to move it by either activating the move modifier button, or clicking on the -central handle. - -The size of the `~matplotlib.widgets.SpanSelector` can now be changed using -the edge handles. diff --git a/doc/users/next_whats_new/widget_ignore_events.rst b/doc/users/next_whats_new/widget_ignore_events.rst deleted file mode 100644 index 2d90e65ad5a7..000000000000 --- a/doc/users/next_whats_new/widget_ignore_events.rst +++ /dev/null @@ -1,7 +0,0 @@ -Ignore events outside selection -------------------------------- -The `~matplotlib.widgets.SpanSelector`, `~matplotlib.widgets.RectangleSelector` -and `~matplotlib.widgets.EllipseSelector` have a new keyword argument, -*ignore_event_outside*, which when set to `True` will ignore events outside of -the current selection. The handles or the new dragging functionality can instead -be used to change the selection. diff --git a/doc/users/performance.rst b/doc/users/performance.rst new file mode 100644 index 000000000000..aa291f407c1e --- /dev/null +++ b/doc/users/performance.rst @@ -0,0 +1,151 @@ +.. _performance: + +Performance +=========== + +Whether exploring data in interactive mode or programmatically +saving lots of plots, rendering performance can be a challenging +bottleneck in your pipeline. Matplotlib provides multiple +ways to greatly reduce rendering time at the cost of a slight +change (to a settable tolerance) in your plot's appearance. +The methods available to reduce rendering time depend on the +type of plot that is being created. + +Line segment simplification +--------------------------- + +For plots that have line segments (e.g. typical line plots, outlines +of polygons, etc.), rendering performance can be controlled by +:rc:`path.simplify` and :rc:`path.simplify_threshold`, which +can be defined e.g. in the :file:`matplotlibrc` file (see +:doc:`/tutorials/introductory/customizing` for more information about +the :file:`matplotlibrc` file). :rc:`path.simplify` is a Boolean +indicating whether or not line segments are simplified at all. +:rc:`path.simplify_threshold` controls how much line segments are simplified; +higher thresholds result in quicker rendering. + +The following script will first display the data without any +simplification, and then display the same data with simplification. +Try interacting with both of them:: + + import numpy as np + import matplotlib.pyplot as plt + import matplotlib as mpl + + # Setup, and create the data to plot + y = np.random.rand(100000) + y[50000:] *= 2 + y[np.geomspace(10, 50000, 400).astype(int)] = -1 + mpl.rcParams['path.simplify'] = True + + mpl.rcParams['path.simplify_threshold'] = 0.0 + plt.plot(y) + plt.show() + + mpl.rcParams['path.simplify_threshold'] = 1.0 + plt.plot(y) + plt.show() + +Matplotlib currently defaults to a conservative simplification +threshold of ``1/9``. To change default settings to use a different +value, change the :file:`matplotlibrc` file. Alternatively, users +can create a new style for interactive plotting (with maximal +simplification) and another style for publication quality plotting +(with minimal simplification) and activate them as necessary. See +:doc:`/tutorials/introductory/customizing` for instructions on +how to perform these actions. + + +The simplification works by iteratively merging line segments +into a single vector until the next line segment's perpendicular +distance to the vector (measured in display-coordinate space) +is greater than the ``path.simplify_threshold`` parameter. + +.. note:: + Changes related to how line segments are simplified were made + in version 2.1. Rendering time will still be improved by these + parameters prior to 2.1, but rendering time for some kinds of + data will be vastly improved in versions 2.1 and greater. + +Marker simplification +--------------------- + +Markers can also be simplified, albeit less robustly than +line segments. Marker simplification is only available +to :class:`~matplotlib.lines.Line2D` objects (through the +``markevery`` property). Wherever +:class:`~matplotlib.lines.Line2D` construction parameters +are passed through, such as +:func:`matplotlib.pyplot.plot` and +:meth:`matplotlib.axes.Axes.plot`, the ``markevery`` +parameter can be used:: + + plt.plot(x, y, markevery=10) + +The ``markevery`` argument allows for naive subsampling, or an +attempt at evenly spaced (along the *x* axis) sampling. See the +:doc:`/gallery/lines_bars_and_markers/markevery_demo` +for more information. + +Splitting lines into smaller chunks +----------------------------------- + +If you are using the Agg backend (see :ref:`what-is-a-backend`), +then you can make use of :rc:`agg.path.chunksize` +This allows users to specify a chunk size, and any lines with +greater than that many vertices will be split into multiple +lines, each of which has no more than ``agg.path.chunksize`` +many vertices. (Unless ``agg.path.chunksize`` is zero, in +which case there is no chunking.) For some kind of data, +chunking the line up into reasonable sizes can greatly +decrease rendering time. + +The following script will first display the data without any +chunk size restriction, and then display the same data with +a chunk size of 10,000. The difference can best be seen when +the figures are large, try maximizing the GUI and then +interacting with them:: + + import numpy as np + import matplotlib.pyplot as plt + import matplotlib as mpl + mpl.rcParams['path.simplify_threshold'] = 1.0 + + # Setup, and create the data to plot + y = np.random.rand(100000) + y[50000:] *= 2 + y[np.geomspace(10, 50000, 400).astype(int)] = -1 + mpl.rcParams['path.simplify'] = True + + mpl.rcParams['agg.path.chunksize'] = 0 + plt.plot(y) + plt.show() + + mpl.rcParams['agg.path.chunksize'] = 10000 + plt.plot(y) + plt.show() + +Legends +------- + +The default legend behavior for axes attempts to find the location +that covers the fewest data points (``loc='best'``). This can be a +very expensive computation if there are lots of data points. In +this case, you may want to provide a specific location. + +Using the *fast* style +---------------------- + +The *fast* style can be used to automatically set +simplification and chunking parameters to reasonable +settings to speed up plotting large amounts of data. +The following code runs it:: + + import matplotlib.style as mplstyle + mplstyle.use('fast') + +It is very lightweight, so it works well with other +styles. Be sure the fast style is applied last +so that other styles do not overwrite the settings:: + + mplstyle.use(['dark_background', 'ggplot', 'fast']) diff --git a/doc/users/prev_whats_new/whats_new_3.5.0.rst b/doc/users/prev_whats_new/whats_new_3.5.0.rst new file mode 100644 index 000000000000..0fc7d33f69a2 --- /dev/null +++ b/doc/users/prev_whats_new/whats_new_3.5.0.rst @@ -0,0 +1,429 @@ +============================== +What's new in Matplotlib 3.5.0 +============================== + +For a list of all of the issues and pull requests since the last revision, see +the :ref:`github-stats`. + +.. contents:: Table of Contents + :depth: 4 + +.. toctree:: + :maxdepth: 4 + +Figure and Axes creation / management +===================================== + +Figure now has ``draw_without_rendering`` method +------------------------------------------------ + +Some aspects of a figure are only determined at draw-time, such as the exact +position of text artists or deferred computation like automatic data limits. +If you need these values, you can use ``figure.canvas.draw()`` to force a full +draw. However, this has side effects, sometimes requires an open file, and is +doing more work than is needed. + +The new `.Figure.draw_without_rendering` method runs all the updates that +``draw()`` does, but skips rendering the figure. It's thus more efficient if +you need the updated values to configure further aspects of the figure. + +Figure ``__init__`` passes keyword arguments through to set +----------------------------------------------------------- + +Similar to many other sub-classes of `~.Artist`, the `~.FigureBase`, +`~.SubFigure`, and `~.Figure` classes will now pass any additional keyword +arguments to `~.Artist.set` to allow properties of the newly created object to +be set at initialization time. For example:: + + from matplotlib.figure import Figure + fig = Figure(label='my figure') + +Plotting methods +================ + +Add ``Annulus`` patch +--------------------- + +`.Annulus` is a new class for drawing elliptical annuli. + +.. plot:: + + import matplotlib.pyplot as plt + from matplotlib.patches import Annulus + + fig, ax = plt.subplots() + cir = Annulus((0.5, 0.5), 0.2, 0.05, fc='g') # circular annulus + ell = Annulus((0.5, 0.5), (0.5, 0.3), 0.1, 45, # elliptical + fc='m', ec='b', alpha=0.5, hatch='xxx') + ax.add_patch(cir) + ax.add_patch(ell) + ax.set_aspect('equal') + +``set_data`` method for ``FancyArrow`` patch +-------------------------------------------- + +`.FancyArrow`, the patch returned by ``ax.arrow``, now has a ``set_data`` +method that allows modifying the arrow after creation, e.g., for animation. + +New arrow styles in ``ArrowStyle`` and ``ConnectionPatch`` +---------------------------------------------------------- + +The new *arrow* parameter to `.ArrowStyle` substitutes the use of the +*beginarrow* and *endarrow* parameters in the creation of arrows. It receives +arrows strings like ``'<-'``, ``']-[``' and ``']->``' instead of individual +booleans. + +Two new styles ``']->'`` and ``'<-['`` are also added via this mechanism. +`.ConnectionPatch`, which accepts arrow styles though its *arrowstyle* +parameter, also accepts these new styles. + +Setting collection offset transform after initialization +-------------------------------------------------------- + +The added `.collections.Collection.set_offset_transform` may be used to set the +offset transform after initialization. This can be helpful when creating a +`.collections.Collection` outside an Axes object and later adding it with +`.Axes.add_collection()` and settings the offset transform to +`.Axes.transData`. + +Colors and colormaps +==================== + +Colormap registry (experimental) +-------------------------------- + +Colormaps are now managed via `matplotlib.colormaps` (or `.pyplot.colormaps`), +which is a `.ColormapRegistry`. While we are confident that the API is final, +we formally mark it as experimental for 3.5 because we want to keep the option +to still adapt the API for 3.6 should the need arise. + +Colormaps can be obtained using item access:: + + import matplotlib.pyplot as plt + cmap = plt.colormaps['viridis'] + +To register new colormaps use:: + + plt.colormaps.register(my_colormap) + +We recommend to use the new API instead of the `~.cm.get_cmap` and +`~.cm.register_cmap` functions for new code. `matplotlib.cm.get_cmap` and +`matplotlib.cm.register_cmap` will eventually be deprecated and removed. +Within `.pyplot`, ``plt.get_cmap()`` and ``plt.register_cmap()`` will continue +to be supported for backward compatibility. + +Image interpolation now possible at RGBA stage +---------------------------------------------- + +Images in Matplotlib created via `~.axes.Axes.imshow` are resampled to match +the resolution of the current canvas. It is useful to apply an auto-aliasing +filter when downsampling to reduce Moiré effects. By default, interpolation is +done on the data, a norm applied, and then the colormapping performed. + +However, it is often desirable for the anti-aliasing interpolation to happen +in RGBA space, where the colors are interpolated rather than the data. This +usually leads to colors outside the colormap, but visually blends adjacent +colors, and is what browsers and other image processing software do. + +A new keyword argument *interpolation_stage* is provided for +`~.axes.Axes.imshow` to set the stage at which the anti-aliasing interpolation +happens. The default is the current behaviour of "data", with the alternative +being "rgba" for the newly-available behavior. + +For more details see the discussion of the new keyword argument in +:doc:`/gallery/images_contours_and_fields/image_antialiasing`. + +A callback registry has been added to Normalize objects +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`.colors.Normalize` objects now have a callback registry, ``callbacks``, that +can be connected to by other objects to be notified when the norm is updated. +The callback emits the key ``changed`` when the norm is modified. +`.cm.ScalarMappable` is now a listener and will register a change when the +norm's vmin, vmax or other attributes are changed. + +Titles, ticks, and labels +========================= + +Settings tick positions and labels simultaneously in ``set_ticks`` +------------------------------------------------------------------ + +`.Axis.set_ticks` (and the corresponding `.Axes.set_xticks` / +`.Axes.set_yticks`) has a new parameter *labels* allowing to set tick positions +and labels simultaneously. + +Previously, setting tick labels was done using `.Axis.set_ticklabels` (or +the corresponding `.Axes.set_xticklabels` / `.Axes.set_yticklabels`). This +usually only makes sense if tick positions were previously fixed with +`~.Axis.set_ticks`. The combined functionality is now available in +`~.Axis.set_ticks`. The use of `.Axis.set_ticklabels` is discouraged, but it +will stay available for backward compatibility. + +Note: This addition makes the API of `~.Axis.set_ticks` also more similar to +`.pyplot.xticks` / `.pyplot.yticks`, which already had the additional *labels* +parameter. + +Fonts and Text +============== + +Font properties of legend title are configurable +------------------------------------------------ + +Title's font properties can be set via the *title_fontproperties* keyword +argument, for example: + +.. plot:: + + fig, ax = plt.subplots(figsize=(4, 3)) + ax.plot(range(10), label='point') + ax.legend(title='Points', + title_fontproperties={'family': 'serif', 'size': 20}) + +Text can be positioned inside TextBox widget +-------------------------------------------- + +A new parameter called *textalignment* can be used to control for the position +of the text inside the Axes of the `.TextBox` widget. + +.. plot:: + + from matplotlib import pyplot as plt + from matplotlib.widgets import TextBox + + for i, alignment in enumerate(['left', 'center', 'right']): + box_input = plt.axes([0.2, 0.7 - i*0.2, 0.6, 0.1]) + text_box = TextBox(ax=box_input, initial=f'{alignment} alignment', + label='', textalignment=alignment) + +Simplifying the font setting for usetex mode +-------------------------------------------- + +Now the :rc:`font.family` accepts some font names as value for a more +user-friendly setup. + +.. code-block:: + + plt.rcParams.update({ + "text.usetex": True, + "font.family": "Helvetica" + }) + +Type 42 subsetting is now enabled for PDF/PS backends +----------------------------------------------------- + +`~matplotlib.backends.backend_pdf` and `~matplotlib.backends.backend_ps` now +use a unified Type 42 font subsetting interface, with the help of `fontTools +`_ + +Set :rc:`pdf.fonttype` or :rc:`ps.fonttype` to ``42`` to trigger this +workflow:: + + # for PDF backend + plt.rcParams['pdf.fonttype'] = 42 + + # for PS backend + plt.rcParams['ps.fonttype'] = 42 + + fig, ax = plt.subplots() + ax.text(0.4, 0.5, 'subsetted document is smaller in size!') + + fig.savefig("document.pdf") + fig.savefig("document.ps") + +rcParams improvements +===================== + +Allow setting default legend labelcolor globally +------------------------------------------------ + +A new :rc:`legend.labelcolor` sets the default *labelcolor* argument for +`.Figure.legend`. The special values 'linecolor', 'markerfacecolor' (or +'mfc'), or 'markeredgecolor' (or 'mec') will cause the legend text to match the +corresponding color of marker. + + +.. plot:: + + plt.rcParams['legend.labelcolor'] = 'linecolor' + + # Make some fake data. + a = np.arange(0, 3, .02) + c = np.exp(a) + d = c[::-1] + + fig, ax = plt.subplots() + ax.plot(a, c, 'g--', label='Model length') + ax.plot(a, d, 'r:', label='Data length') + + ax.legend() + + plt.show() + +3D Axes improvements +==================== + +Axes3D now allows manual control of draw order +---------------------------------------------- + +The `~mpl_toolkits.mplot3d.axes3d.Axes3D` class now has *computed_zorder* +parameter. When set to False, Artists are drawn using their ``zorder`` +attribute. + +Allow changing the vertical axis in 3d plots +---------------------------------------------- + +`~mpl_toolkits.mplot3d.axes3d.Axes3D.view_init` now has the parameter +*vertical_axis* which allows switching which axis is aligned vertically. + +Interactive tool improvements +============================= + +Updated the appearance of Slider widgets +---------------------------------------- + +The appearance of `~.Slider` and `~.RangeSlider` widgets were updated and given +new styling parameters for the added handles. + +.. plot:: + + import matplotlib.pyplot as plt + from matplotlib.widgets import Slider + + plt.figure(figsize=(4, 2)) + ax_old = plt.axes([0.2, 0.65, 0.65, 0.1]) + ax_new = plt.axes([0.2, 0.25, 0.65, 0.1]) + Slider(ax_new, "New", 0, 1) + + ax = ax_old + valmin = 0 + valinit = 0.5 + ax.set_xlim([0, 1]) + ax_old.axvspan(valmin, valinit, 0, 1) + ax.axvline(valinit, 0, 1, color="r", lw=1) + ax.set_xticks([]) + ax.set_yticks([]) + ax.text( + -0.02, + 0.5, + "Old", + transform=ax.transAxes, + verticalalignment="center", + horizontalalignment="right", + ) + + ax.text( + 1.02, + 0.5, + "0.5", + transform=ax.transAxes, + verticalalignment="center", + horizontalalignment="left", + ) + +Removing points on a PolygonSelector +------------------------------------ + +After completing a `~matplotlib.widgets.PolygonSelector`, individual points can +now be removed by right-clicking on them. + +Dragging selectors +------------------ + +The `~matplotlib.widgets.SpanSelector`, `~matplotlib.widgets.RectangleSelector` +and `~matplotlib.widgets.EllipseSelector` have a new keyword argument, +*drag_from_anywhere*, which when set to `True` allows you to click and drag +from anywhere inside the selector to move it. Previously it was only possible +to move it by either activating the move modifier button, or clicking on the +central handle. + +The size of the `~matplotlib.widgets.SpanSelector` can now be changed using the +edge handles. + +Clearing selectors +------------------ + +The selectors (`~.widgets.EllipseSelector`, `~.widgets.LassoSelector`, +`~.widgets.PolygonSelector`, `~.widgets.RectangleSelector`, and +`~.widgets.SpanSelector`) have a new method *clear*, which will clear the +current selection and get the selector ready to make a new selection. This is +equivalent to pressing the *escape* key. + +Setting artist properties of selectors +-------------------------------------- + +The artist properties of the `~.widgets.EllipseSelector`, +`~.widgets.LassoSelector`, `~.widgets.PolygonSelector`, +`~.widgets.RectangleSelector` and `~.widgets.SpanSelector` selectors can be +changed using the ``set_props`` and ``set_handle_props`` methods. + +Ignore events outside selection +------------------------------- + +The `~.widgets.EllipseSelector`, `~.widgets.RectangleSelector` and +`~.widgets.SpanSelector` selectors have a new keyword argument, +*ignore_event_outside*, which when set to `True` will ignore events outside of +the current selection. The handles or the new dragging functionality can instead +be used to change the selection. + +``CallbackRegistry`` objects gain a method to temporarily block signals +----------------------------------------------------------------------- + +The context manager `~matplotlib.cbook.CallbackRegistry.blocked` can be used +to block callback signals from being processed by the ``CallbackRegistry``. +The optional keyword, *signal*, can be used to block a specific signal +from being processed and let all other signals pass. + +.. code-block:: + + import matplotlib.pyplot as plt + + fig, ax = plt.subplots() + ax.imshow([[0, 1], [2, 3]]) + + # Block all interactivity through the canvas callbacks + with fig.canvas.callbacks.blocked(): + plt.show() + + fig, ax = plt.subplots() + ax.imshow([[0, 1], [2, 3]]) + + # Only block key press events + with fig.canvas.callbacks.blocked(signal="key_press_event"): + plt.show() + +Sphinx extensions +================= + +More configuration of ``mathmpl`` sphinx extension +-------------------------------------------------- + +The `matplotlib.sphinxext.mathmpl` sphinx extension supports two new +configuration options that may be specified in your ``conf.py``: + +- ``mathmpl_fontsize`` (float), which sets the font size of the math text in + points; +- ``mathmpl_srcset`` (list of str), which provides a list of sizes to support + `responsive resolution images + `__ + The list should contain additional x-descriptors (``'1.5x'``, ``'2x'``, etc.) + to generate (1x is the default and always included.) + +Backend-specific improvements +============================= + +Version information +=================== + +We switched to the `release-branch-semver`_ version scheme. This only affects, +the version information for development builds. Their version number now +describes the targeted release, i.e. 3.5.0.dev820+g6768ef8c4c.d20210520 is 820 +commits after the previous release and is scheduled to be officially released +as 3.5.0 later. + +In addition to the string ``__version__``, there is now a namedtuple +``__version_info__`` as well, which is modelled after `sys.version_info`_. Its +primary use is safely comparing version information, e.g. ``if +__version_info__ >= (3, 4, 2)``. + +.. _release-branch-semver: https://github.com/pypa/setuptools_scm#version-number-construction +.. _sys.version_info: https://docs.python.org/3/library/sys.html#sys.version_info diff --git a/doc/users/release_notes.rst b/doc/users/release_notes.rst index d8458e5ba1c2..4608dbc93977 100644 --- a/doc/users/release_notes.rst +++ b/doc/users/release_notes.rst @@ -9,6 +9,14 @@ Release notes .. include from another document so that it's easy to exclude this for releases .. include:: release_notes_next.rst +Version 3.5 +=========== +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_3.5.0.rst + ../api/prev_api_changes/api_changes_3.5.0.rst + Version 3.4 =========== .. toctree:: diff --git a/environment.yml b/environment.yml index eb284c6ab09d..a0d869a356b9 100644 --- a/environment.yml +++ b/environment.yml @@ -27,17 +27,15 @@ dependencies: - ipython - ipywidgets - numpydoc>=0.8 + - packaging - pydata-sphinx-theme - scipy - sphinx>=1.8.1,!=2.0.0 - sphinx-copybutton + - sphinx-gallery>=0.10 - pip - pip: - - sphinxcontrib-svg2pdfconverter - # b41e328 is PR 808 which adds the image_srcset directive. When this is - # released with sphinx gallery, we can change to the last release w/o this feature: - # sphinx-gallery>0.9 - - git+git://github.com/sphinx-gallery/sphinx-gallery@b41e328#egg=sphinx-gallery + - sphinxcontrib-svg2pdfconverter # testing - coverage - flake8>=3.8 diff --git a/examples/README b/examples/README deleted file mode 100644 index 84a95a7d66f4..000000000000 --- a/examples/README +++ /dev/null @@ -1,42 +0,0 @@ -Matplotlib examples -=================== - -There are a variety of ways to use Matplotlib, and most of them are -illustrated in the examples in this directory. - -Probably the most common way people use Matplotlib is with the -procedural interface, which follows the MATLAB/IDL/Mathematica approach -of using simple procedures like "plot" or "title" to modify the current -figure. These examples are included in the "pylab_examples" directory. -If you want to write more robust scripts, e.g., for production use or in -a web application server, you will probably want to use the Matplotlib -API for full control. These examples are found in the "api" directory. -Below is a brief description of the different directories found here: - - * animation - Dynamic plots, see the documentation at - https://matplotlib.org/api/animation_api.html - - * axes_grid1 - Examples related to the axes_grid1 toolkit. - - * axisartist - Examples related to the axisartist toolkit. - - * event_handling - How to interact with your figure, mouse presses, - key presses, object picking, etc. - - * misc - Miscellaneous examples. Some demos for loading and working - with record arrays. - - * mplot3d - 3D examples. - - * pylab_examples - The interface to Matplotlib similar to MATLAB. - - * tests - Tests used by Matplotlib developers to check functionality. - (These tests are still sometimes useful, but mostly developers should - use the pytest tests which perform automatic image comparison.) - - * units - Working with unit data and custom types in Matplotlib. - - * user_interfaces - Using Matplotlib in a GUI application, e.g., - Tkinter, wxPython, PyGObject, PyQt widgets. - - * widgets - Examples using interactive widgets. diff --git a/examples/animation/animate_decay.py b/examples/animation/animate_decay.py index 88f25e2d6aaa..c4e8eded4e6e 100644 --- a/examples/animation/animate_decay.py +++ b/examples/animation/animate_decay.py @@ -4,6 +4,7 @@ ===== This example showcases: + - using a generator to drive an animation, - changing axes limits during an animation. """ diff --git a/examples/animation/pause_resume.py b/examples/animation/pause_resume.py index ed20197f6167..0a8c2f7c549a 100644 --- a/examples/animation/pause_resume.py +++ b/examples/animation/pause_resume.py @@ -4,6 +4,7 @@ ================================= This example showcases: + - using the Animation.pause() method to pause an animation. - using the Animation.resume() method to resume an animation. """ diff --git a/examples/axes_grid1/scatter_hist_locatable_axes.py b/examples/axes_grid1/scatter_hist_locatable_axes.py index 30111e876d1c..c605e9e81258 100644 --- a/examples/axes_grid1/scatter_hist_locatable_axes.py +++ b/examples/axes_grid1/scatter_hist_locatable_axes.py @@ -3,16 +3,16 @@ Scatter Histogram (Locatable Axes) ================================== -Show the marginal distributions of a scatter as histograms at the sides of +Show the marginal distributions of a scatter plot as histograms at the sides of the plot. For a nice alignment of the main axes with the marginals, the axes positions -are defined by a ``Divider``, produced via `.make_axes_locatable`. +are defined by a ``Divider``, produced via `.make_axes_locatable`. Note that +the ``Divider`` API allows setting axes sizes and pads in inches, which is its +main feature. -An alternative method to produce a similar figure is shown in the -:doc:`/gallery/lines_bars_and_markers/scatter_hist` example. The advantage of -the locatable axes method shown below is that the marginal axes follow the -fixed aspect ratio of the main axes. +If one wants to set axes sizes and pads relative to the main Figure, see the +:doc:`/gallery/lines_bars_and_markers/scatter_hist` example. """ import numpy as np @@ -65,7 +65,7 @@ ############################################################################# # -## .. admonition:: References +# .. admonition:: References # # The use of the following functions, methods, classes and modules is shown # in this example: diff --git a/examples/event_handling/coords_demo.py b/examples/event_handling/coords_demo.py index 4bfb49518ef5..171ec6317d2c 100644 --- a/examples/event_handling/coords_demo.py +++ b/examples/event_handling/coords_demo.py @@ -18,11 +18,12 @@ def on_move(event): - # get the x and y pixel coords - x, y = event.x, event.y if event.inaxes: + # get the x and y pixel coords + x, y = event.x, event.y ax = event.inaxes # the axes instance - print('data coords %f %f' % (event.xdata, event.ydata)) + print('data coords %f %f, pixel coords %f %f' + % (event.xdata, event.ydata, x, y)) def on_click(event): diff --git a/examples/images_contours_and_fields/colormap_interactive_adjustment.py b/examples/images_contours_and_fields/colormap_interactive_adjustment.py index c1a933878af0..3ab9074fd1b6 100644 --- a/examples/images_contours_and_fields/colormap_interactive_adjustment.py +++ b/examples/images_contours_and_fields/colormap_interactive_adjustment.py @@ -3,90 +3,31 @@ Interactive Adjustment of Colormap Range ======================================== -Demonstration of using colorbar, picker, and event functionality to make an -interactively adjustable colorbar widget. - -Left clicks and drags inside the colorbar axes adjust the high range of the -color scheme. Likewise, right clicks and drags adjust the low range. The -connected AxesImage immediately updates to reflect the change. +Demonstration of how a colorbar can be used to interactively adjust the +range of colormapping on an image. To use the interactive feature, you must +be in either zoom mode (magnifying glass toolbar button) or +pan mode (4-way arrow toolbar button) and click inside the colorbar. + +When zooming, the bounding box of the zoom region defines the new vmin and +vmax of the norm. Zooming using the right mouse button will expand the +vmin and vmax proportionally to the selected region, in the same manner that +one can zoom out on an axis. When panning, the vmin and vmax of the norm are +both shifted according to the direction of movement. The +Home/Back/Forward buttons can also be used to get back to a previous state. .. redirect-from:: /gallery/userdemo/colormap_interactive_adjustment """ - -import numpy as np import matplotlib.pyplot as plt -from matplotlib.backend_bases import MouseButton - -############################################################################### -# Callback definitions - - -def on_pick(event): - adjust_colorbar(event.mouseevent) - - -def on_move(mouseevent): - if mouseevent.inaxes is colorbar.ax: - adjust_colorbar(mouseevent) - - -def adjust_colorbar(mouseevent): - if mouseevent.button == MouseButton.LEFT: - colorbar.norm.vmax = max(mouseevent.ydata, colorbar.norm.vmin) - elif mouseevent.button == MouseButton.RIGHT: - colorbar.norm.vmin = min(mouseevent.ydata, colorbar.norm.vmax) - else: - # discard all others - return - - canvas.draw_idle() - +import numpy as np -############################################################################### -# Generate figure with Axesimage and Colorbar +t = np.linspace(0, 2 * np.pi, 1024) +data2d = np.sin(t)[:, np.newaxis] * np.cos(t)[np.newaxis, :] fig, ax = plt.subplots() -canvas = fig.canvas - -delta = 0.1 -x = np.arange(-3.0, 4.001, delta) -y = np.arange(-4.0, 3.001, delta) -X, Y = np.meshgrid(x, y) -Z1 = np.exp(-X**2 - Y**2) -Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2) -Z = (0.9*Z1 - 0.5*Z2) * 2 - -cmap = plt.colormaps['viridis'].with_extremes( - over='xkcd:orange', under='xkcd:dark red') -axesimage = plt.imshow(Z, cmap=cmap) -colorbar = plt.colorbar(axesimage, ax=ax, use_gridspec=True) - -############################################################################### -# Note that axesimage and colorbar share a Normalize object -# so they will stay in sync - -assert colorbar.norm is axesimage.norm -colorbar.norm.vmax = 1.5 -axesimage.norm.vmin = -0.75 - -############################################################################### -# Hook Colorbar up to canvas events - -# `set_navigate` helps you see what value you are about to set the range -# to, and enables zoom and pan in the colorbar which can be helpful for -# narrow or wide data ranges -colorbar.ax.set_navigate(True) - -# React to all motion with left or right mouse buttons held -canvas.mpl_connect("motion_notify_event", on_move) - -# React only to left and right clicks -colorbar.ax.set_picker(True) -canvas.mpl_connect("pick_event", on_pick) +im = ax.imshow(data2d) +ax.set_title('Pan on the colorbar to shift the color mapping\n' + 'Zoom on the colorbar to scale the color mapping') -############################################################################### -# Display -# -# The colormap will now respond to left and right clicks in the Colorbar axes +fig.colorbar(im, ax=ax, label='Interactive colorbar') plt.show() diff --git a/examples/lines_bars_and_markers/fill_between_alpha.py b/examples/lines_bars_and_markers/fill_between_alpha.py index acd6cebbbd7f..30e7f6be3dcd 100644 --- a/examples/lines_bars_and_markers/fill_between_alpha.py +++ b/examples/lines_bars_and_markers/fill_between_alpha.py @@ -17,9 +17,6 @@ import matplotlib.cbook as cbook -# Fixing random state for reproducibility -np.random.seed(19680801) - # load up some sample financial data r = (cbook.get_sample_data('goog.npz', np_load=True)['price_data'] .view(np.recarray)) @@ -29,7 +26,7 @@ pricemin = r.close.min() ax1.plot(r.date, r.close, lw=2) -ax2.fill_between(r.date, pricemin, r.close, facecolor='blue', alpha=0.5) +ax2.fill_between(r.date, pricemin, r.close, alpha=0.7) for ax in ax1, ax2: ax.grid(True) @@ -52,16 +49,19 @@ # # Our next example computes two populations of random walkers with a # different mean and standard deviation of the normal distributions from -# which the steps are drawn. We use shared regions to plot +/- one +# which the steps are drawn. We use filled regions to plot +/- one # standard deviation of the mean position of the population. Here the # alpha channel is useful, not just aesthetic. +# Fixing random state for reproducibility +np.random.seed(19680801) + Nsteps, Nwalkers = 100, 250 t = np.arange(Nsteps) # an (Nsteps x Nwalkers) array of random walk steps -S1 = 0.002 + 0.01*np.random.randn(Nsteps, Nwalkers) -S2 = 0.004 + 0.02*np.random.randn(Nsteps, Nwalkers) +S1 = 0.004 + 0.02*np.random.randn(Nsteps, Nwalkers) +S2 = 0.002 + 0.01*np.random.randn(Nsteps, Nwalkers) # an (Nsteps x Nwalkers) array of random walker positions X1 = S1.cumsum(axis=0) @@ -77,10 +77,10 @@ # plot it! fig, ax = plt.subplots(1) -ax.plot(t, mu1, lw=2, label='mean population 1', color='blue') -ax.plot(t, mu2, lw=2, label='mean population 2', color='yellow') -ax.fill_between(t, mu1+sigma1, mu1-sigma1, facecolor='blue', alpha=0.5) -ax.fill_between(t, mu2+sigma2, mu2-sigma2, facecolor='yellow', alpha=0.5) +ax.plot(t, mu1, lw=2, label='mean population 1') +ax.plot(t, mu2, lw=2, label='mean population 2') +ax.fill_between(t, mu1+sigma1, mu1-sigma1, facecolor='C0', alpha=0.4) +ax.fill_between(t, mu2+sigma2, mu2-sigma2, facecolor='C1', alpha=0.4) ax.set_title(r'random walkers empirical $\mu$ and $\pm \sigma$ interval') ax.legend(loc='upper left') ax.set_xlabel('num steps') @@ -93,11 +93,14 @@ # as the x, ymin and ymax arguments, and only fills in the region where # the boolean mask is True. In the example below, we simulate a single # random walker and compute the analytic mean and standard deviation of -# the population positions. The population mean is shown as the black -# dashed line, and the plus/minus one sigma deviation from the mean is -# shown as the yellow filled region. We use the where mask -# ``X > upper_bound`` to find the region where the walker is above the one -# sigma boundary, and shade that region blue. +# the population positions. The population mean is shown as the dashed +# line, and the plus/minus one sigma deviation from the mean is shown +# as the filled region. We use the where mask ``X > upper_bound`` to +# find the region where the walker is outside the one sigma boundary, +# and shade that region red. + +# Fixing random state for reproducibility +np.random.seed(1) Nsteps = 500 t = np.arange(Nsteps) @@ -114,16 +117,16 @@ upper_bound = mu*t + sigma*np.sqrt(t) fig, ax = plt.subplots(1) -ax.plot(t, X, lw=2, label='walker position', color='blue') -ax.plot(t, mu*t, lw=1, label='population mean', color='black', ls='--') -ax.fill_between(t, lower_bound, upper_bound, facecolor='yellow', alpha=0.5, +ax.plot(t, X, lw=2, label='walker position') +ax.plot(t, mu*t, lw=1, label='population mean', color='C0', ls='--') +ax.fill_between(t, lower_bound, upper_bound, facecolor='C0', alpha=0.4, label='1 sigma range') ax.legend(loc='upper left') # here we use the where argument to only fill the region where the # walker is above the population 1 sigma boundary -ax.fill_between(t, upper_bound, X, where=X > upper_bound, facecolor='blue', - alpha=0.5) +ax.fill_between(t, upper_bound, X, where=X > upper_bound, fc='red', alpha=0.4) +ax.fill_between(t, lower_bound, X, where=X < lower_bound, fc='red', alpha=0.4) ax.set_xlabel('num steps') ax.set_ylabel('position') ax.grid() diff --git a/examples/lines_bars_and_markers/filled_step.py b/examples/lines_bars_and_markers/filled_step.py index acd4823b4f46..a4185366b7a2 100644 --- a/examples/lines_bars_and_markers/filled_step.py +++ b/examples/lines_bars_and_markers/filled_step.py @@ -53,6 +53,7 @@ def filled_hist(ax, edges, values, bottoms=None, orientation='v', "not {o}".format(o=orientation)) kwargs.setdefault('step', 'post') + kwargs.setdefault('alpha', 0.7) edges = np.asarray(edges) values = np.asarray(values) if len(edges) - 1 != len(values): diff --git a/examples/lines_bars_and_markers/gradient_bar.py b/examples/lines_bars_and_markers/gradient_bar.py index 70998fe138a7..2441f4d00744 100644 --- a/examples/lines_bars_and_markers/gradient_bar.py +++ b/examples/lines_bars_and_markers/gradient_bar.py @@ -70,8 +70,8 @@ def gradient_bar(ax, x, y, width=0.5, bottom=0): ax.set(xlim=xlim, ylim=ylim, autoscale_on=False) # background image -gradient_image(ax, direction=0, extent=(0, 1, 0, 1), transform=ax.transAxes, - cmap=plt.cm.Oranges, cmap_range=(0.1, 0.6)) +gradient_image(ax, direction=1, extent=(0, 1, 0, 1), transform=ax.transAxes, + cmap=plt.cm.RdYlGn, cmap_range=(0.2, 0.8), alpha=0.5) N = 10 x = np.arange(N) + 0.15 diff --git a/examples/lines_bars_and_markers/marker_reference.py b/examples/lines_bars_and_markers/marker_reference.py index 98af2519124c..c4564d2b027d 100644 --- a/examples/lines_bars_and_markers/marker_reference.py +++ b/examples/lines_bars_and_markers/marker_reference.py @@ -19,8 +19,10 @@ .. redirect-from:: /gallery/shapes_and_collections/marker_path """ +from matplotlib.markers import MarkerStyle import matplotlib.pyplot as plt from matplotlib.lines import Line2D +from matplotlib.transforms import Affine2D text_style = dict(horizontalalignment='right', verticalalignment='center', @@ -159,3 +161,96 @@ def split_list(a_list): format_axes(ax) plt.show() + +############################################################################### +# Advanced marker modifications with transform +# ============================================ +# +# Markers can be modified by passing a transform to the MarkerStyle +# constructor. Following example shows how a supplied rotation is applied to +# several marker shapes. + +common_style = {k: v for k, v in filled_marker_style.items() if k != 'marker'} +angles = [0, 10, 20, 30, 45, 60, 90] + +fig, ax = plt.subplots() +fig.suptitle('Rotated markers', fontsize=14) + +ax.text(-0.5, 0, 'Filled marker', **text_style) +for x, theta in enumerate(angles): + t = Affine2D().rotate_deg(theta) + ax.plot(x, 0, marker=MarkerStyle('o', 'left', t), **common_style) + +ax.text(-0.5, 1, 'Un-filled marker', **text_style) +for x, theta in enumerate(angles): + t = Affine2D().rotate_deg(theta) + ax.plot(x, 1, marker=MarkerStyle('1', 'left', t), **common_style) + +ax.text(-0.5, 2, 'Equation marker', **text_style) +for x, theta in enumerate(angles): + t = Affine2D().rotate_deg(theta) + eq = r'$\frac{1}{x}$' + ax.plot(x, 2, marker=MarkerStyle(eq, 'left', t), **common_style) + +for x, theta in enumerate(angles): + ax.text(x, 2.5, f"{theta}°", horizontalalignment="center") +format_axes(ax) + +fig.tight_layout() +plt.show() + +############################################################################### +# Setting marker cap style and join style +# ======================================= +# +# Markers have default cap and join styles, but these can be +# customized when creating a MarkerStyle. + +from matplotlib.markers import JoinStyle, CapStyle + +marker_inner = dict(markersize=35, + markerfacecolor='tab:blue', + markerfacecoloralt='lightsteelblue', + markeredgecolor='brown', + markeredgewidth=8, + ) + +marker_outer = dict(markersize=35, + markerfacecolor='tab:blue', + markerfacecoloralt='lightsteelblue', + markeredgecolor='white', + markeredgewidth=1, + ) + +fig, ax = plt.subplots() +fig.suptitle('Marker CapStyle', fontsize=14) +fig.subplots_adjust(left=0.1) + +for y, cap_style in enumerate(CapStyle): + ax.text(-0.5, y, cap_style.name, **text_style) + for x, theta in enumerate(angles): + t = Affine2D().rotate_deg(theta) + m = MarkerStyle('1', transform=t, capstyle=cap_style) + ax.plot(x, y, marker=m, **marker_inner) + ax.plot(x, y, marker=m, **marker_outer) + ax.text(x, len(CapStyle) - .5, f'{theta}°', ha='center') +format_axes(ax) +plt.show() + +############################################################################### +# Modifying the join style: + +fig, ax = plt.subplots() +fig.suptitle('Marker JoinStyle', fontsize=14) +fig.subplots_adjust(left=0.05) + +for y, join_style in enumerate(JoinStyle): + ax.text(-0.5, y, join_style.name, **text_style) + for x, theta in enumerate(angles): + t = Affine2D().rotate_deg(theta) + m = MarkerStyle('*', transform=t, joinstyle=join_style) + ax.plot(x, y, marker=m, **marker_inner) + ax.text(x, len(JoinStyle) - .5, f'{theta}°', ha='center') +format_axes(ax) + +plt.show() diff --git a/examples/lines_bars_and_markers/multivariate_marker_plot.py b/examples/lines_bars_and_markers/multivariate_marker_plot.py new file mode 100644 index 000000000000..487bb2b85fd3 --- /dev/null +++ b/examples/lines_bars_and_markers/multivariate_marker_plot.py @@ -0,0 +1,46 @@ +""" +============================================== +Mapping marker properties to multivariate data +============================================== + +This example shows how to use different properties of markers to plot +multivariate datasets. Here we represent a successful baseball throw as a +smiley face with marker size mapped to the skill of thrower, marker rotation to +the take-off angle, and thrust to the marker color. +""" + +import numpy as np +import matplotlib.pyplot as plt +from matplotlib.markers import MarkerStyle +from matplotlib.transforms import Affine2D +from matplotlib.textpath import TextPath +from matplotlib.colors import Normalize + +SUCCESS_SYMBOLS = [ + TextPath((0, 0), "☹"), + TextPath((0, 0), "😒"), + TextPath((0, 0), "☺"), +] + +N = 25 +np.random.seed(42) +skills = np.random.uniform(5, 80, size=N) * 0.1 + 5 +takeoff_angles = np.random.normal(0, 90, N) +thrusts = np.random.uniform(size=N) +successfull = np.random.randint(0, 3, size=N) +positions = np.random.normal(size=(N, 2)) * 5 +data = zip(skills, takeoff_angles, thrusts, successfull, positions) + +cmap = plt.cm.get_cmap("plasma") +fig, ax = plt.subplots() +fig.suptitle("Throwing success", size=14) +for skill, takeoff, thrust, mood, pos in data: + t = Affine2D().scale(skill).rotate_deg(takeoff) + m = MarkerStyle(SUCCESS_SYMBOLS[mood], transform=t) + ax.plot(pos[0], pos[1], marker=m, color=cmap(thrust)) +fig.colorbar(plt.cm.ScalarMappable(norm=Normalize(0, 1), cmap=cmap), + ax=ax, label="Normalized Thrust [a.u.]") +ax.set_xlabel("X position [m]") +ax.set_ylabel("Y position [m]") + +plt.show() diff --git a/examples/lines_bars_and_markers/scatter_hist.py b/examples/lines_bars_and_markers/scatter_hist.py index 1ad5d8ac1c8b..1e18932a76db 100644 --- a/examples/lines_bars_and_markers/scatter_hist.py +++ b/examples/lines_bars_and_markers/scatter_hist.py @@ -3,18 +3,22 @@ Scatter plot with histograms ============================ -Show the marginal distributions of a scatter as histograms at the sides of +Show the marginal distributions of a scatter plot as histograms at the sides of the plot. For a nice alignment of the main axes with the marginals, two options are shown -below. +below: -* the axes positions are defined in terms of rectangles in figure coordinates -* the axes positions are defined via a gridspec +.. contents:: + :local: + +While `.Axes.inset_axes` may be a bit more complex, it allows correct handling +of main axes with a fixed aspect ratio. An alternative method to produce a similar figure using the ``axes_grid1`` -toolkit is shown in the -:doc:`/gallery/axes_grid1/scatter_hist_locatable_axes` example. +toolkit is shown in the :doc:`/gallery/axes_grid1/scatter_hist_locatable_axes` +example. Finally, it is also possible to position all axes in absolute +coordinates using `.Figure.add_axes` (not shown here). Let us first define a function that takes x and y data as input, as well as three axes, the main axes for the scatter, and two marginal axes. It will @@ -52,60 +56,53 @@ def scatter_hist(x, y, ax, ax_histx, ax_histy): ############################################################################# # -# Axes in figure coordinates -# -------------------------- -# -# To define the axes positions, `.Figure.add_axes` is provided with a rectangle -# ``[left, bottom, width, height]`` in figure coordinates. The marginal axes -# share one dimension with the main axes. - -# definitions for the axes -left, width = 0.1, 0.65 -bottom, height = 0.1, 0.65 -spacing = 0.005 - - -rect_scatter = [left, bottom, width, height] -rect_histx = [left, bottom + height + spacing, width, 0.2] -rect_histy = [left + width + spacing, bottom, 0.2, height] - -# start with a square Figure -fig = plt.figure(figsize=(8, 8)) - -ax = fig.add_axes(rect_scatter) -ax_histx = fig.add_axes(rect_histx, sharex=ax) -ax_histy = fig.add_axes(rect_histy, sharey=ax) - -# use the previously defined function -scatter_hist(x, y, ax, ax_histx, ax_histy) - -plt.show() - - -############################################################################# -# -# Using a gridspec -# ---------------- +# Defining the axes positions using a gridspec +# -------------------------------------------- # -# We may equally define a gridspec with unequal width- and height-ratios to -# achieve desired layout. Also see the :doc:`/tutorials/intermediate/gridspec` -# tutorial. - -# start with a square Figure -fig = plt.figure(figsize=(8, 8)) +# We define a gridspec with unequal width- and height-ratios to achieve desired +# layout. Also see the :doc:`/tutorials/intermediate/gridspec` tutorial. -# Add a gridspec with two rows and two columns and a ratio of 2 to 7 between +# Start with a square Figure. +fig = plt.figure(figsize=(6, 6)) +# Add a gridspec with two rows and two columns and a ratio of 1 to 4 between # the size of the marginal axes and the main axes in both directions. # Also adjust the subplot parameters for a square plot. -gs = fig.add_gridspec(2, 2, width_ratios=(7, 2), height_ratios=(2, 7), +gs = fig.add_gridspec(2, 2, width_ratios=(4, 1), height_ratios=(1, 4), left=0.1, right=0.9, bottom=0.1, top=0.9, wspace=0.05, hspace=0.05) - +# Create the Axes. ax = fig.add_subplot(gs[1, 0]) ax_histx = fig.add_subplot(gs[0, 0], sharex=ax) ax_histy = fig.add_subplot(gs[1, 1], sharey=ax) +# Draw the scatter plot and marginals. +scatter_hist(x, y, ax, ax_histx, ax_histy) + -# use the previously defined function +############################################################################# +# +# Defining the axes positions using inset_axes +# -------------------------------------------- +# +# `~.Axes.inset_axes` can be used to position marginals *outside* the main +# axes. The advantage of doing so is that the aspect ratio of the main axes +# can be fixed, and the marginals will always be drawn relative to the position +# of the axes. + +# Create a Figure, which doesn't have to be square. +fig = plt.figure(constrained_layout=True) +# Create the main axes, leaving 25% of the figure space at the top and on the +# right to position marginals. +ax = fig.add_gridspec(top=0.75, right=0.75).subplots() +# The main axes' aspect can be fixed. +ax.set(aspect=1) +# Create marginal axes, which have 25% of the size of the main axes. Note that +# the inset axes are positioned *outside* (on the right and the top) of the +# main axes, by specifying axes coordinates greater than 1. Axes coordinates +# less than 0 would likewise specify positions on the left and the bottom of +# the main axes. +ax_histx = ax.inset_axes([0, 1.05, 1, 0.25], sharex=ax) +ax_histy = ax.inset_axes([1.05, 0, 0.25, 1], sharey=ax) +# Draw the scatter plot and marginals. scatter_hist(x, y, ax, ax_histx, ax_histy) plt.show() @@ -118,8 +115,8 @@ def scatter_hist(x, y, ax, ax_histx, ax_histy): # The use of the following functions, methods, classes and modules is shown # in this example: # -# - `matplotlib.figure.Figure.add_axes` # - `matplotlib.figure.Figure.add_subplot` # - `matplotlib.figure.Figure.add_gridspec` +# - `matplotlib.axes.Axes.inset_axes` # - `matplotlib.axes.Axes.scatter` # - `matplotlib.axes.Axes.hist` diff --git a/examples/lines_bars_and_markers/stackplot_demo.py b/examples/lines_bars_and_markers/stackplot_demo.py index 6c9acfcacc34..142b3d2a0ce0 100644 --- a/examples/lines_bars_and_markers/stackplot_demo.py +++ b/examples/lines_bars_and_markers/stackplot_demo.py @@ -29,7 +29,7 @@ fig, ax = plt.subplots() ax.stackplot(year, population_by_continent.values(), - labels=population_by_continent.keys()) + labels=population_by_continent.keys(), alpha=0.8) ax.legend(loc='upper left') ax.set_title('World population') ax.set_xlabel('Year') diff --git a/examples/misc/demo_agg_filter.py b/examples/misc/demo_agg_filter.py index 45cd8bcd9620..f3eb95e520fd 100644 --- a/examples/misc/demo_agg_filter.py +++ b/examples/misc/demo_agg_filter.py @@ -7,7 +7,7 @@ rendering. You can modify the rendering of Artists by applying a filter via `.Artist.set_agg_filter`. -.. _Anti-Grain Geometry (AGG): http://antigrain.com +.. _Anti-Grain Geometry (AGG): http://agg.sourceforge.net/antigrain.com """ import matplotlib.cm as cm diff --git a/examples/subplots_axes_and_figures/figure_size_units.py b/examples/subplots_axes_and_figures/figure_size_units.py index 82063ad4577d..94de72c1554c 100644 --- a/examples/subplots_axes_and_figures/figure_size_units.py +++ b/examples/subplots_axes_and_figures/figure_size_units.py @@ -56,15 +56,15 @@ # tedious for quick iterations. # # Because of the default ``rcParams['figure.dpi'] = 100``, one can mentally -# divide the needed pixel value by 100 [*]_: +# divide the needed pixel value by 100 [#]_: # plt.subplots(figsize=(6, 2)) plt.text(0.5, 0.5, '600px x 200px', **text_kwargs) plt.show() ############################################################################# -# .. [*] Unfortunately, this does not work well for the ``matplotlib inline`` -# backend in jupyter because that backend uses a different default of +# .. [#] Unfortunately, this does not work well for the ``matplotlib inline`` +# backend in Jupyter because that backend uses a different default of # ``rcParams['figure.dpi'] = 72``. Additionally, it saves the figure # with ``bbox_inches='tight'``, which crops the figure and makes the # actual size unpredictable. diff --git a/lib/matplotlib/artist.py b/lib/matplotlib/artist.py index 179badd3598e..0f2c887fcadc 100644 --- a/lib/matplotlib/artist.py +++ b/lib/matplotlib/artist.py @@ -302,7 +302,7 @@ def stale(self, val): def get_window_extent(self, renderer): """ - Get the axes bounding box in display space. + Get the artist's bounding box in display space. The bounding box' width and height are nonnegative. @@ -1016,7 +1016,7 @@ def set_animated(self, b): If True, the artist is excluded from regular drawing of the figure. You have to call `.Figure.draw_artist` / `.Axes.draw_artist` - explicitly on the artist. This appoach is used to speed up animations + explicitly on the artist. This approach is used to speed up animations using blitting. See also `matplotlib.animation` and diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 4eca2e6b0bdb..91badadf088e 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -3,6 +3,7 @@ import logging import math from numbers import Integral, Number +from datetime import timedelta import numpy as np from numpy import ma @@ -3180,7 +3181,7 @@ def errorbar(self, x, y, yerr=None, xerr=None, errors. - *None*: No errorbar. - Note that all error arrays should have *positive* values. + All values must be >= 0. See :doc:`/gallery/statistics/errorbar_features` for an example on the usage of ``xerr`` and ``yerr``. @@ -3284,6 +3285,19 @@ def errorbar(self, x, y, yerr=None, xerr=None, if len(x) != len(y): raise ValueError("'x' and 'y' must have the same size") + def has_negative_values(array): + if array is None: + return False + try: + return np.any(array < 0) + except TypeError: # if array contains 'datetime.timedelta' types + return np.any(array < timedelta(0)) + + if has_negative_values(xerr): + raise ValueError("'xerr' must not contain negative values") + if has_negative_values(yerr): + raise ValueError("'yerr' must not contain negative values") + if isinstance(errorevery, Integral): errorevery = (0, errorevery) if isinstance(errorevery, tuple): @@ -5629,9 +5643,8 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None, expanded as needed into the appropriate 2D arrays, making a rectangular grid. - shading : {'flat', 'nearest', 'auto'}, optional - The fill style for the quadrilateral; defaults to 'flat' or - :rc:`pcolor.shading`. Possible values: + shading : {'flat', 'nearest', 'auto'}, default: :rc:`pcolor.shading` + The fill style for the quadrilateral. Possible values: - 'flat': A solid color is used for each quad. The color of the quad (i, j), (i+1, j), (i, j+1), (i+1, j+1) is given by @@ -6016,9 +6029,10 @@ def pcolormesh(self, *args, alpha=None, norm=None, cmap=None, vmin=None, # convert to one dimensional array C = C.ravel() - snap = kwargs.get('snap', rcParams['pcolormesh.snap']) + kwargs.setdefault('snap', rcParams['pcolormesh.snap']) + collection = mcoll.QuadMesh( - coords, antialiased=antialiased, shading=shading, snap=snap, + coords, antialiased=antialiased, shading=shading, array=C, cmap=cmap, norm=norm, alpha=alpha, **kwargs) collection._scale_norm(norm, vmin, vmax) self._pcolor_grid_deprecation_helper() diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 9bb78cf0cdbc..38d408ba02c6 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -69,6 +69,7 @@ 'svgz': 'Scalable Vector Graphics', 'tif': 'Tagged Image File Format', 'tiff': 'Tagged Image File Format', + 'webp': 'WebP Image Format', } _default_backends = { 'eps': 'matplotlib.backends.backend_ps', @@ -84,6 +85,7 @@ 'svgz': 'matplotlib.backends.backend_svg', 'tif': 'matplotlib.backends.backend_agg', 'tiff': 'matplotlib.backends.backend_agg', + 'webp': 'matplotlib.backends.backend_agg', } diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index 4a62f5a921cd..fc495333ddee 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -1,5 +1,5 @@ """ -An `Anti-Grain Geometry `_ (AGG) backend. +An `Anti-Grain Geometry`_ (AGG) backend. Features that are implemented: @@ -17,6 +17,8 @@ Still TODO: * integrate screen dpi w/ ppi and text + +.. _Anti-Grain Geometry: http://agg.sourceforge.net/antigrain.com """ try: @@ -485,6 +487,16 @@ def print_raw(self, filename_or_obj, *args): print_rgba = print_raw + def _print_pil(self, filename_or_obj, fmt, pil_kwargs, metadata=None): + """ + Draw the canvas, then save it using `.image.imsave` (to which + *pil_kwargs* and *metadata* are forwarded). + """ + FigureCanvasAgg.draw(self) + mpl.image.imsave( + filename_or_obj, self.buffer_rgba(), format=fmt, origin="upper", + dpi=self.figure.dpi, metadata=metadata, pil_kwargs=pil_kwargs) + @_check_savefig_extra_args @_api.delete_parameter("3.5", "args") def print_png(self, filename_or_obj, *args, @@ -535,10 +547,7 @@ def print_png(self, filename_or_obj, *args, If the 'pnginfo' key is present, it completely overrides *metadata*, including the default 'Software' key. """ - FigureCanvasAgg.draw(self) - mpl.image.imsave( - filename_or_obj, self.buffer_rgba(), format="png", origin="upper", - dpi=self.figure.dpi, metadata=metadata, pil_kwargs=pil_kwargs) + self._print_pil(filename_or_obj, "png", pil_kwargs, metadata) def print_to_buffer(self): FigureCanvasAgg.draw(self) @@ -553,47 +562,39 @@ def print_to_buffer(self): @_check_savefig_extra_args() @_api.delete_parameter("3.5", "args") def print_jpg(self, filename_or_obj, *args, pil_kwargs=None, **kwargs): - """ - Write the figure to a JPEG file. - - Parameters - ---------- - filename_or_obj : str or path-like or file-like - The file to write to. - - Other Parameters - ---------------- - pil_kwargs : dict, optional - Additional keyword arguments that are passed to - `PIL.Image.Image.save` when saving the figure. - """ # Remove transparency by alpha-blending on an assumed white background. r, g, b, a = mcolors.to_rgba(self.figure.get_facecolor()) try: self.figure.set_facecolor(a * np.array([r, g, b]) + 1 - a) - FigureCanvasAgg.draw(self) + self._print_pil(filename_or_obj, "jpeg", pil_kwargs) finally: self.figure.set_facecolor((r, g, b, a)) - if pil_kwargs is None: - pil_kwargs = {} - pil_kwargs.setdefault("dpi", (self.figure.dpi, self.figure.dpi)) - # Drop alpha channel now. - return (Image.fromarray(np.asarray(self.buffer_rgba())[..., :3]) - .save(filename_or_obj, format='jpeg', **pil_kwargs)) print_jpeg = print_jpg @_check_savefig_extra_args def print_tif(self, filename_or_obj, *, pil_kwargs=None): - FigureCanvasAgg.draw(self) - if pil_kwargs is None: - pil_kwargs = {} - pil_kwargs.setdefault("dpi", (self.figure.dpi, self.figure.dpi)) - return (Image.fromarray(np.asarray(self.buffer_rgba())) - .save(filename_or_obj, format='tiff', **pil_kwargs)) + self._print_pil(filename_or_obj, "tiff", pil_kwargs) print_tiff = print_tif + @_check_savefig_extra_args + def print_webp(self, filename_or_obj, *, pil_kwargs=None): + self._print_pil(filename_or_obj, "webp", pil_kwargs) + + print_jpg.__doc__, print_tif.__doc__, print_webp.__doc__ = map( + """ + Write the figure to a {} file. + + Parameters + ---------- + filename_or_obj : str or path-like or file-like + The file to write to. + pil_kwargs : dict, optional + Additional keyword arguments that are passed to + `PIL.Image.Image.save` when saving the figure. + """.format, ["JPEG", "TIFF", "WebP"]) + @_Backend.export class _BackendAgg(_Backend): diff --git a/lib/matplotlib/backends/backend_gtk4.py b/lib/matplotlib/backends/backend_gtk4.py index 30719b1bba0f..6ddcaaa18bbb 100644 --- a/lib/matplotlib/backends/backend_gtk4.py +++ b/lib/matplotlib/backends/backend_gtk4.py @@ -51,6 +51,7 @@ def _mpl_to_gtk_cursor(mpl_cursor): class FigureCanvasGTK4(Gtk.DrawingArea, FigureCanvasBase): required_interactive_framework = "gtk4" + supports_blit = False _timer_cls = TimerGTK4 _context_is_scaled = False diff --git a/lib/matplotlib/backends/backend_gtk4agg.py b/lib/matplotlib/backends/backend_gtk4agg.py index d47dd07fee3b..58f085e85d8f 100644 --- a/lib/matplotlib/backends/backend_gtk4agg.py +++ b/lib/matplotlib/backends/backend_gtk4agg.py @@ -8,67 +8,35 @@ from . import backend_agg, backend_gtk4 from .backend_cairo import cairo from .backend_gtk4 import Gtk, _BackendGTK4 -from matplotlib import transforms class FigureCanvasGTK4Agg(backend_gtk4.FigureCanvasGTK4, backend_agg.FigureCanvasAgg): def __init__(self, figure): backend_gtk4.FigureCanvasGTK4.__init__(self, figure) - self._bbox_queue = [] def on_draw_event(self, widget, ctx): scale = self.device_pixel_ratio allocation = self.get_allocation() - w = allocation.width * scale - h = allocation.height * scale - if not len(self._bbox_queue): - Gtk.render_background( - self.get_style_context(), ctx, - allocation.x, allocation.y, - allocation.width, allocation.height) - bbox_queue = [transforms.Bbox([[0, 0], [w, h]])] - else: - bbox_queue = self._bbox_queue + Gtk.render_background( + self.get_style_context(), ctx, + allocation.x, allocation.y, + allocation.width, allocation.height) ctx = backend_cairo._to_context(ctx) - for bbox in bbox_queue: - x = int(bbox.x0) - y = h - int(bbox.y1) - width = int(bbox.x1) - int(bbox.x0) - height = int(bbox.y1) - int(bbox.y0) - - buf = cbook._unmultiplied_rgba8888_to_premultiplied_argb32( - np.asarray(self.copy_from_bbox(bbox))) - image = cairo.ImageSurface.create_for_data( - buf.ravel().data, cairo.FORMAT_ARGB32, width, height) - image.set_device_scale(scale, scale) - ctx.set_source_surface(image, x / scale, y / scale) - ctx.paint() - - if len(self._bbox_queue): - self._bbox_queue = [] + buf = cbook._unmultiplied_rgba8888_to_premultiplied_argb32( + np.asarray(self.renderer.buffer_rgba())) + height, width, _ = buf.shape + image = cairo.ImageSurface.create_for_data( + buf.ravel().data, cairo.FORMAT_ARGB32, width, height) + image.set_device_scale(scale, scale) + ctx.set_source_surface(image, 0, 0) + ctx.paint() return False - def blit(self, bbox=None): - # If bbox is None, blit the entire canvas to gtk. Otherwise - # blit only the area defined by the bbox. - if bbox is None: - bbox = self.figure.bbox - - scale = self.device_pixel_ratio - allocation = self.get_allocation() - x = int(bbox.x0 / scale) - y = allocation.height - int(bbox.y1 / scale) - width = (int(bbox.x1) - int(bbox.x0)) // scale - height = (int(bbox.y1) - int(bbox.y0)) // scale - - self._bbox_queue.append(bbox) - self.queue_draw_area(x, y, width, height) - def draw(self): backend_agg.FigureCanvasAgg.draw(self) super().draw() diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index bd0c370f1cba..29838c35316c 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -887,7 +887,7 @@ def dviFontName(self, dvifont): if dvi_info is not None: return dvi_info.pdfname - tex_font_map = dviread.PsfontsMap(dviread.find_tex_file('pdftex.map')) + tex_font_map = dviread.PsfontsMap(dviread._find_tex_file('pdftex.map')) psfont = tex_font_map[dvifont.texname] if psfont.filename is None: raise ValueError( diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index 2fa8c3251b12..b5eec48d922f 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -801,6 +801,10 @@ def _print_pgf_to_fh(self, fh, *, bbox_inches_restore=None): %% Make sure the required packages are loaded in your preamble %% \\usepackage{pgf} %% +%% Also ensure that all the required font packages are loaded; for instance, +%% the lmodern package is sometimes necessary when using math font. +%% \\usepackage{lmodern} +%% %% Figures using additional raster images can only be included by \\input if %% they are in the same directory as the main LaTeX file. For loading figures %% from other directories you can use the `import` package diff --git a/lib/matplotlib/backends/backend_qt.py b/lib/matplotlib/backends/backend_qt.py index ba8eb02becf8..6bb1f82c0f24 100644 --- a/lib/matplotlib/backends/backend_qt.py +++ b/lib/matplotlib/backends/backend_qt.py @@ -312,8 +312,10 @@ def mouseReleaseEvent(self, event): def wheelEvent(self, event): x, y = self.mouseEventCoords(self._get_position(event)) - # from QWheelEvent::delta doc - if event.pixelDelta().x() == 0 and event.pixelDelta().y() == 0: + # from QWheelEvent::pixelDelta doc: pixelDelta is sometimes not + # provided (`isNull()`) and is unreliable on X11 ("xcb"). + if (event.pixelDelta().isNull() + or QtWidgets.QApplication.instance().platformName() == "xcb"): steps = event.angleDelta().y() / 120 else: steps = event.pixelDelta().y() diff --git a/lib/matplotlib/backends/qt_compat.py b/lib/matplotlib/backends/qt_compat.py index 6fbe103ce03b..9e320e341c4c 100644 --- a/lib/matplotlib/backends/qt_compat.py +++ b/lib/matplotlib/backends/qt_compat.py @@ -228,8 +228,20 @@ def _maybe_allow_interrupt(qapp): rsock.fileno(), _enum('QtCore.QSocketNotifier.Type').Read ) + # We do not actually care about this value other than running some + # Python code to ensure that the interpreter has a chance to handle the + # signal in Python land. We also need to drain the socket because it + # will be written to as part of the wakeup! There are some cases where + # this may fire too soon / more than once on Windows so we should be + # forgiving about reading an empty socket. + rsock.setblocking(False) # Clear the socket to re-arm the notifier. - sn.activated.connect(lambda *args: rsock.recv(1)) + @sn.activated.connect + def _may_clear_sock(*args): + try: + rsock.recv(1) + except BlockingIOError: + pass def handle(*args): nonlocal handler_args diff --git a/lib/matplotlib/cbook/__init__.py b/lib/matplotlib/cbook/__init__.py index 9d0fcadc431c..b068fbb1d84a 100644 --- a/lib/matplotlib/cbook/__init__.py +++ b/lib/matplotlib/cbook/__init__.py @@ -2212,6 +2212,10 @@ def _g_sig_digits(value, delta): Return the number of significant digits to %g-format *value*, assuming that it is known with an error of *delta*. """ + if delta == 0: + # delta = 0 may occur when trying to format values over a tiny range; + # in that case, replace it by the distance to the closest float. + delta = np.spacing(value) # If e.g. value = 45.67 and delta = 0.02, then we want to round to 2 digits # after the decimal point (floor(log10(0.02)) = -2); 45.67 contributes 2 # digits before the decimal point (floor(log10(45.67)) + 1 = 2): the total diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index f5081825c2ab..b5f66bc33224 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -633,24 +633,20 @@ def __call__(self, X, alpha=None, bytes=False): xa[xa < 0] = self._i_under xa[mask_bad] = self._i_bad + lut = self._lut if bytes: - lut = (self._lut * 255).astype(np.uint8) - else: - lut = self._lut.copy() # Don't let alpha modify original _lut. + lut = (lut * 255).astype(np.uint8) - rgba = np.empty(shape=xa.shape + (4,), dtype=lut.dtype) - lut.take(xa, axis=0, mode='clip', out=rgba) + rgba = lut.take(xa, axis=0, mode='clip') if alpha is not None: - if np.iterable(alpha): - alpha = np.asarray(alpha) - if alpha.shape != xa.shape: - raise ValueError("alpha is array-like but its shape" - " %s doesn't match that of X %s" % - (alpha.shape, xa.shape)) alpha = np.clip(alpha, 0, 1) if bytes: - alpha = (alpha * 255).astype(np.uint8) + alpha *= 255 # Will be cast to uint8 upon assignment. + if alpha.shape not in [(), xa.shape]: + raise ValueError( + f"alpha is array-like but its shape {alpha.shape} does " + f"not match that of X {xa.shape}") rgba[..., -1] = alpha # If the "bad" color is all zeros, then ignore alpha input. diff --git a/lib/matplotlib/contour.py b/lib/matplotlib/contour.py index 26ce26d43105..9f1307c44791 100644 --- a/lib/matplotlib/contour.py +++ b/lib/matplotlib/contour.py @@ -662,10 +662,9 @@ def _find_closest_point_on_path(xys, p): ax : `~matplotlib.axes.Axes` The Axes object in which the contours are drawn. -collections : `.silent_list` of `.LineCollection`\s or `.PathCollection`\s +collections : `.silent_list` of `.PathCollection`\s The `.Artist`\s representing the contour. This is a list of - `.LineCollection`\s for line contours and a list of `.PathCollection`\s - for filled contours. + `.PathCollection`\s for both line and filled contours. levels : array The values of the contour levels. diff --git a/lib/matplotlib/dviread.py b/lib/matplotlib/dviread.py index 3207a01de8be..7f90a13f1086 100644 --- a/lib/matplotlib/dviread.py +++ b/lib/matplotlib/dviread.py @@ -470,13 +470,12 @@ def _fnt_def_real(self, k, c, s, d, a, l): n = self.file.read(a + l) fontname = n[-l:].decode('ascii') tfm = _tfmfile(fontname) - if tfm is None: - raise FileNotFoundError("missing font metrics file: %s" % fontname) if c != 0 and tfm.checksum != 0 and c != tfm.checksum: raise ValueError('tfm checksum mismatch: %s' % n) - - vf = _vffile(fontname) - + try: + vf = _vffile(fontname) + except FileNotFoundError: + vf = None self.fonts[k] = DviFont(scale=s, tfm=tfm, texname=n, vf=vf) @_dispatch(247, state=_dvistate.pre, args=('u1', 'u4', 'u4', 'u4', 'u1')) @@ -938,9 +937,9 @@ def _parse_and_cache_line(self, line): if basename is None: basename = tfmname if encodingfile is not None: - encodingfile = find_tex_file(encodingfile) + encodingfile = _find_tex_file(encodingfile) if fontfile is not None: - fontfile = find_tex_file(fontfile) + fontfile = _find_tex_file(fontfile) self._parsed[tfmname] = PsFont( texname=tfmname, psname=basename, effects=effects, encoding=encodingfile, filename=fontfile) @@ -992,21 +991,20 @@ def search(self, filename): self._proc.stdin.write(os.fsencode(filename) + b"\n") self._proc.stdin.flush() out = self._proc.stdout.readline().rstrip() - return "" if out == b"nil" else os.fsdecode(out) + return None if out == b"nil" else os.fsdecode(out) @lru_cache() @_api.delete_parameter("3.5", "format") -def find_tex_file(filename, format=None): +def _find_tex_file(filename, format=None): """ - Find a file in the texmf tree. + Find a file in the texmf tree using kpathsea_. - Calls :program:`kpsewhich` which is an interface to the kpathsea - library [1]_. Most existing TeX distributions on Unix-like systems use - kpathsea. It is also available as part of MikTeX, a popular - distribution on Windows. + The kpathsea library, provided by most existing TeX distributions, both + on Unix-like systems and on Windows (MikTeX), is invoked via a long-lived + luatex process if luatex is installed, or via kpsewhich otherwise. - *If the file is not found, an empty string is returned*. + .. _kpathsea: https://www.tug.org/kpathsea/ Parameters ---------- @@ -1016,10 +1014,10 @@ def find_tex_file(filename, format=None): Could be e.g. 'tfm' or 'vf' to limit the search to that type of files. Deprecated. - References - ---------- - .. [1] `Kpathsea documentation `_ - The library that :program:`kpsewhich` is part of. + Raises + ------ + FileNotFoundError + If the file is not found. """ # we expect these to always be ascii encoded, but use utf-8 @@ -1029,39 +1027,63 @@ def find_tex_file(filename, format=None): if isinstance(format, bytes): format = format.decode('utf-8', errors='replace') - if format is None: + try: + lk = _LuatexKpsewhich() + except FileNotFoundError: + lk = None # Fallback to directly calling kpsewhich, as below. + + if lk and format is None: + path = lk.search(filename) + + else: + if os.name == 'nt': + # On Windows only, kpathsea can use utf-8 for cmd args and output. + # The `command_line_encoding` environment variable is set to force + # it to always use utf-8 encoding. See Matplotlib issue #11848. + kwargs = {'env': {**os.environ, 'command_line_encoding': 'utf-8'}, + 'encoding': 'utf-8'} + else: # On POSIX, run through the equivalent of os.fsdecode(). + kwargs = {'encoding': sys.getfilesystemencoding(), + 'errors': 'surrogateescape'} + + cmd = ['kpsewhich'] + if format is not None: + cmd += ['--format=' + format] + cmd += [filename] try: - lk = _LuatexKpsewhich() - except FileNotFoundError: - pass # Fallback to directly calling kpsewhich, as below. - else: - return lk.search(filename) - - if os.name == 'nt': - # On Windows only, kpathsea can use utf-8 for cmd args and output. - # The `command_line_encoding` environment variable is set to force it - # to always use utf-8 encoding. See Matplotlib issue #11848. - kwargs = {'env': {**os.environ, 'command_line_encoding': 'utf-8'}, - 'encoding': 'utf-8'} - else: # On POSIX, run through the equivalent of os.fsdecode(). - kwargs = {'encoding': sys.getfilesystemencoding(), - 'errors': 'surrogatescape'} - - cmd = ['kpsewhich'] - if format is not None: - cmd += ['--format=' + format] - cmd += [filename] + path = (cbook._check_and_log_subprocess(cmd, _log, **kwargs) + .rstrip('\n')) + except (FileNotFoundError, RuntimeError): + path = None + + if path: + return path + else: + raise FileNotFoundError( + f"Matplotlib's TeX implementation searched for a file named " + f"{filename!r} in your texmf tree, but could not find it") + + +# After the deprecation period elapses, delete this shim and rename +# _find_tex_file to find_tex_file everywhere. +@_api.delete_parameter("3.5", "format") +def find_tex_file(filename, format=None): try: - result = cbook._check_and_log_subprocess(cmd, _log, **kwargs) - except (FileNotFoundError, RuntimeError): - return '' - return result.rstrip('\n') + return (_find_tex_file(filename, format) if format is not None else + _find_tex_file(filename)) + except FileNotFoundError as exc: + _api.warn_deprecated( + "3.6", message=f"{exc.args[0]}; in the future, this will raise a " + f"FileNotFoundError.") + return "" + + +find_tex_file.__doc__ = _find_tex_file.__doc__ @lru_cache() def _fontfile(cls, suffix, texname): - filename = find_tex_file(texname + suffix) - return cls(filename) if filename else None + return cls(_find_tex_file(texname + suffix)) _tfmfile = partial(_fontfile, Tfm, ".tfm") @@ -1077,7 +1099,7 @@ def _fontfile(cls, suffix, texname): parser.add_argument("dpi", nargs="?", type=float, default=None) args = parser.parse_args() with Dvi(args.filename, args.dpi) as dvi: - fontmap = PsfontsMap(find_tex_file('pdftex.map')) + fontmap = PsfontsMap(_find_tex_file('pdftex.map')) for page in dvi: print(f"=== new page === " f"(w: {page.width}, h: {page.height}, d: {page.descent})") diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index dee0de8c5691..5f212043c6c9 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -317,9 +317,7 @@ def contains(self, mouseevent): return inside, {} def get_window_extent(self, *args, **kwargs): - """ - Return the figure bounding box in display space. Arguments are ignored. - """ + # docstring inherited return self.bbox def _suplabels(self, t, info, **kwargs): @@ -1596,7 +1594,10 @@ def get_default_bbox_extra_artists(self): def get_tightbbox(self, renderer, bbox_extra_artists=None): """ - Return a (tight) bounding box of the figure in inches. + Return a (tight) bounding box of the figure *in inches*. + + Note that `.FigureBase` differs from all other artists, which return + their `.Bbox` in pixels. Artists that have ``artist.set_in_layout(False)`` are not included in the bbox. diff --git a/lib/matplotlib/gridspec.py b/lib/matplotlib/gridspec.py index 74b35d41797d..5ab3888000ec 100644 --- a/lib/matplotlib/gridspec.py +++ b/lib/matplotlib/gridspec.py @@ -330,6 +330,8 @@ class GridSpec(GridSpecBase): The location of the grid cells is determined in a similar way to `~.figure.SubplotParams` using *left*, *right*, *top*, *bottom*, *wspace* and *hspace*. + + Indexing a GridSpec instance returns a `.SubplotSpec`. """ def __init__(self, nrows, ncols, figure=None, left=None, bottom=None, right=None, top=None, diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index 2d8cdf1691e4..04b95d3d0a37 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -1601,20 +1601,20 @@ def imsave(fname, arr, vmin=None, vmax=None, cmap=None, format=None, else: # Don't bother creating an image; this avoids rounding errors on the # size when dividing and then multiplying by dpi. - sm = cm.ScalarMappable(cmap=cmap) - sm.set_clim(vmin, vmax) if origin is None: origin = mpl.rcParams["image.origin"] if origin == "lower": arr = arr[::-1] if (isinstance(arr, memoryview) and arr.format == "B" and arr.ndim == 3 and arr.shape[-1] == 4): - # Such an ``arr`` would also be handled fine by sm.to_rgba (after - # casting with asarray), but it is useful to special-case it + # Such an ``arr`` would also be handled fine by sm.to_rgba below + # (after casting with asarray), but it is useful to special-case it # because that's what backend_agg passes, and can be in fact used # as is, saving a few operations. rgba = arr else: + sm = cm.ScalarMappable(cmap=cmap) + sm.set_clim(vmin, vmax) rgba = sm.to_rgba(arr, bytes=True) if pil_kwargs is None: pil_kwargs = {} diff --git a/lib/matplotlib/legend.py b/lib/matplotlib/legend.py index b255bba67240..bdebc5495a28 100644 --- a/lib/matplotlib/legend.py +++ b/lib/matplotlib/legend.py @@ -712,27 +712,19 @@ def _init_legend_box(self, handles, labels, markerfirst=True): fontsize = self._fontsize - # legend_box is a HPacker, horizontally packed with - # columns. Each column is a VPacker, vertically packed with - # legend items. Each legend item is HPacker packed with - # legend handleBox and labelBox. handleBox is an instance of - # offsetbox.DrawingArea which contains legend handle. labelBox - # is an instance of offsetbox.TextArea which contains legend - # text. + # legend_box is a HPacker, horizontally packed with columns. + # Each column is a VPacker, vertically packed with legend items. + # Each legend item is a HPacker packed with: + # - handlebox: a DrawingArea which contains the legend handle. + # - labelbox: a TextArea which contains the legend text. text_list = [] # the list of text instances handle_list = [] # the list of handle instances handles_and_labels = [] - label_prop = dict(verticalalignment='baseline', - horizontalalignment='left', - fontproperties=self.prop, - ) - # The approximate height and descent of text. These values are # only used for plotting the legend handle. - descent = 0.35 * fontsize * (self.handleheight - 0.7) - # 0.35 and 0.7 are just heuristic numbers and may need to be improved. + descent = 0.35 * fontsize * (self.handleheight - 0.7) # heuristic. height = fontsize * self.handleheight - descent # each handle needs to be drawn inside a box of (x, y, w, h) = # (0, -descent, width, height). And their coordinates should @@ -744,7 +736,7 @@ def _init_legend_box(self, handles, labels, markerfirst=True): # manually set their transform to the self.get_transform(). legend_handler_map = self.get_legend_handler_map() - for orig_handle, lab in zip(handles, labels): + for orig_handle, label in zip(handles, labels): handler = self.get_legend_handler(legend_handler_map, orig_handle) if handler is None: _api.warn_external( @@ -753,12 +745,14 @@ def _init_legend_box(self, handles, labels, markerfirst=True): "https://matplotlib.org/users/legend_guide.html" "#creating-artists-specifically-for-adding-to-the-legend-" "aka-proxy-artists".format(orig_handle)) - # We don't have a handle for this artist, so we just defer - # to None. + # No handle for this artist, so we just defer to None. handle_list.append(None) else: - textbox = TextArea(lab, textprops=label_prop, - multilinebaseline=True) + textbox = TextArea(label, multilinebaseline=True, + textprops=dict( + verticalalignment='baseline', + horizontalalignment='left', + fontproperties=self.prop)) handlebox = DrawingArea(width=self.handlelength * fontsize, height=height, xdescent=0., ydescent=descent) @@ -770,34 +764,25 @@ def _init_legend_box(self, handles, labels, markerfirst=True): fontsize, handlebox)) handles_and_labels.append((handlebox, textbox)) - if handles_and_labels: - # We calculate number of rows in each column. The first - # (num_largecol) columns will have (nrows+1) rows, and remaining - # (num_smallcol) columns will have (nrows) rows. - ncol = min(self._ncol, len(handles_and_labels)) - nrows, num_largecol = divmod(len(handles_and_labels), ncol) - num_smallcol = ncol - num_largecol - # starting index of each column and number of rows in it. - rows_per_col = [nrows + 1] * num_largecol + [nrows] * num_smallcol - start_idxs = np.concatenate([[0], np.cumsum(rows_per_col)[:-1]]) - cols = zip(start_idxs, rows_per_col) - else: - cols = [] - columnbox = [] - for i0, di in cols: - # pack handleBox and labelBox into itemBox - itemBoxes = [HPacker(pad=0, + # array_split splits n handles_and_labels into ncol columns, with the + # first n%ncol columns having an extra entry. filter(len, ...) handles + # the case where n < ncol: the last ncol-n columns are empty and get + # filtered out. + for handles_and_labels_column \ + in filter(len, np.array_split(handles_and_labels, self._ncol)): + # pack handlebox and labelbox into itembox + itemboxes = [HPacker(pad=0, sep=self.handletextpad * fontsize, children=[h, t] if markerfirst else [t, h], align="baseline") - for h, t in handles_and_labels[i0:i0 + di]] - # pack columnBox + for h, t in handles_and_labels_column] + # pack columnbox alignment = "baseline" if markerfirst else "right" columnbox.append(VPacker(pad=0, sep=self.labelspacing * fontsize, align=alignment, - children=itemBoxes)) + children=itemboxes)) mode = "expand" if self._mode == "expand" else "fixed" sep = self.columnspacing * fontsize @@ -900,20 +885,7 @@ def get_window_extent(self, renderer=None): return self._legend_box.get_window_extent(renderer=renderer) def get_tightbbox(self, renderer): - """ - Like `.Legend.get_window_extent`, but uses the box for the legend. - - Parameters - ---------- - renderer : `.RendererBase` subclass - renderer that will be used to draw the figures (i.e. - ``fig.canvas.get_renderer()``) - - Returns - ------- - `.BboxBase` - The bounding box in figure pixel coordinates. - """ + # docstring inherited return self._legend_box.get_window_extent(renderer) def get_frame_on(self): diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py index ea3b98b63208..f1efd125a4ed 100644 --- a/lib/matplotlib/lines.py +++ b/lib/matplotlib/lines.py @@ -370,7 +370,10 @@ def __init__(self, xdata, ydata, self.set_color(color) if marker is None: marker = 'none' # Default. - self._marker = MarkerStyle(marker, fillstyle) + if not isinstance(marker, MarkerStyle): + self._marker = MarkerStyle(marker, fillstyle) + else: + self._marker = marker self._markevery = None self._markersize = None diff --git a/lib/matplotlib/markers.py b/lib/matplotlib/markers.py index 8fbbb71818a4..b1d6fa2be6a4 100644 --- a/lib/matplotlib/markers.py +++ b/lib/matplotlib/markers.py @@ -82,11 +82,16 @@ plt.plot([1, 2, 3], marker=11) plt.plot([1, 2, 3], marker=matplotlib.markers.CARETDOWNBASE) +Markers join and cap styles can be customized by creating a new instance of +MarkerStyle. +A MarkerStyle can also have a custom `~matplotlib.transforms.Transform` +allowing it to be arbitrarily rotated or offset. + Examples showing the use of markers: * :doc:`/gallery/lines_bars_and_markers/marker_reference` * :doc:`/gallery/lines_bars_and_markers/scatter_star_poly` - +* :doc:`/gallery/lines_bars_and_markers/multivariate_marker_plot` .. |m00| image:: /_static/markers/m00.png .. |m01| image:: /_static/markers/m01.png @@ -127,6 +132,7 @@ .. |m36| image:: /_static/markers/m36.png .. |m37| image:: /_static/markers/m37.png """ +import copy from collections.abc import Sized import inspect @@ -221,7 +227,8 @@ class MarkerStyle: _unset = object() # For deprecation of MarkerStyle(). - def __init__(self, marker=_unset, fillstyle=None): + def __init__(self, marker=_unset, fillstyle=None, + transform=None, capstyle=None, joinstyle=None): """ Parameters ---------- @@ -234,8 +241,21 @@ def __init__(self, marker=_unset, fillstyle=None): fillstyle : str, default: :rc:`markers.fillstyle` One of 'full', 'left', 'right', 'bottom', 'top', 'none'. + + transform : transforms.Transform, default: None + Transform that will be combined with the native transform of the + marker. + + capstyle : CapStyle, default: None + Cap style that will override the default cap style of the marker. + + joinstyle : JoinStyle, default: None + Join style that will override the default join style of the marker. """ self._marker_function = None + self._user_transform = transform + self._user_capstyle = capstyle + self._user_joinstyle = joinstyle self._set_fillstyle(fillstyle) # Remove _unset and signature rewriting after deprecation elapses. if marker is self._unset: @@ -265,7 +285,7 @@ def _recache(self): self._alt_transform = None self._snap_threshold = None self._joinstyle = JoinStyle.round - self._capstyle = CapStyle.butt + self._capstyle = self._user_capstyle or CapStyle.butt # Initial guess: Assume the marker is filled unless the fillstyle is # set to 'none'. The marker function will override this for unfilled # markers. @@ -342,7 +362,8 @@ def _set_marker(self, marker): self._marker_function = getattr( self, '_set_' + self.markers[marker]) elif isinstance(marker, MarkerStyle): - self.__dict__.update(marker.__dict__) + self.__dict__ = copy.deepcopy(marker.__dict__) + else: try: Path(marker) @@ -369,7 +390,10 @@ def get_transform(self): Return the transform to be applied to the `.Path` from `MarkerStyle.get_path()`. """ - return self._transform.frozen() + if self._user_transform is None: + return self._transform.frozen() + else: + return (self._transform + self._user_transform).frozen() def get_alt_path(self): """ @@ -385,11 +409,86 @@ def get_alt_transform(self): Return the transform to be applied to the `.Path` from `MarkerStyle.get_alt_path()`. """ - return self._alt_transform.frozen() + if self._user_transform is None: + return self._alt_transform.frozen() + else: + return (self._alt_transform + self._user_transform).frozen() def get_snap_threshold(self): return self._snap_threshold + def get_user_transform(self): + """Return user supplied part of marker transform.""" + if self._user_transform is not None: + return self._user_transform.frozen() + + def transformed(self, transform: Affine2D): + """ + Return a new version of this marker with the transform applied. + + Parameters + ---------- + transform : Affine2D, default: None + Transform will be combined with current user supplied transform. + """ + new_marker = MarkerStyle(self) + if new_marker._user_transform is not None: + new_marker._user_transform += transform + else: + new_marker._user_transform = transform + return new_marker + + def rotated(self, *, deg=None, rad=None): + """ + Return a new version of this marker rotated by specified angle. + + Parameters + ---------- + deg : float, default: None + Rotation angle in degrees. + + rad : float, default: None + Rotation angle in radians. + + .. note:: You must specify exactly one of deg or rad. + """ + if deg is None and rad is None: + raise ValueError('One of deg or rad is required') + if deg is not None and rad is not None: + raise ValueError('Only one of deg and rad can be supplied') + new_marker = MarkerStyle(self) + if new_marker._user_transform is None: + new_marker._user_transform = Affine2D() + + if deg is not None: + new_marker._user_transform.rotate_deg(deg) + if rad is not None: + new_marker._user_transform.rotate(rad) + + return new_marker + + def scaled(self, sx, sy=None): + """ + Return new marker scaled by specified scale factors. + + If *sy* is None, the same scale is applied in both the *x*- and + *y*-directions. + + Parameters + ---------- + sx : float + *X*-direction scaling factor. + sy : float, default: None + *Y*-direction scaling factor. + """ + if sy is None: + sy = sx + + new_marker = MarkerStyle(self) + _transform = new_marker._user_transform or Affine2D() + new_marker._user_transform = _transform.scale(sx, sy) + return new_marker + def _set_nothing(self): self._filled = False @@ -413,14 +512,14 @@ def _set_tuple_marker(self): symstyle = marker[1] if symstyle == 0: self._path = Path.unit_regular_polygon(numsides) - self._joinstyle = JoinStyle.miter + self._joinstyle = self._user_joinstyle or JoinStyle.miter elif symstyle == 1: self._path = Path.unit_regular_star(numsides) - self._joinstyle = JoinStyle.bevel + self._joinstyle = self._user_joinstyle or JoinStyle.bevel elif symstyle == 2: self._path = Path.unit_regular_asterisk(numsides) self._filled = False - self._joinstyle = JoinStyle.bevel + self._joinstyle = self._user_joinstyle or JoinStyle.bevel else: raise ValueError(f"Unexpected tuple marker: {marker}") self._transform = Affine2D().scale(0.5).rotate_deg(rotation) @@ -521,7 +620,7 @@ def _set_triangle(self, rot, skip): self._alt_transform = self._transform - self._joinstyle = JoinStyle.miter + self._joinstyle = self._user_joinstyle or JoinStyle.miter def _set_triangle_up(self): return self._set_triangle(0.0, 0) @@ -551,7 +650,7 @@ def _set_square(self): self._transform.rotate_deg(rotate) self._alt_transform = self._transform - self._joinstyle = JoinStyle.miter + self._joinstyle = self._user_joinstyle or JoinStyle.miter def _set_diamond(self): self._transform = Affine2D().translate(-0.5, -0.5).rotate_deg(45) @@ -565,7 +664,7 @@ def _set_diamond(self): rotate = {'right': 0, 'top': 90, 'left': 180, 'bottom': 270}[fs] self._transform.rotate_deg(rotate) self._alt_transform = self._transform - self._joinstyle = JoinStyle.miter + self._joinstyle = self._user_joinstyle or JoinStyle.miter def _set_thin_diamond(self): self._set_diamond() @@ -592,7 +691,7 @@ def _set_pentagon(self): }[self.get_fillstyle()] self._alt_transform = self._transform - self._joinstyle = JoinStyle.miter + self._joinstyle = self._user_joinstyle or JoinStyle.miter def _set_star(self): self._transform = Affine2D().scale(0.5) @@ -614,7 +713,7 @@ def _set_star(self): }[self.get_fillstyle()] self._alt_transform = self._transform - self._joinstyle = JoinStyle.bevel + self._joinstyle = self._user_joinstyle or JoinStyle.bevel def _set_hexagon1(self): self._transform = Affine2D().scale(0.5) @@ -638,7 +737,7 @@ def _set_hexagon1(self): }[self.get_fillstyle()] self._alt_transform = self._transform - self._joinstyle = JoinStyle.miter + self._joinstyle = self._user_joinstyle or JoinStyle.miter def _set_hexagon2(self): self._transform = Affine2D().scale(0.5).rotate_deg(30) @@ -664,7 +763,7 @@ def _set_hexagon2(self): }[self.get_fillstyle()] self._alt_transform = self._transform - self._joinstyle = JoinStyle.miter + self._joinstyle = self._user_joinstyle or JoinStyle.miter def _set_octagon(self): self._transform = Affine2D().scale(0.5) @@ -685,7 +784,7 @@ def _set_octagon(self): {'left': 0, 'bottom': 90, 'right': 180, 'top': 270}[fs]) self._alt_transform = self._transform.frozen().rotate_deg(180.0) - self._joinstyle = JoinStyle.miter + self._joinstyle = self._user_joinstyle or JoinStyle.miter _line_marker_path = Path([[0.0, -1.0], [0.0, 1.0]]) @@ -759,7 +858,7 @@ def _set_caretdown(self): self._snap_threshold = 3.0 self._filled = False self._path = self._caret_path - self._joinstyle = JoinStyle.miter + self._joinstyle = self._user_joinstyle or JoinStyle.miter def _set_caretup(self): self._set_caretdown() @@ -825,7 +924,7 @@ def _set_x(self): def _set_plus_filled(self): self._transform = Affine2D() self._snap_threshold = 5.0 - self._joinstyle = JoinStyle.miter + self._joinstyle = self._user_joinstyle or JoinStyle.miter if not self._half_fill(): self._path = self._plus_filled_path else: @@ -849,7 +948,7 @@ def _set_plus_filled(self): def _set_x_filled(self): self._transform = Affine2D() self._snap_threshold = 5.0 - self._joinstyle = JoinStyle.miter + self._joinstyle = self._user_joinstyle or JoinStyle.miter if not self._half_fill(): self._path = self._x_filled_path else: diff --git a/lib/matplotlib/offsetbox.py b/lib/matplotlib/offsetbox.py index aabc1db652e1..27145abdf96c 100644 --- a/lib/matplotlib/offsetbox.py +++ b/lib/matplotlib/offsetbox.py @@ -346,7 +346,7 @@ def get_extent(self, renderer): return w, h, xd, yd def get_window_extent(self, renderer): - """Return the bounding box (`.Bbox`) in display space.""" + # docstring inherited w, h, xd, yd, offsets = self.get_extent_offsets(renderer) px, py = self.get_offset(w, h, xd, yd, renderer) return mtransforms.Bbox.from_bounds(px - xd, py - yd, w, h) @@ -640,7 +640,7 @@ def get_offset(self): return self._offset def get_window_extent(self, renderer): - """Return the bounding box in display space.""" + # docstring inherited w, h, xd, yd = self.get_extent(renderer) ox, oy = self.get_offset() # w, h, xd, yd) @@ -800,7 +800,7 @@ def get_offset(self): return self._offset def get_window_extent(self, renderer): - """Return the bounding box in display space.""" + # docstring inherited w, h, xd, yd = self.get_extent(renderer) ox, oy = self.get_offset() return mtransforms.Bbox.from_bounds(ox - xd, oy - yd, w, h) @@ -901,7 +901,7 @@ def get_offset(self): return self._offset def get_window_extent(self, renderer): - """Return the bounding box in display space.""" + # docstring inherited w, h, xd, yd = self.get_extent(renderer) ox, oy = self.get_offset() # w, h, xd, yd) return mtransforms.Bbox.from_bounds(ox - xd, oy - yd, w, h) @@ -1085,7 +1085,7 @@ def set_bbox_to_anchor(self, bbox, transform=None): self.stale = True def get_window_extent(self, renderer): - """Return the bounding box in display space.""" + # docstring inherited self._update_offset_func(renderer) w, h, xd, yd = self.get_extent(renderer) ox, oy = self.get_offset(w, h, xd, yd, renderer) @@ -1249,7 +1249,7 @@ def get_children(self): return [self.image] def get_window_extent(self, renderer): - """Return the bounding box in display space.""" + # docstring inherited w, h, xd, yd = self.get_extent(renderer) ox, oy = self.get_offset() return mtransforms.Bbox.from_bounds(ox - xd, oy - yd, w, h) @@ -1440,18 +1440,14 @@ def get_fontsize(self): return self.prop.get_size_in_points() def get_window_extent(self, renderer): - """ - get the bounding box in display space. - """ + # docstring inherited bboxes = [child.get_window_extent(renderer) for child in self.get_children()] return Bbox.union(bboxes) def get_tightbbox(self, renderer): - """ - get tight bounding box in display space. - """ + # docstring inherited bboxes = [child.get_tightbbox(renderer) for child in self.get_children()] diff --git a/lib/matplotlib/path.py b/lib/matplotlib/path.py index 4280d55eeacd..43d3f2fd95da 100644 --- a/lib/matplotlib/path.py +++ b/lib/matplotlib/path.py @@ -272,13 +272,10 @@ def __deepcopy__(self, memo=None): Return a deepcopy of the `Path`. The `Path` will not be readonly, even if the source `Path` is. """ - try: - codes = self.codes.copy() - except AttributeError: - codes = None - return self.__class__( - self.vertices.copy(), codes, - _interpolation_steps=self._interpolation_steps) + # Deepcopying arrays (vertices, codes) strips the writeable=False flag. + p = copy.deepcopy(super(), memo) + p._readonly = False + return p deepcopy = __deepcopy__ diff --git a/lib/matplotlib/table.py b/lib/matplotlib/table.py index e46cec6193f8..15ac6e4ae75a 100644 --- a/lib/matplotlib/table.py +++ b/lib/matplotlib/table.py @@ -447,7 +447,7 @@ def get_children(self): return list(self._cells.values()) def get_window_extent(self, renderer): - """Return the bounding box of the table in window coords.""" + # docstring inherited self._update_positions(renderer) boxes = [cell.get_window_extent(renderer) for cell in self._cells.values()] diff --git a/lib/matplotlib/testing/__init__.py b/lib/matplotlib/testing/__init__.py index f9c547ce00aa..754277c41f43 100644 --- a/lib/matplotlib/testing/__init__.py +++ b/lib/matplotlib/testing/__init__.py @@ -78,4 +78,8 @@ def _check_for_pgf(texsystem): def _has_tex_package(package): - return bool(mpl.dviread.find_tex_file(f"{package}.sty")) + try: + mpl.dviread._find_tex_file(f"{package}.sty") + return True + except FileNotFoundError: + return False diff --git a/lib/matplotlib/testing/decorators.py b/lib/matplotlib/testing/decorators.py index a38d55fd1dcc..562cb4f43567 100644 --- a/lib/matplotlib/testing/decorators.py +++ b/lib/matplotlib/testing/decorators.py @@ -14,10 +14,7 @@ import matplotlib.style import matplotlib.units import matplotlib.testing -from matplotlib import cbook -from matplotlib import ft2font -from matplotlib import pyplot as plt -from matplotlib import ticker +from matplotlib import cbook, ft2font, pyplot as plt, ticker, _pylab_helpers from .compare import comparable_formats, compare_images, make_test_filename from .exceptions import ImageComparisonFailure @@ -129,6 +126,29 @@ def remove_ticks(ax): remove_ticks(ax) +@contextlib.contextmanager +def _collect_new_figures(): + """ + After:: + + with _collect_new_figures() as figs: + some_code() + + the list *figs* contains the figures that have been created during the + execution of ``some_code``, sorted by figure number. + """ + managers = _pylab_helpers.Gcf.figs + preexisting = [manager for manager in managers.values()] + new_figs = [] + try: + yield new_figs + finally: + new_managers = sorted([manager for manager in managers.values() + if manager not in preexisting], + key=lambda manager: manager.num) + new_figs[:] = [manager.canvas.figure for manager in new_managers] + + def _raise_on_image_difference(expected, actual, tol): __tracebackhide__ = True @@ -178,10 +198,8 @@ def copy_baseline(self, baseline, extension): f"{orig_expected_path}") from err return expected_fname - def compare(self, idx, baseline, extension, *, _lock=False): + def compare(self, fig, baseline, extension, *, _lock=False): __tracebackhide__ = True - fignum = plt.get_fignums()[idx] - fig = plt.figure(fignum) if self.remove_text: remove_ticks_and_titles(fig) @@ -196,7 +214,12 @@ def compare(self, idx, baseline, extension, *, _lock=False): lock = (cbook._lock_path(actual_path) if _lock else contextlib.nullcontext()) with lock: - fig.savefig(actual_path, **kwargs) + try: + fig.savefig(actual_path, **kwargs) + finally: + # Matplotlib has an autouse fixture to close figures, but this + # makes things more convenient for third-party users. + plt.close(fig) expected_path = self.copy_baseline(baseline, extension) _raise_on_image_difference(expected_path, actual_path, self.tol) @@ -235,7 +258,9 @@ def wrapper(*args, extension, request, **kwargs): img = _ImageComparisonBase(func, tol=tol, remove_text=remove_text, savefig_kwargs=savefig_kwargs) matplotlib.testing.set_font_settings_for_testing() - func(*args, **kwargs) + + with _collect_new_figures() as figs: + func(*args, **kwargs) # If the test is parametrized in any way other than applied via # this decorator, then we need to use a lock to prevent two @@ -252,11 +277,11 @@ def wrapper(*args, extension, request, **kwargs): our_baseline_images = request.getfixturevalue( 'baseline_images') - assert len(plt.get_fignums()) == len(our_baseline_images), ( + assert len(figs) == len(our_baseline_images), ( "Test generated {} images but there are {} baseline images" - .format(len(plt.get_fignums()), len(our_baseline_images))) - for idx, baseline in enumerate(our_baseline_images): - img.compare(idx, baseline, extension, _lock=needs_lock) + .format(len(figs), len(our_baseline_images))) + for fig, baseline in zip(figs, our_baseline_images): + img.compare(fig, baseline, extension, _lock=needs_lock) parameters = list(old_sig.parameters.values()) if 'extension' not in old_sig.parameters: @@ -427,11 +452,9 @@ def wrapper(*args, ext, request, **kwargs): try: fig_test = plt.figure("test") fig_ref = plt.figure("reference") - # Keep track of number of open figures, to make sure test - # doesn't create any new ones - n_figs = len(plt.get_fignums()) - func(*args, fig_test=fig_test, fig_ref=fig_ref, **kwargs) - if len(plt.get_fignums()) > n_figs: + with _collect_new_figures() as figs: + func(*args, fig_test=fig_test, fig_ref=fig_ref, **kwargs) + if figs: raise RuntimeError('Number of open figures changed during ' 'test. Make sure you are plotting to ' 'fig_test or fig_ref, or if this is ' diff --git a/lib/matplotlib/tests/Courier10PitchBT-Bold.pfb b/lib/matplotlib/tests/Courier10PitchBT-Bold.pfb new file mode 100644 index 000000000000..88d9af2af701 Binary files /dev/null and b/lib/matplotlib/tests/Courier10PitchBT-Bold.pfb differ diff --git a/lib/matplotlib/tests/test_agg.py b/lib/matplotlib/tests/test_agg.py index 0e4abf86fe02..32fea7cd4590 100644 --- a/lib/matplotlib/tests/test_agg.py +++ b/lib/matplotlib/tests/test_agg.py @@ -249,6 +249,25 @@ def test_pil_kwargs_tiff(): assert tags["ImageDescription"] == "test image" +def test_pil_kwargs_webp(): + plt.plot([0, 1, 2], [0, 1, 0]) + buf_small = io.BytesIO() + pil_kwargs_low = {"quality": 1} + plt.savefig(buf_small, format="webp", pil_kwargs=pil_kwargs_low) + buf_large = io.BytesIO() + pil_kwargs_high = {"quality": 100} + plt.savefig(buf_large, format="webp", pil_kwargs=pil_kwargs_high) + assert buf_large.getbuffer().nbytes > buf_small.getbuffer().nbytes + + +def test_webp_alpha(): + plt.plot([0, 1, 2], [0, 1, 0]) + buf = io.BytesIO() + plt.savefig(buf, format="webp", transparent=True) + im = Image.open(buf) + assert im.mode == "RGBA" + + def test_draw_path_collection_error_handling(): fig, ax = plt.subplots() ax.scatter([1], [1]).set_paths(path.Path([(0, 1), (2, 3)])) diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 3168fe86ad66..1b349b2a9c31 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -1249,22 +1249,23 @@ def test_pcolorflaterror(): ax.pcolormesh(x, y, Z, shading='flat') +@pytest.mark.parametrize('snap', [False, True]) @check_figures_equal(extensions=["png"]) -def test_pcolorauto(fig_test, fig_ref): +def test_pcolorauto(fig_test, fig_ref, snap): ax = fig_test.subplots() x = np.arange(0, 10) y = np.arange(0, 4) np.random.seed(19680801) Z = np.random.randn(3, 9) # this is the same as flat; note that auto is default - ax.pcolormesh(x, y, Z) + ax.pcolormesh(x, y, Z, snap=snap) ax = fig_ref.subplots() # specify the centers x2 = x[:-1] + np.diff(x) / 2 y2 = y[:-1] + np.diff(y) / 2 # this is same as nearest: - ax.pcolormesh(x2, y2, Z) + ax.pcolormesh(x2, y2, Z, snap=snap) @image_comparison(['canonical']) @@ -3513,6 +3514,27 @@ def test_errorbar_every_invalid(): ax.errorbar(x, y, yerr, errorevery='foobar') +def test_xerr_yerr_not_negative(): + ax = plt.figure().subplots() + + with pytest.raises(ValueError, + match="'xerr' must not contain negative values"): + ax.errorbar(x=[0], y=[0], xerr=[[-0.5], [1]], yerr=[[-0.5], [1]]) + with pytest.raises(ValueError, + match="'xerr' must not contain negative values"): + ax.errorbar(x=[0], y=[0], xerr=[[-0.5], [1]]) + with pytest.raises(ValueError, + match="'yerr' must not contain negative values"): + ax.errorbar(x=[0], y=[0], yerr=[[-0.5], [1]]) + with pytest.raises(ValueError, + match="'yerr' must not contain negative values"): + x = np.arange(5) + y = [datetime.datetime(2021, 9, i * 2 + 1) for i in x] + ax.errorbar(x=x, + y=y, + yerr=datetime.timedelta(days=-10)) + + @check_figures_equal() def test_errorbar_every(fig_test, fig_ref): x = np.linspace(0, 1, 15) diff --git a/lib/matplotlib/tests/test_backend_qt.py b/lib/matplotlib/tests/test_backend_qt.py index b22772c35f78..7bdc01fe0223 100644 --- a/lib/matplotlib/tests/test_backend_qt.py +++ b/lib/matplotlib/tests/test_backend_qt.py @@ -24,6 +24,9 @@ pytestmark = pytest.mark.skip('No usable Qt bindings') +_test_timeout = 60 # A reasonably safe value for slower architectures. + + @pytest.fixture def qt_core(request): backend, = request.node.get_closest_marker('backend').args @@ -33,19 +36,6 @@ def qt_core(request): return QtCore -@pytest.fixture -def platform_simulate_ctrl_c(request): - import signal - from functools import partial - - if hasattr(signal, "CTRL_C_EVENT"): - win32api = pytest.importorskip('win32api') - return partial(win32api.GenerateConsoleCtrlEvent, 0, 0) - else: - # we're not on windows - return partial(os.kill, os.getpid(), signal.SIGINT) - - @pytest.mark.backend('QtAgg', skip_on_importerror=True) def test_fig_close(): @@ -64,50 +54,143 @@ def test_fig_close(): assert init_figs == Gcf.figs -@pytest.mark.backend('QtAgg', skip_on_importerror=True) -@pytest.mark.parametrize("target, kwargs", [ - (plt.show, {"block": True}), - (plt.pause, {"interval": 10}) -]) -def test_sigint(qt_core, platform_simulate_ctrl_c, target, - kwargs): - plt.figure() - def fire_signal(): - platform_simulate_ctrl_c() +class WaitForStringPopen(subprocess.Popen): + """ + A Popen that passes flags that allow triggering KeyboardInterrupt. + """ - qt_core.QTimer.singleShot(100, fire_signal) - with pytest.raises(KeyboardInterrupt): + def __init__(self, *args, **kwargs): + if sys.platform == 'win32': + kwargs['creationflags'] = subprocess.CREATE_NEW_CONSOLE + super().__init__( + *args, **kwargs, + # Force Agg so that each test can switch to its desired Qt backend. + env={**os.environ, "MPLBACKEND": "Agg", "SOURCE_DATE_EPOCH": "0"}, + stdout=subprocess.PIPE, universal_newlines=True) + + def wait_for(self, terminator): + """Read until the terminator is reached.""" + buf = '' + while True: + c = self.stdout.read(1) + if not c: + raise RuntimeError( + f'Subprocess died before emitting expected {terminator!r}') + buf += c + if buf.endswith(terminator): + return + + +def _test_sigint_impl(backend, target_name, kwargs): + import sys + import matplotlib.pyplot as plt + import os + import threading + + plt.switch_backend(backend) + from matplotlib.backends.qt_compat import QtCore + + def interupter(): + if sys.platform == 'win32': + import win32api + win32api.GenerateConsoleCtrlEvent(0, 0) + else: + import signal + os.kill(os.getpid(), signal.SIGINT) + + target = getattr(plt, target_name) + timer = threading.Timer(1, interupter) + fig = plt.figure() + fig.canvas.mpl_connect( + 'draw_event', + lambda *args: print('DRAW', flush=True) + ) + fig.canvas.mpl_connect( + 'draw_event', + lambda *args: timer.start() + ) + try: target(**kwargs) + except KeyboardInterrupt: + print('SUCCESS', flush=True) @pytest.mark.backend('QtAgg', skip_on_importerror=True) @pytest.mark.parametrize("target, kwargs", [ - (plt.show, {"block": True}), - (plt.pause, {"interval": 10}) + ('show', {'block': True}), + ('pause', {'interval': 10}) ]) -def test_other_signal_before_sigint(qt_core, platform_simulate_ctrl_c, - target, kwargs): - plt.figure() +def test_sigint(target, kwargs): + backend = plt.get_backend() + proc = WaitForStringPopen( + [sys.executable, "-c", + inspect.getsource(_test_sigint_impl) + + f"\n_test_sigint_impl({backend!r}, {target!r}, {kwargs!r})"]) + try: + proc.wait_for('DRAW') + stdout, _ = proc.communicate(timeout=_test_timeout) + except: + proc.kill() + stdout, _ = proc.communicate() + raise + print(stdout) + assert 'SUCCESS' in stdout + + +def _test_other_signal_before_sigint_impl(backend, target_name, kwargs): + import signal + import sys + import matplotlib.pyplot as plt + plt.switch_backend(backend) + from matplotlib.backends.qt_compat import QtCore - sigcld_caught = False - def custom_sigpipe_handler(signum, frame): - nonlocal sigcld_caught - sigcld_caught = True - signal.signal(signal.SIGCHLD, custom_sigpipe_handler) + target = getattr(plt, target_name) - def fire_other_signal(): - os.kill(os.getpid(), signal.SIGCHLD) + fig = plt.figure() + fig.canvas.mpl_connect('draw_event', + lambda *args: print('DRAW', flush=True)) - def fire_sigint(): - platform_simulate_ctrl_c() + timer = fig.canvas.new_timer(interval=1) + timer.single_shot = True + timer.add_callback(print, 'SIGUSR1', flush=True) - qt_core.QTimer.singleShot(50, fire_other_signal) - qt_core.QTimer.singleShot(100, fire_sigint) + def custom_signal_handler(signum, frame): + timer.start() + signal.signal(signal.SIGUSR1, custom_signal_handler) - with pytest.raises(KeyboardInterrupt): + try: target(**kwargs) + except KeyboardInterrupt: + print('SUCCESS', flush=True) - assert sigcld_caught + +@pytest.mark.skipif(sys.platform == 'win32', + reason='No other signal available to send on Windows') +@pytest.mark.backend('QtAgg', skip_on_importerror=True) +@pytest.mark.parametrize("target, kwargs", [ + ('show', {'block': True}), + ('pause', {'interval': 10}) +]) +def test_other_signal_before_sigint(target, kwargs): + backend = plt.get_backend() + proc = WaitForStringPopen( + [sys.executable, "-c", + inspect.getsource(_test_other_signal_before_sigint_impl) + + "\n_test_other_signal_before_sigint_impl(" + f"{backend!r}, {target!r}, {kwargs!r})"]) + try: + proc.wait_for('DRAW') + os.kill(proc.pid, signal.SIGUSR1) + proc.wait_for('SIGUSR1') + os.kill(proc.pid, signal.SIGINT) + stdout, _ = proc.communicate(timeout=_test_timeout) + except: + proc.kill() + stdout, _ = proc.communicate() + raise + print(stdout) + assert 'SUCCESS' in stdout + plt.figure() @pytest.mark.backend('Qt5Agg') @@ -140,29 +223,31 @@ def custom_handler(signum, frame): signal.signal(signal.SIGINT, custom_handler) - # mainloop() sets SIGINT, starts Qt event loop (which triggers timer and - # exits) and then mainloop() resets SIGINT - matplotlib.backends.backend_qt._BackendQT.mainloop() + try: + # mainloop() sets SIGINT, starts Qt event loop (which triggers timer + # and exits) and then mainloop() resets SIGINT + matplotlib.backends.backend_qt._BackendQT.mainloop() - # Assert: signal handler during loop execution is changed - # (can't test equality with func) - assert event_loop_handler != custom_handler + # Assert: signal handler during loop execution is changed + # (can't test equality with func) + assert event_loop_handler != custom_handler - # Assert: current signal handler is the same as the one we set before - assert signal.getsignal(signal.SIGINT) == custom_handler + # Assert: current signal handler is the same as the one we set before + assert signal.getsignal(signal.SIGINT) == custom_handler - # Repeat again to test that SIG_DFL and SIG_IGN will not be overridden - for custom_handler in (signal.SIG_DFL, signal.SIG_IGN): - qt_core.QTimer.singleShot(0, fire_signal_and_quit) - signal.signal(signal.SIGINT, custom_handler) + # Repeat again to test that SIG_DFL and SIG_IGN will not be overridden + for custom_handler in (signal.SIG_DFL, signal.SIG_IGN): + qt_core.QTimer.singleShot(0, fire_signal_and_quit) + signal.signal(signal.SIGINT, custom_handler) - _BackendQT5.mainloop() + _BackendQT5.mainloop() - assert event_loop_handler == custom_handler - assert signal.getsignal(signal.SIGINT) == custom_handler + assert event_loop_handler == custom_handler + assert signal.getsignal(signal.SIGINT) == custom_handler - # Reset SIGINT handler to what it was before the test - signal.signal(signal.SIGINT, original_handler) + finally: + # Reset SIGINT handler to what it was before the test + signal.signal(signal.SIGINT, original_handler) @pytest.mark.parametrize( @@ -548,8 +633,6 @@ def _get_testable_qt_backends(): envs.append(pytest.param(env, marks=marks, id=str(env))) return envs -_test_timeout = 60 # A reasonably safe value for slower architectures. - @pytest.mark.parametrize("env", _get_testable_qt_backends()) def test_enums_available(env): diff --git a/lib/matplotlib/tests/test_backends_interactive.py b/lib/matplotlib/tests/test_backends_interactive.py index bb17e5fdaf82..5d51cf2ab9c3 100644 --- a/lib/matplotlib/tests/test_backends_interactive.py +++ b/lib/matplotlib/tests/test_backends_interactive.py @@ -222,8 +222,14 @@ def _test_thread_impl(): param.marks.append( pytest.mark.xfail(raises=subprocess.CalledProcessError)) elif backend == "macosx": - param.marks.append( - pytest.mark.xfail(raises=subprocess.TimeoutExpired, strict=True)) + from packaging.version import parse + mac_ver = platform.mac_ver()[0] + # Note, macOS Big Sur is both 11 and 10.16, depending on SDK that + # Python was compiled against. + if mac_ver and parse(mac_ver) < parse('10.16'): + param.marks.append( + pytest.mark.xfail(raises=subprocess.TimeoutExpired, + strict=True)) elif param.values[0].get("QT_API") == "PySide2": param.marks.append( pytest.mark.xfail(raises=subprocess.CalledProcessError)) diff --git a/lib/matplotlib/tests/test_dviread.py b/lib/matplotlib/tests/test_dviread.py index a40151fd555f..7e10975f44d5 100644 --- a/lib/matplotlib/tests/test_dviread.py +++ b/lib/matplotlib/tests/test_dviread.py @@ -7,7 +7,7 @@ def test_PsfontsMap(monkeypatch): - monkeypatch.setattr(dr, 'find_tex_file', lambda x: x) + monkeypatch.setattr(dr, '_find_tex_file', lambda x: x) filename = str(Path(__file__).parent / 'baseline_images/dviread/test.map') fontmap = dr.PsfontsMap(filename) diff --git a/lib/matplotlib/tests/test_image.py b/lib/matplotlib/tests/test_image.py index 2c4a2763e4c6..719b19057875 100644 --- a/lib/matplotlib/tests/test_image.py +++ b/lib/matplotlib/tests/test_image.py @@ -341,6 +341,7 @@ def test_cursor_data(): ([[10001, 10000]], "[10001.000]"), ([[.123, .987]], "[0.123]"), ([[np.nan, 1, 2]], "[]"), + ([[1, 1+1e-15]], "[1.0000000000000000]"), ]) def test_format_cursor_data(data, text): from matplotlib.backend_bases import MouseEvent diff --git a/lib/matplotlib/tests/test_marker.py b/lib/matplotlib/tests/test_marker.py index 75681b0e1a9b..f50f45bbd4ee 100644 --- a/lib/matplotlib/tests/test_marker.py +++ b/lib/matplotlib/tests/test_marker.py @@ -4,6 +4,7 @@ from matplotlib._api.deprecation import MatplotlibDeprecationWarning from matplotlib.path import Path from matplotlib.testing.decorators import check_figures_equal +from matplotlib.transforms import Affine2D import pytest @@ -204,3 +205,99 @@ def test_marker_clipping(fig_ref, fig_test): ax_test.set(xlim=(-0.5, ncol), ylim=(-0.5, 2 * nrow)) ax_ref.axis('off') ax_test.axis('off') + + +def test_marker_init_transforms(): + """Test that initializing marker with transform is a simple addition.""" + marker = markers.MarkerStyle("o") + t = Affine2D().translate(1, 1) + t_marker = markers.MarkerStyle("o", transform=t) + assert marker.get_transform() + t == t_marker.get_transform() + + +def test_marker_init_joinstyle(): + marker = markers.MarkerStyle("*") + jstl = markers.JoinStyle.round + styled_marker = markers.MarkerStyle("*", joinstyle=jstl) + assert styled_marker.get_joinstyle() == jstl + assert marker.get_joinstyle() != jstl + + +def test_marker_init_captyle(): + marker = markers.MarkerStyle("*") + capstl = markers.CapStyle.round + styled_marker = markers.MarkerStyle("*", capstyle=capstl) + assert styled_marker.get_capstyle() == capstl + assert marker.get_capstyle() != capstl + + +@pytest.mark.parametrize("marker,transform,expected", [ + (markers.MarkerStyle("o"), Affine2D().translate(1, 1), + Affine2D().translate(1, 1)), + (markers.MarkerStyle("o", transform=Affine2D().translate(1, 1)), + Affine2D().translate(1, 1), Affine2D().translate(2, 2)), + (markers.MarkerStyle("$|||$", transform=Affine2D().translate(1, 1)), + Affine2D().translate(1, 1), Affine2D().translate(2, 2)), + (markers.MarkerStyle( + markers.TICKLEFT, transform=Affine2D().translate(1, 1)), + Affine2D().translate(1, 1), Affine2D().translate(2, 2)), +]) +def test_marker_transformed(marker, transform, expected): + new_marker = marker.transformed(transform) + assert new_marker is not marker + assert new_marker.get_user_transform() == expected + assert marker._user_transform is not new_marker._user_transform + + +def test_marker_rotated_invalid(): + marker = markers.MarkerStyle("o") + with pytest.raises(ValueError): + new_marker = marker.rotated() + with pytest.raises(ValueError): + new_marker = marker.rotated(deg=10, rad=10) + + +@pytest.mark.parametrize("marker,deg,rad,expected", [ + (markers.MarkerStyle("o"), 10, None, Affine2D().rotate_deg(10)), + (markers.MarkerStyle("o"), None, 0.01, Affine2D().rotate(0.01)), + (markers.MarkerStyle("o", transform=Affine2D().translate(1, 1)), + 10, None, Affine2D().translate(1, 1).rotate_deg(10)), + (markers.MarkerStyle("o", transform=Affine2D().translate(1, 1)), + None, 0.01, Affine2D().translate(1, 1).rotate(0.01)), + (markers.MarkerStyle("$|||$", transform=Affine2D().translate(1, 1)), + 10, None, Affine2D().translate(1, 1).rotate_deg(10)), + (markers.MarkerStyle( + markers.TICKLEFT, transform=Affine2D().translate(1, 1)), + 10, None, Affine2D().translate(1, 1).rotate_deg(10)), +]) +def test_marker_rotated(marker, deg, rad, expected): + new_marker = marker.rotated(deg=deg, rad=rad) + assert new_marker is not marker + assert new_marker.get_user_transform() == expected + assert marker._user_transform is not new_marker._user_transform + + +def test_marker_scaled(): + marker = markers.MarkerStyle("1") + new_marker = marker.scaled(2) + assert new_marker is not marker + assert new_marker.get_user_transform() == Affine2D().scale(2) + assert marker._user_transform is not new_marker._user_transform + + new_marker = marker.scaled(2, 3) + assert new_marker is not marker + assert new_marker.get_user_transform() == Affine2D().scale(2, 3) + assert marker._user_transform is not new_marker._user_transform + + marker = markers.MarkerStyle("1", transform=Affine2D().translate(1, 1)) + new_marker = marker.scaled(2) + assert new_marker is not marker + expected = Affine2D().translate(1, 1).scale(2) + assert new_marker.get_user_transform() == expected + assert marker._user_transform is not new_marker._user_transform + + +def test_alt_transform(): + m1 = markers.MarkerStyle("o", "left") + m2 = markers.MarkerStyle("o", "left", Affine2D().rotate_deg(90)) + assert m1.get_alt_transform().rotate_deg(90) == m2.get_alt_transform() diff --git a/lib/matplotlib/tests/test_textpath.py b/lib/matplotlib/tests/test_textpath.py new file mode 100644 index 000000000000..e421d2623cad --- /dev/null +++ b/lib/matplotlib/tests/test_textpath.py @@ -0,0 +1,10 @@ +import copy + +from matplotlib.textpath import TextPath + + +def test_copy(): + tp = TextPath((0, 0), ".") + assert copy.deepcopy(tp).vertices is not tp.vertices + assert (copy.deepcopy(tp).vertices == tp.vertices).all() + assert copy.copy(tp).vertices is tp.vertices diff --git a/lib/matplotlib/tests/test_type1font.py b/lib/matplotlib/tests/test_type1font.py index 99cc3e500b0e..6a16da10def1 100644 --- a/lib/matplotlib/tests/test_type1font.py +++ b/lib/matplotlib/tests/test_type1font.py @@ -1,6 +1,7 @@ import matplotlib.type1font as t1f import os.path import difflib +import pytest def test_Type1Font(): @@ -13,10 +14,35 @@ def test_Type1Font(): assert font.parts[0] == rawdata[0x0006:0x10c5] assert font.parts[1] == rawdata[0x10cb:0x897f] assert font.parts[2] == rawdata[0x8985:0x8ba6] - assert font.parts[1:] == slanted.parts[1:] - assert font.parts[1:] == condensed.parts[1:] assert font.decrypted.startswith(b'dup\n/Private 18 dict dup begin') assert font.decrypted.endswith(b'mark currentfile closefile\n') + assert slanted.decrypted.startswith(b'dup\n/Private 18 dict dup begin') + assert slanted.decrypted.endswith(b'mark currentfile closefile\n') + assert b'UniqueID 5000793' in font.parts[0] + assert b'UniqueID 5000793' in font.decrypted + assert font._pos['UniqueID'] == [(797, 818), (4483, 4504)] + + len0 = len(font.parts[0]) + for key in font._pos.keys(): + for pos0, pos1 in font._pos[key]: + if pos0 < len0: + data = font.parts[0][pos0:pos1] + else: + data = font.decrypted[pos0-len0:pos1-len0] + assert data.startswith(f'/{key}'.encode('ascii')) + assert {'FontType', 'FontMatrix', 'PaintType', 'ItalicAngle', 'RD' + } < set(font._pos.keys()) + + assert b'UniqueID 5000793' not in slanted.parts[0] + assert b'UniqueID 5000793' not in slanted.decrypted + assert 'UniqueID' not in slanted._pos + assert font.prop['Weight'] == 'Medium' + assert not font.prop['isFixedPitch'] + assert font.prop['ItalicAngle'] == 0 + assert slanted.prop['ItalicAngle'] == -45 + assert font.prop['Encoding'][5] == 'Pi' + assert isinstance(font.prop['CharStrings']['Pi'], bytes) + assert font._abbr['ND'] == 'ND' differ = difflib.Differ() diff = list(differ.compare( @@ -24,14 +50,13 @@ def test_Type1Font(): slanted.parts[0].decode('latin-1').splitlines())) for line in ( # Removes UniqueID - '- FontDirectory/CMR10 known{/CMR10 findfont dup/UniqueID known{dup', - '+ FontDirectory/CMR10 known{/CMR10 findfont dup', + '- /UniqueID 5000793 def', # Changes the font name '- /FontName /CMR10 def', - '+ /FontName /CMR10_Slant_1000 def', + '+ /FontName/CMR10_Slant_1000 def', # Alters FontMatrix '- /FontMatrix [0.001 0 0 0.001 0 0 ]readonly def', - '+ /FontMatrix [0.001 0 0.001 0.001 0 0]readonly def', + '+ /FontMatrix [0.001 0 0.001 0.001 0 0] readonly def', # Alters ItalicAngle '- /ItalicAngle 0 def', '+ /ItalicAngle -45.0 def'): @@ -42,17 +67,73 @@ def test_Type1Font(): condensed.parts[0].decode('latin-1').splitlines())) for line in ( # Removes UniqueID - '- FontDirectory/CMR10 known{/CMR10 findfont dup/UniqueID known{dup', - '+ FontDirectory/CMR10 known{/CMR10 findfont dup', + '- /UniqueID 5000793 def', # Changes the font name '- /FontName /CMR10 def', - '+ /FontName /CMR10_Extend_500 def', + '+ /FontName/CMR10_Extend_500 def', # Alters FontMatrix '- /FontMatrix [0.001 0 0 0.001 0 0 ]readonly def', - '+ /FontMatrix [0.0005 0 0 0.001 0 0]readonly def'): + '+ /FontMatrix [0.0005 0 0 0.001 0 0] readonly def'): assert line in diff, 'diff to condensed font must contain %s' % line +def test_Type1Font_2(): + filename = os.path.join(os.path.dirname(__file__), + 'Courier10PitchBT-Bold.pfb') + font = t1f.Type1Font(filename) + assert font.prop['Weight'] == 'Bold' + assert font.prop['isFixedPitch'] + assert font.prop['Encoding'][65] == 'A' # the font uses StandardEncoding + (pos0, pos1), = font._pos['Encoding'] + assert font.parts[0][pos0:pos1] == b'/Encoding StandardEncoding' + assert font._abbr['ND'] == '|-' + + +def test_tokenize(): + data = (b'1234/abc false -9.81 Foo <<[0 1 2]<0 1ef a\t>>>\n' + b'(string with(nested\t\\) par)ens\\\\)') + # 1 2 x 2 xx1 + # 1 and 2 are matching parens, x means escaped character + n, w, num, kw, d = 'name', 'whitespace', 'number', 'keyword', 'delimiter' + b, s = 'boolean', 'string' + correct = [ + (num, 1234), (n, 'abc'), (w, ' '), (b, False), (w, ' '), (num, -9.81), + (w, ' '), (kw, 'Foo'), (w, ' '), (d, '<<'), (d, '['), (num, 0), + (w, ' '), (num, 1), (w, ' '), (num, 2), (d, ']'), (s, b'\x01\xef\xa0'), + (d, '>>'), (w, '\n'), (s, 'string with(nested\t) par)ens\\') + ] + correct_no_ws = [x for x in correct if x[0] != w] + + def convert(tokens): + return [(t.kind, t.value()) for t in tokens] + + assert convert(t1f._tokenize(data, False)) == correct + assert convert(t1f._tokenize(data, True)) == correct_no_ws + + def bin_after(n): + tokens = t1f._tokenize(data, True) + result = [] + for _ in range(n): + result.append(next(tokens)) + result.append(tokens.send(10)) + return convert(result) + + for n in range(1, len(correct_no_ws)): + result = bin_after(n) + assert result[:-1] == correct_no_ws[:n] + assert result[-1][0] == 'binary' + assert isinstance(result[-1][1], bytes) + + +def test_tokenize_errors(): + with pytest.raises(ValueError): + list(t1f._tokenize(b'1234 (this (string) is unterminated\\)', True)) + with pytest.raises(ValueError): + list(t1f._tokenize(b'/Foo<01234', True)) + with pytest.raises(ValueError): + list(t1f._tokenize(b'/Foo<01234abcg>/Bar', True)) + + def test_overprecision(): # We used to output too many digits in FontMatrix entries and # ItalicAngle, which could make Type-1 parsers unhappy. diff --git a/lib/matplotlib/text.py b/lib/matplotlib/text.py index 3c0bc9965ed1..c9f7c75851d5 100644 --- a/lib/matplotlib/text.py +++ b/lib/matplotlib/text.py @@ -1987,18 +1987,7 @@ def draw(self, renderer): Text.draw(self, renderer) def get_window_extent(self, renderer=None): - """ - Return the `.Bbox` bounding the text and arrow, in display units. - - Parameters - ---------- - renderer : Renderer, optional - A renderer is needed to compute the bounding box. If the artist - has already been drawn, the renderer is cached; thus, it is only - necessary to pass this argument when calling `get_window_extent` - before the first `draw`. In practice, it is usually easier to - trigger a draw first (e.g. by saving the figure). - """ + # docstring inherited # This block is the same as in Text.get_window_extent, but we need to # set the renderer before calling update_positions(). if not self.get_visible() or not self._check_xy(renderer): diff --git a/lib/matplotlib/textpath.py b/lib/matplotlib/textpath.py index 9b14e79ec2d2..5ef56e4be885 100644 --- a/lib/matplotlib/textpath.py +++ b/lib/matplotlib/textpath.py @@ -279,7 +279,7 @@ def get_glyphs_tex(self, prop, s, glyph_map=None, @staticmethod @functools.lru_cache(50) def _get_ps_font_and_encoding(texname): - tex_font_map = dviread.PsfontsMap(dviread.find_tex_file('pdftex.map')) + tex_font_map = dviread.PsfontsMap(dviread._find_tex_file('pdftex.map')) psfont = tex_font_map[texname] if psfont.filename is None: raise ValueError( diff --git a/lib/matplotlib/type1font.py b/lib/matplotlib/type1font.py index f417c0fc97a4..0413cb0016a0 100644 --- a/lib/matplotlib/type1font.py +++ b/lib/matplotlib/type1font.py @@ -7,10 +7,10 @@ Usage:: - >>> font = Type1Font(filename) - >>> clear_part, encrypted_part, finale = font.parts - >>> slanted_font = font.transform({'slant': 0.167}) - >>> extended_font = font.transform({'extend': 1.2}) + font = Type1Font(filename) + clear_part, encrypted_part, finale = font.parts + slanted_font = font.transform({'slant': 0.167}) + extended_font = font.transform({'extend': 1.2}) Sources: @@ -22,10 +22,10 @@ """ import binascii -import enum -import itertools +import functools import logging import re +import string import struct import numpy as np @@ -35,9 +35,287 @@ _log = logging.getLogger(__name__) -# token types -_TokenType = enum.Enum('_TokenType', - 'whitespace name string delimiter number') + +class _Token: + """ + A token in a PostScript stream. + + Attributes + ---------- + pos : int + Position, i.e. offset from the beginning of the data. + raw : str + Raw text of the token. + kind : str + Description of the token (for debugging or testing). + """ + __slots__ = ('pos', 'raw') + kind = '?' + + def __init__(self, pos, raw): + _log.debug('type1font._Token %s at %d: %r', self.kind, pos, raw) + self.pos = pos + self.raw = raw + + def __str__(self): + return f"<{self.kind} {self.raw} @{self.pos}>" + + def endpos(self): + """Position one past the end of the token""" + return self.pos + len(self.raw) + + def is_keyword(self, *names): + """Is this a name token with one of the names?""" + return False + + def is_slash_name(self): + """Is this a name token that starts with a slash?""" + return False + + def is_delim(self): + """Is this a delimiter token?""" + return False + + def is_number(self): + """Is this a number token?""" + return False + + def value(self): + return self.raw + + +class _NameToken(_Token): + kind = 'name' + + def is_slash_name(self): + return self.raw.startswith('/') + + def value(self): + return self.raw[1:] + + +class _BooleanToken(_Token): + kind = 'boolean' + + def value(self): + return self.raw == 'true' + + +class _KeywordToken(_Token): + kind = 'keyword' + + def is_keyword(self, *names): + return self.raw in names + + +class _DelimiterToken(_Token): + kind = 'delimiter' + + def is_delim(self): + return True + + def opposite(self): + return {'[': ']', ']': '[', + '{': '}', '}': '{', + '<<': '>>', '>>': '<<' + }[self.raw] + + +class _WhitespaceToken(_Token): + kind = 'whitespace' + + +class _StringToken(_Token): + kind = 'string' + _escapes_re = re.compile(r'\\([\\()nrtbf]|[0-7]{1,3})') + _replacements = {'\\': '\\', '(': '(', ')': ')', 'n': '\n', + 'r': '\r', 't': '\t', 'b': '\b', 'f': '\f'} + _ws_re = re.compile('[\0\t\r\f\n ]') + + @classmethod + def _escape(cls, match): + group = match.group(1) + try: + return cls._replacements[group] + except KeyError: + return chr(int(group, 8)) + + @functools.lru_cache() + def value(self): + if self.raw[0] == '(': + return self._escapes_re.sub(self._escape, self.raw[1:-1]) + else: + data = self._ws_re.sub('', self.raw[1:-1]) + if len(data) % 2 == 1: + data += '0' + return binascii.unhexlify(data) + + +class _BinaryToken(_Token): + kind = 'binary' + + def value(self): + return self.raw[1:] + + +class _NumberToken(_Token): + kind = 'number' + + def is_number(self): + return True + + def value(self): + if '.' not in self.raw: + return int(self.raw) + else: + return float(self.raw) + + +def _tokenize(data: bytes, skip_ws: bool): + """ + A generator that produces _Token instances from Type-1 font code. + + The consumer of the generator may send an integer to the tokenizer to + indicate that the next token should be _BinaryToken of the given length. + + Parameters + ---------- + data : bytes + The data of the font to tokenize. + + skip_ws : bool + If true, the generator will drop any _WhitespaceTokens from the output. + """ + + text = data.decode('ascii', 'replace') + whitespace_or_comment_re = re.compile(r'[\0\t\r\f\n ]+|%[^\r\n]*') + token_re = re.compile(r'/{0,2}[^]\0\t\r\f\n ()<>{}/%[]+') + instring_re = re.compile(r'[()\\]') + hex_re = re.compile(r'^<[0-9a-fA-F\0\t\r\f\n ]*>$') + oct_re = re.compile(r'[0-7]{1,3}') + pos = 0 + next_binary = None + + while pos < len(text): + if next_binary is not None: + n = next_binary + next_binary = (yield _BinaryToken(pos, data[pos:pos+n])) + pos += n + continue + match = whitespace_or_comment_re.match(text, pos) + if match: + if not skip_ws: + next_binary = (yield _WhitespaceToken(pos, match.group())) + pos = match.end() + elif text[pos] == '(': + # PostScript string rules: + # - parentheses must be balanced + # - backslashes escape backslashes and parens + # - also codes \n\r\t\b\f and octal escapes are recognized + # - other backslashes do not escape anything + start = pos + pos += 1 + depth = 1 + while depth: + match = instring_re.search(text, pos) + if match is None: + raise ValueError( + f'Unterminated string starting at {start}') + pos = match.end() + if match.group() == '(': + depth += 1 + elif match.group() == ')': + depth -= 1 + else: # a backslash + char = text[pos] + if char in r'\()nrtbf': + pos += 1 + else: + octal = oct_re.match(text, pos) + if octal: + pos = octal.end() + else: + pass # non-escaping backslash + next_binary = (yield _StringToken(start, text[start:pos])) + elif text[pos:pos + 2] in ('<<', '>>'): + next_binary = (yield _DelimiterToken(pos, text[pos:pos + 2])) + pos += 2 + elif text[pos] == '<': + start = pos + try: + pos = text.index('>', pos) + 1 + except ValueError as e: + raise ValueError(f'Unterminated hex string starting at {start}' + ) from e + if not hex_re.match(text[start:pos]): + raise ValueError(f'Malformed hex string starting at {start}') + next_binary = (yield _StringToken(pos, text[start:pos])) + else: + match = token_re.match(text, pos) + if match: + raw = match.group() + if raw.startswith('/'): + next_binary = (yield _NameToken(pos, raw)) + elif match.group() in ('true', 'false'): + next_binary = (yield _BooleanToken(pos, raw)) + else: + try: + float(raw) + next_binary = (yield _NumberToken(pos, raw)) + except ValueError: + next_binary = (yield _KeywordToken(pos, raw)) + pos = match.end() + else: + next_binary = (yield _DelimiterToken(pos, text[pos])) + pos += 1 + + +class _BalancedExpression(_Token): + pass + + +def _expression(initial, tokens, data): + """ + Consume some number of tokens and return a balanced PostScript expression. + + Parameters + ---------- + initial : _Token + The token that triggered parsing a balanced expression. + tokens : iterator of _Token + Following tokens. + data : bytes + Underlying data that the token positions point to. + + Returns + ------- + _BalancedExpression + """ + delim_stack = [] + token = initial + while True: + if token.is_delim(): + if token.raw in ('[', '{'): + delim_stack.append(token) + elif token.raw in (']', '}'): + if not delim_stack: + raise RuntimeError(f"unmatched closing token {token}") + match = delim_stack.pop() + if match.raw != token.opposite(): + raise RuntimeError( + f"opening token {match} closed by {token}" + ) + if not delim_stack: + break + else: + raise RuntimeError(f'unknown delimiter {token}') + elif not delim_stack: + break + token = next(tokens) + return _BalancedExpression( + initial.pos, + data[initial.pos:token.endpos()].decode('ascii', 'replace') + ) class Type1Font: @@ -49,12 +327,29 @@ class Type1Font: parts : tuple A 3-tuple of the cleartext part, the encrypted part, and the finale of zeros. + decrypted : bytes - The decrypted form of parts[1]. + The decrypted form of ``parts[1]``. + prop : dict[str, Any] - A dictionary of font properties. + A dictionary of font properties. Noteworthy keys include: + + - FontName: PostScript name of the font + - Encoding: dict from numeric codes to glyph names + - FontMatrix: bytes object encoding a matrix + - UniqueID: optional font identifier, dropped when modifying the font + - CharStrings: dict from glyph names to byte code + - Subrs: array of byte code subroutines + - OtherSubrs: bytes object encoding some PostScript code """ - __slots__ = ('parts', 'decrypted', 'prop') + __slots__ = ('parts', 'decrypted', 'prop', '_pos', '_abbr') + # the _pos dict contains (begin, end) indices to parts[0] + decrypted + # so that they can be replaced when transforming the font; + # but since sometimes a definition appears in both parts[0] and decrypted, + # _pos[name] is an array of such pairs + # + # _abbr maps three standard abbreviations to their particular names in + # this font (e.g. 'RD' is named '-|' in some fonts) def __init__(self, input): """ @@ -74,6 +369,7 @@ def __init__(self, input): self.parts = self._split(data) self.decrypted = self._decrypt(self.parts[1], 'eexec') + self._abbr = {'RD': 'RD', 'ND': 'ND', 'NP': 'NP'} self._parse() def _read(self, file): @@ -144,14 +440,10 @@ def _split(self, data): return data[:len1], binary, data[idx+1:] - _whitespace_or_comment_re = re.compile(br'[\0\t\r\014\n ]+|%[^\r\n\v]*') - _token_re = re.compile(br'/{0,2}[^]\0\t\r\v\n ()<>{}/%[]+') - _instring_re = re.compile(br'[()\\]') - @staticmethod def _decrypt(ciphertext, key, ndiscard=4): """ - Decrypt ciphertext using the Type-1 font algorithm + Decrypt ciphertext using the Type-1 font algorithm. The algorithm is described in Adobe's "Adobe Type 1 Font Format". The key argument can be an integer, or one of the strings @@ -173,7 +465,7 @@ def _decrypt(ciphertext, key, ndiscard=4): @staticmethod def _encrypt(plaintext, key, ndiscard=4): """ - Encrypt plaintext using the Type-1 font algorithm + Encrypt plaintext using the Type-1 font algorithm. The algorithm is described in Adobe's "Adobe Type 1 Font Format". The key argument can be an integer, or one of the strings @@ -196,101 +488,83 @@ def _encrypt(plaintext, key, ndiscard=4): return bytes(ciphertext) - @classmethod - def _tokens(cls, text): - """ - A PostScript tokenizer. Yield (token, value) pairs such as - (_TokenType.whitespace, ' ') or (_TokenType.name, '/Foobar'). - """ - # Preload enum members for speed. - tok_whitespace = _TokenType.whitespace - tok_name = _TokenType.name - tok_string = _TokenType.string - tok_delimiter = _TokenType.delimiter - tok_number = _TokenType.number - pos = 0 - while pos < len(text): - match = cls._whitespace_or_comment_re.match(text, pos) - if match: - yield (tok_whitespace, match.group()) - pos = match.end() - elif text[pos:pos+1] == b'(': - start = pos - pos += 1 - depth = 1 - while depth: - match = cls._instring_re.search(text, pos) - if match is None: - return - pos = match.end() - if match.group() == b'(': - depth += 1 - elif match.group() == b')': - depth -= 1 - else: # a backslash - skip the next character - pos += 1 - yield (tok_string, text[start:pos]) - elif text[pos:pos + 2] in (b'<<', b'>>'): - yield (tok_delimiter, text[pos:pos + 2]) - pos += 2 - elif text[pos:pos+1] == b'<': - start = pos - pos = text.index(b'>', pos) - yield (tok_string, text[start:pos]) - else: - match = cls._token_re.match(text, pos) - if match: - try: - float(match.group()) - yield (tok_number, match.group()) - except ValueError: - yield (tok_name, match.group()) - pos = match.end() - else: - yield (tok_delimiter, text[pos:pos + 1]) - pos += 1 - def _parse(self): """ Find the values of various font properties. This limited kind of parsing is described in Chapter 10 "Adobe Type Manager Compatibility" of the Type-1 spec. """ - # Preload enum members for speed. - tok_whitespace = _TokenType.whitespace - tok_name = _TokenType.name - tok_string = _TokenType.string - tok_number = _TokenType.number # Start with reasonable defaults - prop = {'weight': 'Regular', 'ItalicAngle': 0.0, 'isFixedPitch': False, + prop = {'Weight': 'Regular', 'ItalicAngle': 0.0, 'isFixedPitch': False, 'UnderlinePosition': -100, 'UnderlineThickness': 50} - filtered = ((token, value) - for token, value in self._tokens(self.parts[0]) - if token is not tok_whitespace) - # The spec calls this an ASCII format; in Python 2.x we could - # just treat the strings and names as opaque bytes but let's - # turn them into proper Unicode, and be lenient in case of high bytes. - def convert(x): return x.decode('ascii', 'replace') - for token, value in filtered: - if token is tok_name and value.startswith(b'/'): - key = convert(value[1:]) - token, value = next(filtered) - if token is tok_name: - if value in (b'true', b'false'): - value = value == b'true' - else: - value = convert(value.lstrip(b'/')) - elif token is tok_string: - value = convert(value.lstrip(b'(').rstrip(b')')) - elif token is tok_number: - if b'.' in value: - value = float(value) - else: - value = int(value) - else: # more complicated value such as an array - value = None - if key != 'FontInfo' and value is not None: - prop[key] = value + pos = {} + data = self.parts[0] + self.decrypted + + source = _tokenize(data, True) + while True: + # See if there is a key to be assigned a value + # e.g. /FontName in /FontName /Helvetica def + try: + token = next(source) + except StopIteration: + break + if token.is_delim(): + # skip over this - we want top-level keys only + _expression(token, source, data) + if token.is_slash_name(): + key = token.value() + keypos = token.pos + else: + continue + + # Some values need special parsing + if key in ('Subrs', 'CharStrings', 'Encoding', 'OtherSubrs'): + prop[key], endpos = { + 'Subrs': self._parse_subrs, + 'CharStrings': self._parse_charstrings, + 'Encoding': self._parse_encoding, + 'OtherSubrs': self._parse_othersubrs + }[key](source, data) + pos.setdefault(key, []).append((keypos, endpos)) + continue + + try: + token = next(source) + except StopIteration: + break + + if isinstance(token, _KeywordToken): + # constructs like + # FontDirectory /Helvetica known {...} {...} ifelse + # mean the key was not really a key + continue + + if token.is_delim(): + value = _expression(token, source, data).raw + else: + value = token.value() + + # look for a 'def' possibly preceded by access modifiers + try: + kw = next( + kw for kw in source + if not kw.is_keyword('readonly', 'noaccess', 'executeonly') + ) + except StopIteration: + break + + # sometimes noaccess def and readonly def are abbreviated + if kw.is_keyword('def', self._abbr['ND'], self._abbr['NP']): + prop[key] = value + pos.setdefault(key, []).append((keypos, kw.endpos())) + + # detect the standard abbreviations + if value == '{noaccess def}': + self._abbr['ND'] = key + elif value == '{noaccess put}': + self._abbr['NP'] = key + elif value == '{string currentfile exch readstring pop}': + self._abbr['RD'] = key # Fill in the various *Name properties if 'FontName' not in prop: @@ -303,79 +577,119 @@ def convert(x): return x.decode('ascii', 'replace') extras = ('(?i)([ -](regular|plain|italic|oblique|(semi)?bold|' '(ultra)?light|extra|condensed))+$') prop['FamilyName'] = re.sub(extras, '', prop['FullName']) + # Decrypt the encrypted parts + ndiscard = prop.get('lenIV', 4) + cs = prop['CharStrings'] + for key, value in cs.items(): + cs[key] = self._decrypt(value, 'charstring', ndiscard) + if 'Subrs' in prop: + prop['Subrs'] = [ + self._decrypt(value, 'charstring', ndiscard) + for value in prop['Subrs'] + ] self.prop = prop + self._pos = pos - @classmethod - def _transformer(cls, tokens, slant, extend): - tok_whitespace = _TokenType.whitespace - tok_name = _TokenType.name - - def fontname(name): - result = name - if slant: - result += b'_Slant_%d' % int(1000 * slant) - if extend != 1.0: - result += b'_Extend_%d' % int(1000 * extend) - return result - - def italicangle(angle): - return b'%a' % round( - float(angle) - np.arctan(slant) / np.pi * 180, - 5 + def _parse_subrs(self, tokens, _data): + count_token = next(tokens) + if not count_token.is_number(): + raise RuntimeError( + f"Token following /Subrs must be a number, was {count_token}" ) + count = count_token.value() + array = [None] * count + next(t for t in tokens if t.is_keyword('array')) + for _ in range(count): + next(t for t in tokens if t.is_keyword('dup')) + index_token = next(tokens) + if not index_token.is_number(): + raise RuntimeError( + "Token following dup in Subrs definition must be a " + f"number, was {index_token}" + ) + nbytes_token = next(tokens) + if not nbytes_token.is_number(): + raise RuntimeError( + "Second token following dup in Subrs definition must " + f"be a number, was {nbytes_token}" + ) + token = next(tokens) + if not token.is_keyword(self._abbr['RD']): + raise RuntimeError( + f"Token preceding subr must be {self._abbr['RD']}, " + f"was {token}" + ) + binary_token = tokens.send(1+nbytes_token.value()) + array[index_token.value()] = binary_token.value() + + return array, next(tokens).endpos() - def fontmatrix(array): - array = array.lstrip(b'[').rstrip(b']').split() - array = [float(x) for x in array] - oldmatrix = np.eye(3, 3) - oldmatrix[0:3, 0] = array[::2] - oldmatrix[0:3, 1] = array[1::2] - modifier = np.array([[extend, 0, 0], - [slant, 1, 0], - [0, 0, 1]]) - newmatrix = np.dot(modifier, oldmatrix) - array[::2] = newmatrix[0:3, 0] - array[1::2] = newmatrix[0:3, 1] - return ( - '[%s]' % ' '.join(_format_approx(x, 6) for x in array) - ).encode('ascii') - - def replace(fun): - def replacer(tokens): - token, value = next(tokens) # name, e.g., /FontMatrix - yield value - token, value = next(tokens) # possible whitespace - while token is tok_whitespace: - yield value - token, value = next(tokens) - if value != b'[': # name/number/etc. - yield fun(value) - else: # array, e.g., [1 2 3] - result = b'' - while value != b']': - result += value - token, value = next(tokens) - result += value - yield fun(result) - return replacer - - def suppress(tokens): - for _ in itertools.takewhile(lambda x: x[1] != b'def', tokens): - pass - yield b'' - - table = {b'/FontName': replace(fontname), - b'/ItalicAngle': replace(italicangle), - b'/FontMatrix': replace(fontmatrix), - b'/UniqueID': suppress} - - for token, value in tokens: - if token is tok_name and value in table: - yield from table[value]( - itertools.chain([(token, value)], tokens)) - else: - yield value + @staticmethod + def _parse_charstrings(tokens, _data): + count_token = next(tokens) + if not count_token.is_number(): + raise RuntimeError( + "Token following /CharStrings must be a number, " + f"was {count_token}" + ) + count = count_token.value() + charstrings = {} + next(t for t in tokens if t.is_keyword('begin')) + while True: + token = next(t for t in tokens + if t.is_keyword('end') or t.is_slash_name()) + if token.raw == 'end': + return charstrings, token.endpos() + glyphname = token.value() + nbytes_token = next(tokens) + if not nbytes_token.is_number(): + raise RuntimeError( + f"Token following /{glyphname} in CharStrings definition " + f"must be a number, was {nbytes_token}" + ) + next(tokens) # usually RD or |- + binary_token = tokens.send(1+nbytes_token.value()) + charstrings[glyphname] = binary_token.value() + + @staticmethod + def _parse_encoding(tokens, _data): + # this only works for encodings that follow the Adobe manual + # but some old fonts include non-compliant data - we log a warning + # and return a possibly incomplete encoding + encoding = {} + while True: + token = next(t for t in tokens + if t.is_keyword('StandardEncoding', 'dup', 'def')) + if token.is_keyword('StandardEncoding'): + return _StandardEncoding, token.endpos() + if token.is_keyword('def'): + return encoding, token.endpos() + index_token = next(tokens) + if not index_token.is_number(): + _log.warning( + f"Parsing encoding: expected number, got {index_token}" + ) + continue + name_token = next(tokens) + if not name_token.is_slash_name(): + _log.warning( + f"Parsing encoding: expected slash-name, got {name_token}" + ) + continue + encoding[index_token.value()] = name_token.value() + + @staticmethod + def _parse_othersubrs(tokens, data): + init_pos = None + while True: + token = next(tokens) + if init_pos is None: + init_pos = token.pos + if token.is_delim(): + _expression(token, tokens, data) + elif token.is_keyword('def', 'ND', '|-'): + return data[init_pos:token.endpos()], token.endpos() def transform(self, effects): """ @@ -397,8 +711,167 @@ def transform(self, effects): ------- `Type1Font` """ - tokenizer = self._tokens(self.parts[0]) - transformed = self._transformer(tokenizer, - slant=effects.get('slant', 0.0), - extend=effects.get('extend', 1.0)) - return Type1Font((b"".join(transformed), self.parts[1], self.parts[2])) + fontname = self.prop['FontName'] + italicangle = self.prop['ItalicAngle'] + + array = [ + float(x) for x in (self.prop['FontMatrix'] + .lstrip('[').rstrip(']').split()) + ] + oldmatrix = np.eye(3, 3) + oldmatrix[0:3, 0] = array[::2] + oldmatrix[0:3, 1] = array[1::2] + modifier = np.eye(3, 3) + + if 'slant' in effects: + slant = effects['slant'] + fontname += '_Slant_%d' % int(1000 * slant) + italicangle = round( + float(italicangle) - np.arctan(slant) / np.pi * 180, + 5 + ) + modifier[1, 0] = slant + + if 'extend' in effects: + extend = effects['extend'] + fontname += '_Extend_%d' % int(1000 * extend) + modifier[0, 0] = extend + + newmatrix = np.dot(modifier, oldmatrix) + array[::2] = newmatrix[0:3, 0] + array[1::2] = newmatrix[0:3, 1] + fontmatrix = ( + '[%s]' % ' '.join(_format_approx(x, 6) for x in array) + ) + replacements = ( + [(x, '/FontName/%s def' % fontname) + for x in self._pos['FontName']] + + [(x, '/ItalicAngle %a def' % italicangle) + for x in self._pos['ItalicAngle']] + + [(x, '/FontMatrix %s readonly def' % fontmatrix) + for x in self._pos['FontMatrix']] + + [(x, '') for x in self._pos.get('UniqueID', [])] + ) + + data = bytearray(self.parts[0]) + data.extend(self.decrypted) + len0 = len(self.parts[0]) + for (pos0, pos1), value in sorted(replacements, reverse=True): + data[pos0:pos1] = value.encode('ascii', 'replace') + if pos0 < len(self.parts[0]): + if pos1 >= len(self.parts[0]): + raise RuntimeError( + f"text to be replaced with {value} spans " + "the eexec boundary" + ) + len0 += len(value) - pos1 + pos0 + + data = bytes(data) + return Type1Font(( + data[:len0], + self._encrypt(data[len0:], 'eexec'), + self.parts[2] + )) + + +_StandardEncoding = { + **{ord(letter): letter for letter in string.ascii_letters}, + 0: '.notdef', + 32: 'space', + 33: 'exclam', + 34: 'quotedbl', + 35: 'numbersign', + 36: 'dollar', + 37: 'percent', + 38: 'ampersand', + 39: 'quoteright', + 40: 'parenleft', + 41: 'parenright', + 42: 'asterisk', + 43: 'plus', + 44: 'comma', + 45: 'hyphen', + 46: 'period', + 47: 'slash', + 48: 'zero', + 49: 'one', + 50: 'two', + 51: 'three', + 52: 'four', + 53: 'five', + 54: 'six', + 55: 'seven', + 56: 'eight', + 57: 'nine', + 58: 'colon', + 59: 'semicolon', + 60: 'less', + 61: 'equal', + 62: 'greater', + 63: 'question', + 64: 'at', + 91: 'bracketleft', + 92: 'backslash', + 93: 'bracketright', + 94: 'asciicircum', + 95: 'underscore', + 96: 'quoteleft', + 123: 'braceleft', + 124: 'bar', + 125: 'braceright', + 126: 'asciitilde', + 161: 'exclamdown', + 162: 'cent', + 163: 'sterling', + 164: 'fraction', + 165: 'yen', + 166: 'florin', + 167: 'section', + 168: 'currency', + 169: 'quotesingle', + 170: 'quotedblleft', + 171: 'guillemotleft', + 172: 'guilsinglleft', + 173: 'guilsinglright', + 174: 'fi', + 175: 'fl', + 177: 'endash', + 178: 'dagger', + 179: 'daggerdbl', + 180: 'periodcentered', + 182: 'paragraph', + 183: 'bullet', + 184: 'quotesinglbase', + 185: 'quotedblbase', + 186: 'quotedblright', + 187: 'guillemotright', + 188: 'ellipsis', + 189: 'perthousand', + 191: 'questiondown', + 193: 'grave', + 194: 'acute', + 195: 'circumflex', + 196: 'tilde', + 197: 'macron', + 198: 'breve', + 199: 'dotaccent', + 200: 'dieresis', + 202: 'ring', + 203: 'cedilla', + 205: 'hungarumlaut', + 206: 'ogonek', + 207: 'caron', + 208: 'emdash', + 225: 'AE', + 227: 'ordfeminine', + 232: 'Lslash', + 233: 'Oslash', + 234: 'OE', + 235: 'ordmasculine', + 241: 'ae', + 245: 'dotlessi', + 248: 'lslash', + 249: 'oslash', + 250: 'oe', + 251: 'germandbls', +} diff --git a/lib/mpl_toolkits/mplot3d/axes3d.py b/lib/mpl_toolkits/mplot3d/axes3d.py index b087229ce432..25d9e9fde457 100644 --- a/lib/mpl_toolkits/mplot3d/axes3d.py +++ b/lib/mpl_toolkits/mplot3d/axes3d.py @@ -2103,8 +2103,9 @@ def contour(self, X, Y, Z, *args, Parameters ---------- - X, Y, Z : array-like - Input data. + X, Y, Z : array-like, + Input data. See `~matplotlib.axes.Axes.contour` for acceptable + data shapes. extend3d : bool, default: False Whether to extend contour in 3D. stride : int @@ -2148,7 +2149,8 @@ def tricontour(self, *args, Parameters ---------- X, Y, Z : array-like - Input data. + Input data. See `~matplotlib.axes.Axes.tricontour` for acceptable + data shapes. extend3d : bool, default: False Whether to extend contour in 3D. stride : int @@ -2206,7 +2208,8 @@ def contourf(self, X, Y, Z, *args, zdir='z', offset=None, **kwargs): Parameters ---------- X, Y, Z : array-like - Input data. + Input data. See `~matplotlib.axes.Axes.contourf` for acceptable + data shapes. zdir : {'x', 'y', 'z'}, default: 'z' The direction to use. offset : float, optional @@ -2244,7 +2247,8 @@ def tricontourf(self, *args, zdir='z', offset=None, **kwargs): Parameters ---------- X, Y, Z : array-like - Input data. + Input data. See `~matplotlib.axes.Axes.tricontourf` for acceptable + data shapes. zdir : {'x', 'y', 'z'}, default: 'z' The direction to use. offset : float, optional @@ -2325,7 +2329,7 @@ def scatter(self, xs, ys, zs=0, zdir='z', s=20, c=None, depthshade=True, Parameters ---------- xs, ys : array-like - The data positions. + The data positions. zs : float or array-like, default: 0 The z-positions. Either an array of the same length as *xs* and *ys* or a single value to place all points in the same plane. diff --git a/lib/mpl_toolkits/mplot3d/axis3d.py b/lib/mpl_toolkits/mplot3d/axis3d.py index adced6f4c21d..7d6110c25a47 100644 --- a/lib/mpl_toolkits/mplot3d/axis3d.py +++ b/lib/mpl_toolkits/mplot3d/axis3d.py @@ -451,7 +451,7 @@ def draw(self, renderer): # TODO: Get this to work (more) properly when mplot3d supports the # transforms framework. def get_tightbbox(self, renderer, *, for_layout_only=False): - # inherited docstring + # docstring inherited if not self.get_visible(): return # We have to directly access the internal data structures diff --git a/plot_types/arrays/barbs.py b/plot_types/arrays/barbs.py new file mode 100644 index 000000000000..a54424a58b46 --- /dev/null +++ b/plot_types/arrays/barbs.py @@ -0,0 +1,33 @@ +""" +================ +barbs(X, Y U, V) +================ + +See `~matplotlib.axes.Axes.barbs`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery-nogrid') + +# make data: +X, Y = np.meshgrid([1, 2, 3, 4], [1, 2, 3, 4]) +angle = np.pi / 180 * np.array([[15., 30, 35, 45], + [25., 40, 55, 60], + [35., 50, 65, 75], + [45., 60, 75, 90]]) +amplitude = np.array([[5, 10, 25, 50], + [10, 15, 30, 60], + [15, 26, 50, 70], + [20, 45, 80, 100]]) +U = amplitude * np.sin(angle) +V = amplitude * np.cos(angle) + +# plot: +fig, ax = plt.subplots() + +ax.barbs(X, Y, U, V, barbcolor="C0", flagcolor="C0", length=7, linewidth=1.5) + +ax.set(xlim=(0, 4.5), ylim=(0, 4.5)) + +plt.show() diff --git a/plot_types/arrays/imshow.py b/plot_types/arrays/imshow.py index beca827cdc80..be647d1f2924 100644 --- a/plot_types/arrays/imshow.py +++ b/plot_types/arrays/imshow.py @@ -12,9 +12,8 @@ plt.style.use('_mpl-gallery-nogrid') # make data -X, Y = np.meshgrid(np.linspace(-3, 3, 256), np.linspace(-3, 3, 256)) +X, Y = np.meshgrid(np.linspace(-3, 3, 16), np.linspace(-3, 3, 16)) Z = (1 - X/2 + X**5 + Y**3) * np.exp(-X**2 - Y**2) -Z = Z[::16, ::16] # plot fig, ax = plt.subplots() diff --git a/plot_types/arrays/pcolormesh.py b/plot_types/arrays/pcolormesh.py index dad94dd2ae5d..b490dcb99d3f 100644 --- a/plot_types/arrays/pcolormesh.py +++ b/plot_types/arrays/pcolormesh.py @@ -12,18 +12,11 @@ plt.style.use('_mpl-gallery-nogrid') -# make full-res data -X, Y = np.meshgrid(np.linspace(-3, 3, 256), np.linspace(-3, 3, 256)) +# make data with uneven sampling in x +x = [-3, -2, -1.6, -1.2, -.8, -.5, -.2, .1, .3, .5, .8, 1.1, 1.5, 1.9, 2.3, 3] +X, Y = np.meshgrid(x, np.linspace(-3, 3, 128)) Z = (1 - X/2 + X**5 + Y**3) * np.exp(-X**2 - Y**2) -# sample unevenly in x: -dx = np.sqrt((np.arange(16) - 8)**2) + 6 -dx = np.floor(dx / sum(dx) * 255) -xint = np.cumsum(dx).astype('int') -X = X[0, xint] -Y = Y[::8, 0] -Z = Z[::8, :][:, xint] - # plot fig, ax = plt.subplots() diff --git a/plot_types/basic/fill_between.py b/plot_types/basic/fill_between.py new file mode 100644 index 000000000000..a454c3c30772 --- /dev/null +++ b/plot_types/basic/fill_between.py @@ -0,0 +1,29 @@ +""" +======================= +fill_between(x, y1, y2) +======================= + +See `~matplotlib.axes.Axes.fill_between`. +""" + +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# make data +np.random.seed(1) +x = np.linspace(0, 8, 16) +y1 = 3 + 4*x/8 + np.random.uniform(0.0, 0.5, len(x)) +y2 = 1 + 2*x/8 + np.random.uniform(0.0, 0.5, len(x)) + +# plot +fig, ax = plt.subplots() + +ax.fill_between(x, y1, y2, alpha=.5, linewidth=0) +ax.plot(x, (y1 + y2)/2, linewidth=2) + +ax.set(xlim=(0, 8), xticks=np.arange(1, 8), + ylim=(0, 8), yticks=np.arange(1, 8)) + +plt.show() diff --git a/plot_types/stats/barbs.py b/plot_types/stats/barbs.py deleted file mode 100644 index 7b632139662d..000000000000 --- a/plot_types/stats/barbs.py +++ /dev/null @@ -1,28 +0,0 @@ -""" -=========== -barbs(U, V) -=========== - -See `~matplotlib.axes.Axes.barbs`. -""" -import matplotlib.pyplot as plt -import numpy as np - -plt.style.use('_mpl-gallery') - -# make data: -np.random.seed(1) -X = [[2, 4, 6]] -Y = [[1.5, 3, 2]] -U = np.zeros_like(X) -V = -np.ones_like(X) * np.linspace(50, 100, 3) - -# plot: -fig, ax = plt.subplots() - -ax.barbs(X, Y, U, V, barbcolor="C0", flagcolor="C0", length=10, linewidth=1.5) - -ax.set(xlim=(0, 8), xticks=np.arange(1, 8), - ylim=(0, 8), yticks=np.arange(1, 8)) - -plt.show() diff --git a/plot_types/basic/pie.py b/plot_types/stats/pie.py similarity index 100% rename from plot_types/basic/pie.py rename to plot_types/stats/pie.py diff --git a/requirements/doc/doc-requirements.txt b/requirements/doc/doc-requirements.txt index 8be10d4d107c..30b7c0c88669 100644 --- a/requirements/doc/doc-requirements.txt +++ b/requirements/doc/doc-requirements.txt @@ -12,13 +12,10 @@ colorspacious ipython ipywidgets numpydoc>=0.8 +packaging>=20 pydata-sphinx-theme>=0.6.0 sphinxcontrib-svg2pdfconverter>=1.1.0 -# sphinx-gallery>=0.7 -# b41e328 is PR 808 which adds the image_srcset directive. When this is -# released with sphinx gallery, we can change to the last release w/o this feature: -# sphinx-gallery>0.90 -git+git://github.com/sphinx-gallery/sphinx-gallery@b41e328#egg=sphinx-gallery +sphinx-gallery>=0.10 sphinx-copybutton sphinx-panels scipy diff --git a/setupext.py b/setupext.py index 0d85f479d106..5320ee3a518b 100644 --- a/setupext.py +++ b/setupext.py @@ -607,12 +607,25 @@ def do_custom_build(self, env): print(f"Building freetype in {src_path}") if sys.platform != 'win32': # compilation on non-windows - env = {**env, "CFLAGS": "{} -fPIC".format(env.get("CFLAGS", ""))} - subprocess.check_call( - ["./configure", "--with-zlib=no", "--with-bzip2=no", - "--with-png=no", "--with-harfbuzz=no", "--enable-static", - "--disable-shared"], - env=env, cwd=src_path) + env = { + **env, + **{ + var: value + for var, value in sysconfig.get_config_vars().items() + if var in {"CC", "CFLAGS", "CXX", "CXXFLAGS", "LD", + "LDFLAGS"} + }, + } + env["CFLAGS"] = env.get("CFLAGS", "") + " -fPIC" + configure = [ + "./configure", "--with-zlib=no", "--with-bzip2=no", + "--with-png=no", "--with-harfbuzz=no", "--enable-static", + "--disable-shared" + ] + host = sysconfig.get_config_var('BUILD_GNU_TYPE') + if host is not None: # May be unset on PyPy. + configure.append(f"--host={host}") + subprocess.check_call(configure, env=env, cwd=src_path) if 'GNUMAKE' in env: make = env['GNUMAKE'] elif 'MAKE' in env: diff --git a/src/ft2font.cpp b/src/ft2font.cpp index e1287c68592e..bb7fb1fa2980 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -20,7 +20,7 @@ To improve the hinting of the fonts, this code uses a hack presented here: - http://antigrain.com/research/font_rasterization/index.html + http://agg.sourceforge.net/antigrain.com/research/font_rasterization/index.html The idea is to limit the effect of hinting in the x-direction, while preserving hinting in the y-direction. Since freetype does not diff --git a/tutorials/introductory/sample_plots.py b/tutorials/introductory/sample_plots.py index dd5f0cc078be..84dd92d3289b 100644 --- a/tutorials/introductory/sample_plots.py +++ b/tutorials/introductory/sample_plots.py @@ -42,7 +42,7 @@ :target: ../../gallery/images_contours_and_fields/image_demo.html :align: center - Example of using :func:`~matplotlib.pyplot.imshow` to display a CT scan + Example of using :func:`~matplotlib.pyplot.imshow` to display an MRI .. _screenshots_pcolormesh_demo: diff --git a/tutorials/introductory/usage.py b/tutorials/introductory/usage.py index 17e623399b65..45253117a892 100644 --- a/tutorials/introductory/usage.py +++ b/tutorials/introductory/usage.py @@ -249,543 +249,3 @@ def my_plotter(ax, data1, data2, param_dict): ############################################################################### # These examples provide convenience for more complex graphs. -# -# -# .. _backends: -# -# Backends -# ======== -# -# .. _what-is-a-backend: -# -# What is a backend? -# ------------------ -# -# A lot of documentation on the website and in the mailing lists refers -# to the "backend" and many new users are confused by this term. -# Matplotlib targets many different use cases and output formats. Some -# people use Matplotlib interactively from the Python shell and have -# plotting windows pop up when they type commands. Some people run -# `Jupyter `_ notebooks and draw inline plots for -# quick data analysis. Others embed Matplotlib into graphical user -# interfaces like PyQt or PyGObject to build rich applications. Some -# people use Matplotlib in batch scripts to generate postscript images -# from numerical simulations, and still others run web application -# servers to dynamically serve up graphs. -# -# To support all of these use cases, Matplotlib can target different -# outputs, and each of these capabilities is called a backend; the -# "frontend" is the user facing code, i.e., the plotting code, whereas the -# "backend" does all the hard work behind-the-scenes to make the figure. -# There are two types of backends: user interface backends (for use in -# PyQt/PySide, PyGObject, Tkinter, wxPython, or macOS/Cocoa); also referred to -# as "interactive backends") and hardcopy backends to make image files -# (PNG, SVG, PDF, PS; also referred to as "non-interactive backends"). -# -# Selecting a backend -# ------------------- -# -# There are three ways to configure your backend: -# -# - The :rc:`backend` parameter in your :file:`matplotlibrc` file -# - The :envvar:`MPLBACKEND` environment variable -# - The function :func:`matplotlib.use` -# -# Below is a more detailed description. -# -# If there is more than one configuration present, the last one from the -# list takes precedence; e.g. calling :func:`matplotlib.use()` will override -# the setting in your :file:`matplotlibrc`. -# -# Without a backend explicitly set, Matplotlib automatically detects a usable -# backend based on what is available on your system and on whether a GUI event -# loop is already running. The first usable backend in the following list is -# selected: MacOSX, QtAgg, GTK4Agg, Gtk3Agg, TkAgg, WxAgg, Agg. The last, Agg, -# is a non-interactive backend that can only write to files. It is used on -# Linux, if Matplotlib cannot connect to either an X display or a Wayland -# display. -# -# Here is a detailed description of the configuration methods: -# -# #. Setting :rc:`backend` in your :file:`matplotlibrc` file:: -# -# backend : qtagg # use pyqt with antigrain (agg) rendering -# -# See also :doc:`/tutorials/introductory/customizing`. -# -# #. Setting the :envvar:`MPLBACKEND` environment variable: -# -# You can set the environment variable either for your current shell or for -# a single script. -# -# On Unix:: -# -# > export MPLBACKEND=qtagg -# > python simple_plot.py -# -# > MPLBACKEND=qtagg python simple_plot.py -# -# On Windows, only the former is possible:: -# -# > set MPLBACKEND=qtagg -# > python simple_plot.py -# -# Setting this environment variable will override the ``backend`` parameter -# in *any* :file:`matplotlibrc`, even if there is a :file:`matplotlibrc` in -# your current working directory. Therefore, setting :envvar:`MPLBACKEND` -# globally, e.g. in your :file:`.bashrc` or :file:`.profile`, is discouraged -# as it might lead to counter-intuitive behavior. -# -# #. If your script depends on a specific backend you can use the function -# :func:`matplotlib.use`:: -# -# import matplotlib -# matplotlib.use('qtagg') -# -# This should be done before any figure is created, otherwise Matplotlib may -# fail to switch the backend and raise an ImportError. -# -# Using `~matplotlib.use` will require changes in your code if users want to -# use a different backend. Therefore, you should avoid explicitly calling -# `~matplotlib.use` unless absolutely necessary. -# -# .. _the-builtin-backends: -# -# The builtin backends -# -------------------- -# -# By default, Matplotlib should automatically select a default backend which -# allows both interactive work and plotting from scripts, with output to the -# screen and/or to a file, so at least initially, you will not need to worry -# about the backend. The most common exception is if your Python distribution -# comes without :mod:`tkinter` and you have no other GUI toolkit installed. -# This happens on certain Linux distributions, where you need to install a -# Linux package named ``python-tk`` (or similar). -# -# If, however, you want to write graphical user interfaces, or a web -# application server -# (:doc:`/gallery/user_interfaces/web_application_server_sgskip`), or need a -# better understanding of what is going on, read on. To make things easily -# more customizable for graphical user interfaces, Matplotlib separates -# the concept of the renderer (the thing that actually does the drawing) -# from the canvas (the place where the drawing goes). The canonical -# renderer for user interfaces is ``Agg`` which uses the `Anti-Grain -# Geometry`_ C++ library to make a raster (pixel) image of the figure; it -# is used by the ``QtAgg``, ``GTK4Agg``, ``GTK3Agg``, ``wxAgg``, ``TkAgg``, and -# ``macosx`` backends. An alternative renderer is based on the Cairo library, -# used by ``QtCairo``, etc. -# -# For the rendering engines, users can also distinguish between `vector -# `_ or `raster -# `_ renderers. Vector -# graphics languages issue drawing commands like "draw a line from this -# point to this point" and hence are scale free. Raster backends -# generate a pixel representation of the line whose accuracy depends on a -# DPI setting. -# -# Here is a summary of the Matplotlib renderers (there is an eponymous -# backend for each; these are *non-interactive backends*, capable of -# writing to a file): -# -# ======== ========= ======================================================= -# Renderer Filetypes Description -# ======== ========= ======================================================= -# AGG png raster_ graphics -- high quality images using the -# `Anti-Grain Geometry`_ engine -# PDF pdf vector_ graphics -- `Portable Document Format`_ -# PS ps, eps vector_ graphics -- Postscript_ output -# SVG svg vector_ graphics -- `Scalable Vector Graphics`_ -# PGF pgf, pdf vector_ graphics -- using the pgf_ package -# Cairo png, ps, raster_ or vector_ graphics -- using the Cairo_ library -# pdf, svg -# ======== ========= ======================================================= -# -# To save plots using the non-interactive backends, use the -# ``matplotlib.pyplot.savefig('filename')`` method. -# -# These are the user interfaces and renderer combinations supported; -# these are *interactive backends*, capable of displaying to the screen -# and using appropriate renderers from the table above to write to -# a file: -# -# ========= ================================================================ -# Backend Description -# ========= ================================================================ -# QtAgg Agg rendering in a Qt_ canvas (requires PyQt_ or `Qt for Python`_, -# a.k.a. PySide). This backend can be activated in IPython with -# ``%matplotlib qt``. -# ipympl Agg rendering embedded in a Jupyter widget. (requires ipympl). -# This backend can be enabled in a Jupyter notebook with -# ``%matplotlib ipympl``. -# GTK3Agg Agg rendering to a GTK_ 3.x canvas (requires PyGObject_, -# and pycairo_ or cairocffi_). This backend can be activated in -# IPython with ``%matplotlib gtk3``. -# GTK4Agg Agg rendering to a GTK_ 4.x canvas (requires PyGObject_, -# and pycairo_ or cairocffi_). This backend can be activated in -# IPython with ``%matplotlib gtk4``. -# macosx Agg rendering into a Cocoa canvas in OSX. This backend can be -# activated in IPython with ``%matplotlib osx``. -# TkAgg Agg rendering to a Tk_ canvas (requires TkInter_). This -# backend can be activated in IPython with ``%matplotlib tk``. -# nbAgg Embed an interactive figure in a Jupyter classic notebook. This -# backend can be enabled in Jupyter notebooks via -# ``%matplotlib notebook``. -# WebAgg On ``show()`` will start a tornado server with an interactive -# figure. -# GTK3Cairo Cairo rendering to a GTK_ 3.x canvas (requires PyGObject_, -# and pycairo_ or cairocffi_). -# GTK4Cairo Cairo rendering to a GTK_ 4.x canvas (requires PyGObject_, -# and pycairo_ or cairocffi_). -# wxAgg Agg rendering to a wxWidgets_ canvas (requires wxPython_ 4). -# This backend can be activated in IPython with ``%matplotlib wx``. -# ========= ================================================================ -# -# .. note:: -# The names of builtin backends case-insensitive; e.g., 'QtAgg' and -# 'qtagg' are equivalent. -# -# .. _`Anti-Grain Geometry`: http://antigrain.com/ -# .. _`Portable Document Format`: https://en.wikipedia.org/wiki/Portable_Document_Format -# .. _Postscript: https://en.wikipedia.org/wiki/PostScript -# .. _`Scalable Vector Graphics`: https://en.wikipedia.org/wiki/Scalable_Vector_Graphics -# .. _pgf: https://ctan.org/pkg/pgf -# .. _Cairo: https://www.cairographics.org -# .. _PyGObject: https://wiki.gnome.org/action/show/Projects/PyGObject -# .. _pycairo: https://www.cairographics.org/pycairo/ -# .. _cairocffi: https://pythonhosted.org/cairocffi/ -# .. _wxPython: https://www.wxpython.org/ -# .. _TkInter: https://docs.python.org/3/library/tk.html -# .. _PyQt: https://riverbankcomputing.com/software/pyqt/intro -# .. _`Qt for Python`: https://doc.qt.io/qtforpython/ -# .. _Qt: https://qt.io/ -# .. _GTK: https://www.gtk.org/ -# .. _Tk: https://www.tcl.tk/ -# .. _wxWidgets: https://www.wxwidgets.org/ -# -# ipympl -# ^^^^^^ -# -# The Jupyter widget ecosystem is moving too fast to support directly in -# Matplotlib. To install ipympl: -# -# .. code-block:: bash -# -# pip install ipympl -# jupyter nbextension enable --py --sys-prefix ipympl -# -# or -# -# .. code-block:: bash -# -# conda install ipympl -c conda-forge -# -# See `jupyter-matplotlib `__ -# for more details. -# -# .. _QT_API-usage: -# -# How do I select PyQt5 or PySide2? -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -# -# The :envvar:`QT_API` environment variable can be set to either ``pyqt5`` or -# ``pyside2`` to use ``PyQt5`` or ``PySide2``, respectively. -# -# Since the default value for the bindings to be used is ``PyQt5``, Matplotlib -# first tries to import it. If the import fails, it tries to import -# ``PySide2``. -# -# Using non-builtin backends -# -------------------------- -# More generally, any importable backend can be selected by using any of the -# methods above. If ``name.of.the.backend`` is the module containing the -# backend, use ``module://name.of.the.backend`` as the backend name, e.g. -# ``matplotlib.use('module://name.of.the.backend')``. -# -# -# .. _interactive-mode: -# -# What is interactive mode? -# ========================= -# -# Use of an interactive backend (see :ref:`what-is-a-backend`) -# permits--but does not by itself require or ensure--plotting -# to the screen. Whether and when plotting to the screen occurs, -# and whether a script or shell session continues after a plot -# is drawn on the screen, depends on the functions and methods -# that are called, and on a state variable that determines whether -# Matplotlib is in "interactive mode." The default Boolean value is set -# by the :file:`matplotlibrc` file, and may be customized like any other -# configuration parameter (see :doc:`/tutorials/introductory/customizing`). It -# may also be set via :func:`matplotlib.interactive`, and its -# value may be queried via :func:`matplotlib.is_interactive`. Turning -# interactive mode on and off in the middle of a stream of plotting -# commands, whether in a script or in a shell, is rarely needed -# and potentially confusing. In the following, we will assume all -# plotting is done with interactive mode either on or off. -# -# .. note:: -# Major changes related to interactivity, and in particular the -# role and behavior of :func:`~matplotlib.pyplot.show`, were made in the -# transition to Matplotlib version 1.0, and bugs were fixed in -# 1.0.1. Here we describe the version 1.0.1 behavior for the -# primary interactive backends, with the partial exception of -# *macosx*. -# -# Interactive mode may also be turned on via :func:`matplotlib.pyplot.ion`, -# and turned off via :func:`matplotlib.pyplot.ioff`. -# -# .. note:: -# Interactive mode works with suitable backends in ipython and in -# the ordinary Python shell, but it does *not* work in the IDLE IDE. -# If the default backend does not support interactivity, an interactive -# backend can be explicitly activated using any of the methods discussed -# in `What is a backend?`_. -# -# -# Interactive example -# -------------------- -# -# From an ordinary Python prompt, or after invoking ipython with no options, -# try this:: -# -# import matplotlib.pyplot as plt -# plt.ion() -# plt.plot([1.6, 2.7]) -# -# This will pop up a plot window. Your terminal prompt will remain active, so -# that you can type additional commands such as:: -# -# plt.title("interactive test") -# plt.xlabel("index") -# -# On most interactive backends, the figure window will also be updated if you -# change it via the object-oriented interface. That is, get a reference to the -# `~matplotlib.axes.Axes` instance, and call a method of that instance:: -# -# ax = plt.gca() -# ax.plot([3.1, 2.2]) -# -# If you are using certain backends (like ``macosx``), or an older version -# of Matplotlib, you may not see the new line added to the plot immediately. -# In this case, you need to explicitly call :func:`~matplotlib.pyplot.draw` -# in order to update the plot:: -# -# plt.draw() -# -# -# Non-interactive example -# ----------------------- -# -# Start a new session as per the previous example, but now -# turn interactive mode off:: -# -# import matplotlib.pyplot as plt -# plt.ioff() -# plt.plot([1.6, 2.7]) -# -# Nothing happened--or at least nothing has shown up on the -# screen (unless you are using *macosx* backend, which is -# anomalous). To make the plot appear, you need to do this:: -# -# plt.show() -# -# Now you see the plot, but your terminal command line is -# unresponsive; `.pyplot.show()` *blocks* the input -# of additional commands until you manually close the plot -# window. -# -# Using a blocking function has benefits to users. Suppose a user -# needs a script that plots the contents of a file to the screen. -# The user may want to look at that plot, and then end the script. -# Without a blocking command such as ``show()``, the script would -# flash up the plot and then end immediately, leaving nothing on -# the screen. -# -# In addition, non-interactive mode delays all drawing until -# ``show()`` is called. This is more efficient than redrawing -# the plot each time a line in the script adds a new feature. -# -# Prior to version 1.0, ``show()`` generally could not be called -# more than once in a single script (although sometimes one -# could get away with it). For version 1.0.1 and above, this -# restriction is lifted, so one can write a script like this:: -# -# import numpy as np -# import matplotlib.pyplot as plt -# -# plt.ioff() -# for i in range(3): -# plt.plot(np.random.rand(10)) -# plt.show() -# -# This makes three plots, one at a time. That is, the second plot will show up -# once the first plot is closed. -# -# Summary -# ------- -# -# In interactive mode, pyplot functions automatically draw -# to the screen. -# -# When plotting interactively, if using -# object method calls in addition to pyplot functions, then -# call :func:`~matplotlib.pyplot.draw` whenever you want to -# refresh the plot. -# -# Use non-interactive mode in scripts in which you want to -# generate one or more figures and display them before ending -# or generating a new set of figures. In that case, use -# :func:`~matplotlib.pyplot.show` to display the figure(s) and -# to block execution until you have manually destroyed them. -# -# .. _performance: -# -# Performance -# =========== -# -# Whether exploring data in interactive mode or programmatically -# saving lots of plots, rendering performance can be a challenging -# bottleneck in your pipeline. Matplotlib provides multiple -# ways to greatly reduce rendering time at the cost of a slight -# change (to a settable tolerance) in your plot's appearance. -# The methods available to reduce rendering time depend on the -# type of plot that is being created. -# -# Line segment simplification -# --------------------------- -# -# For plots that have line segments (e.g. typical line plots, outlines -# of polygons, etc.), rendering performance can be controlled by -# :rc:`path.simplify` and :rc:`path.simplify_threshold`, which -# can be defined e.g. in the :file:`matplotlibrc` file (see -# :doc:`/tutorials/introductory/customizing` for more information about -# the :file:`matplotlibrc` file). :rc:`path.simplify` is a Boolean -# indicating whether or not line segments are simplified at all. -# :rc:`path.simplify_threshold` controls how much line segments are simplified; -# higher thresholds result in quicker rendering. -# -# The following script will first display the data without any -# simplification, and then display the same data with simplification. -# Try interacting with both of them:: -# -# import numpy as np -# import matplotlib.pyplot as plt -# import matplotlib as mpl -# -# # Setup, and create the data to plot -# y = np.random.rand(100000) -# y[50000:] *= 2 -# y[np.geomspace(10, 50000, 400).astype(int)] = -1 -# mpl.rcParams['path.simplify'] = True -# -# mpl.rcParams['path.simplify_threshold'] = 0.0 -# plt.plot(y) -# plt.show() -# -# mpl.rcParams['path.simplify_threshold'] = 1.0 -# plt.plot(y) -# plt.show() -# -# Matplotlib currently defaults to a conservative simplification -# threshold of ``1/9``. To change default settings to use a different -# value, change the :file:`matplotlibrc` file. Alternatively, users -# can create a new style for interactive plotting (with maximal -# simplification) and another style for publication quality plotting -# (with minimal simplification) and activate them as necessary. See -# :doc:`/tutorials/introductory/customizing` for instructions on -# how to perform these actions. -# -# -# The simplification works by iteratively merging line segments -# into a single vector until the next line segment's perpendicular -# distance to the vector (measured in display-coordinate space) -# is greater than the ``path.simplify_threshold`` parameter. -# -# .. note:: -# Changes related to how line segments are simplified were made -# in version 2.1. Rendering time will still be improved by these -# parameters prior to 2.1, but rendering time for some kinds of -# data will be vastly improved in versions 2.1 and greater. -# -# Marker simplification -# --------------------- -# -# Markers can also be simplified, albeit less robustly than -# line segments. Marker simplification is only available -# to :class:`~matplotlib.lines.Line2D` objects (through the -# ``markevery`` property). Wherever -# :class:`~matplotlib.lines.Line2D` construction parameters -# are passed through, such as -# :func:`matplotlib.pyplot.plot` and -# :meth:`matplotlib.axes.Axes.plot`, the ``markevery`` -# parameter can be used:: -# -# plt.plot(x, y, markevery=10) -# -# The ``markevery`` argument allows for naive subsampling, or an -# attempt at evenly spaced (along the *x* axis) sampling. See the -# :doc:`/gallery/lines_bars_and_markers/markevery_demo` -# for more information. -# -# Splitting lines into smaller chunks -# ----------------------------------- -# -# If you are using the Agg backend (see :ref:`what-is-a-backend`), -# then you can make use of :rc:`agg.path.chunksize` -# This allows users to specify a chunk size, and any lines with -# greater than that many vertices will be split into multiple -# lines, each of which has no more than ``agg.path.chunksize`` -# many vertices. (Unless ``agg.path.chunksize`` is zero, in -# which case there is no chunking.) For some kind of data, -# chunking the line up into reasonable sizes can greatly -# decrease rendering time. -# -# The following script will first display the data without any -# chunk size restriction, and then display the same data with -# a chunk size of 10,000. The difference can best be seen when -# the figures are large, try maximizing the GUI and then -# interacting with them:: -# -# import numpy as np -# import matplotlib.pyplot as plt -# import matplotlib as mpl -# mpl.rcParams['path.simplify_threshold'] = 1.0 -# -# # Setup, and create the data to plot -# y = np.random.rand(100000) -# y[50000:] *= 2 -# y[np.geomspace(10, 50000, 400).astype(int)] = -1 -# mpl.rcParams['path.simplify'] = True -# -# mpl.rcParams['agg.path.chunksize'] = 0 -# plt.plot(y) -# plt.show() -# -# mpl.rcParams['agg.path.chunksize'] = 10000 -# plt.plot(y) -# plt.show() -# -# Legends -# ------- -# -# The default legend behavior for axes attempts to find the location -# that covers the fewest data points (``loc='best'``). This can be a -# very expensive computation if there are lots of data points. In -# this case, you may want to provide a specific location. -# -# Using the *fast* style -# ---------------------- -# -# The *fast* style can be used to automatically set -# simplification and chunking parameters to reasonable -# settings to speed up plotting large amounts of data. -# The following code runs it:: -# -# import matplotlib.style as mplstyle -# mplstyle.use('fast') -# -# It is very lightweight, so it works well with other -# styles. Be sure the fast style is applied last -# so that other styles do not overwrite the settings:: -# -# mplstyle.use(['dark_background', 'ggplot', 'fast']) diff --git a/tutorials/text/pgf.py b/tutorials/text/pgf.py index 6f958efe03fc..caedf394f359 100644 --- a/tutorials/text/pgf.py +++ b/tutorials/text/pgf.py @@ -187,6 +187,12 @@ using either the ``rasterized=True`` keyword, or ``.set_rasterized(True)`` as per :doc:`this example `. +* Various math fonts are compiled and rendered only if corresponding font + packages are loaded. Specifically, when using ``\mathbf{}`` on Greek letters, + the default computer modern font may not contain them, in which case the + letter is not rendered. In such scenarios, the ``lmodern`` package should be + loaded. + * If you still need help, please see :ref:`reporting-problems` .. _LaTeX: http://www.tug.org