diff --git a/.appveyor.yml b/.appveyor.yml index 0e8e0a553fd4..df7536f16c7e 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -18,18 +18,15 @@ environment: PYTHONIOENCODING: UTF-8 PYTEST_ARGS: -raR --numprocesses=auto --timeout=300 --durations=25 --cov-report= --cov=lib --log-level=DEBUG - PINNEDVERS: "pyzmq!=21.0.0 pyzmq!=22.0.0" + PINNEDVERS: "pyzmq!=21.0.0,!=22.0.0" matrix: - # In theory we could use a single CONDA_INSTALL_LOCN because we construct - # the envs anyway. But using one for the right python version hopefully - # making things faster due to package caching. - - PYTHON_VERSION: "3.7" - CONDA_INSTALL_LOCN: "C:\\Miniconda37-x64" + - PYTHON_VERSION: "3.8" + CONDA_INSTALL_LOCN: "C:\\Miniconda3-x64" TEST_ALL: "no" EXTRAREQS: "-r requirements/testing/extra.txt" - - PYTHON_VERSION: "3.8" - CONDA_INSTALL_LOCN: "C:\\Miniconda37-x64" + - PYTHON_VERSION: "3.9" + CONDA_INSTALL_LOCN: "C:\\Miniconda3-x64" TEST_ALL: "no" EXTRAREQS: "-r requirements/testing/extra.txt" @@ -55,11 +52,13 @@ install: - conda config --prepend channels conda-forge # For building, use a new environment - - conda create -q -n test-environment python=%PYTHON_VERSION% tk + - conda create -q -n test-environment python=%PYTHON_VERSION% tk "pip<22.0" - activate test-environment # pull pywin32 from conda because on py38 there is something wrong with finding - # the dlls when insalled from pip + # the dlls when installed 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 e70b1befe053..2b6ef7c642f4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,6 +9,34 @@ version: 2.1 # commands: + check-skip: + steps: + - run: + name: Check-skip + command: | + export git_log=$(git log --max-count=1 --pretty=format:"%B" | tr "\n" " ") + echo "Got commit message:" + echo "${git_log}" + if [[ -v CIRCLE_PULL_REQUEST ]] && ([[ "$git_log" == *"[skip circle]"* ]] || [[ "$git_log" == *"[circle skip]"* ]]); then + echo "Skip detected, exiting job ${CIRCLE_JOB} for PR ${CIRCLE_PULL_REQUEST}." + circleci-agent step halt; + fi + + merge: + steps: + - run: + name: Merge with upstream + command: | + if ! git remote -v | grep upstream; then + git remote add upstream https://github.com/matplotlib/matplotlib.git + fi + git fetch upstream + if [[ "$CIRCLE_BRANCH" != "main" ]] && \ + [[ "$CIRCLE_PR_NUMBER" != "" ]]; then + echo "Merging ${CIRCLE_PR_NUMBER}" + git pull --ff-only upstream "refs/pull/${CIRCLE_PR_NUMBER}/merge" + fi + apt-install: steps: - run: @@ -27,10 +55,12 @@ commands: texlive-latex-recommended \ texlive-pictures \ texlive-xetex \ + ttf-wqy-zenhei \ graphviz \ fonts-crosextra-carlito \ fonts-freefont-otf \ fonts-humor-sans \ + fonts-noto-cjk \ optipng fonts-install: @@ -56,9 +86,9 @@ commands: command: | python -m pip install --upgrade --user pip python -m pip install --upgrade --user wheel - python -m pip install --upgrade --user setuptools + python -m pip install --upgrade --user 'setuptools!=60.6.0' - deps-install: + doc-deps-install: parameters: numpy_version: type: string @@ -67,6 +97,8 @@ commands: - run: name: Install Python dependencies command: | + python -m pip install --no-deps --user \ + git+https://github.com/matplotlib/mpl-sphinx-theme.git python -m pip install --user \ numpy<< parameters.numpy_version >> codecov coverage \ -r requirements/doc/doc-requirements.txt @@ -75,7 +107,22 @@ commands: steps: - run: name: Install Matplotlib - command: python -m pip install --user -ve . + command: | + if [[ "$CIRCLE_BRANCH" == v*-doc ]]; then + # The v*-doc branches must build against the specified release. + version=${CIRCLE_BRANCH%-doc} + version=${version#v} + python -m pip install matplotlib==${version} + else + python -m pip install --user -ve . + fi + - save_cache: + key: build-deps-1 + paths: + # FreeType 2.6.1 tarball. + - ~/.cache/matplotlib/0a3c7dfbda6da1e8fce29232e8e96d987ababbbf71ebc8c75659e4132c367014 + # Qhull 2020.2 tarball. + - ~/.cache/matplotlib/b5c2d7eb833278881b952c8a52d20179eab87766b00b865000469a45c1838b7e doc-build: steps: @@ -88,15 +135,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" = "main" ] && \ + [ "$CIRCLE_PR_NUMBER" = "" ]; then + export RELEASE_TAG='-t release' fi - make html O="-T -Ainclude_analytics=$ANALYTICS" + make html O="-T $RELEASE_TAG -j4" rm -r build/html/_sources working_directory: doc - save_cache: @@ -108,7 +153,12 @@ commands: steps: - run: name: Bundle sphinx-gallery documentation artifacts - command: tar cf doc/build/sphinx-gallery-files.tar.gz doc/api/_as_gen doc/gallery doc/tutorials + command: > + tar cf doc/build/sphinx-gallery-files.tar.gz + doc/api/_as_gen + doc/gallery + doc/plot_types + doc/tutorials when: always - store_artifacts: path: doc/build/sphinx-gallery-files.tar.gz @@ -119,59 +169,21 @@ commands: # jobs: - docs-python37: - docker: - - image: circleci/python:3.7 - steps: - - checkout - - - apt-install - - fonts-install - - pip-install - - - deps-install - - mpl-install - - - doc-build - - - doc-bundle - - - store_artifacts: - path: doc/build/html - - docs-python38-min: - docker: - - image: circleci/python:3.8 - steps: - - checkout - - - apt-install - - fonts-install - - pip-install - - - deps-install: - numpy_version: "==1.16.0" - - mpl-install - - - doc-build - - - doc-bundle - - - store_artifacts: - path: doc/build/html - docs-python38: docker: - - image: circleci/python:3.8 + - image: cimg/python:3.8 + resource_class: large steps: - checkout + - check-skip + - merge - apt-install - fonts-install - pip-install - - deps-install - mpl-install + - doc-deps-install - doc-build @@ -179,10 +191,12 @@ jobs: - store_artifacts: path: doc/build/html + - store_test_results: + path: doc/build/test-results - add_ssh_keys: fingerprints: - - "78:13:59:08:61:a9:e5:09:af:df:3a:d8:89:c2:84:c0" + - "6b:83:76:a5:7d:bd:ce:19:a4:e3:81:e0:80:16:a4:fe" - deploy: name: "Deploy new docs" command: ./.circleci/deploy-docs.sh @@ -195,6 +209,4 @@ workflows: version: 2 build: jobs: - - docs-python37 - docs-python38 - - docs-python38-min diff --git a/.circleci/deploy-docs.sh b/.circleci/deploy-docs.sh index 83037d2561a4..8801d5fd073e 100755 --- a/.circleci/deploy-docs.sh +++ b/.circleci/deploy-docs.sh @@ -3,10 +3,10 @@ set -e if [ "$CIRCLE_PROJECT_USERNAME" != "matplotlib" ] || \ - [ "$CIRCLE_BRANCH" != "master" ] || \ + [ "$CIRCLE_BRANCH" != "main" ] || \ [[ "$CIRCLE_PULL_REQUEST" == https://github.com/matplotlib/matplotlib/pull/* ]]; then echo "Not uploading docs for ${CIRCLE_SHA1}"\ - "from non-master branch (${CIRCLE_BRANCH})"\ + "from non-main branch (${CIRCLE_BRANCH})"\ "or pull request (${CIRCLE_PULL_REQUEST})"\ "or non-Matplotlib org (${CIRCLE_PROJECT_USERNAME})." exit diff --git a/.flake8 b/.flake8 index f9f41e9a9fab..4cbcd7e2881f 100644 --- a/.flake8 +++ b/.flake8 @@ -16,8 +16,8 @@ ignore = # flake8 default E121,E123,E126,E226,E24,E704,W503,W504, # Additional ignores: - E122, E127, E131, - E265, E266, + E127, E131, + E266, E305, E306, E722, E741, F841, @@ -35,7 +35,6 @@ exclude = doc/gallery doc/tutorials # External files. - versioneer.py tools/gh_api.py tools/github_stats.py .tox @@ -43,52 +42,31 @@ exclude = per-file-ignores = setup.py: E402 - setupext.py: E501 tests.py: F401 - tools/subset.py: E221, E251, E261, E302, E501 - - lib/matplotlib/__init__.py: F401 + lib/matplotlib/__init__.py: E402, F401 lib/matplotlib/_api/__init__.py: F401 - lib/matplotlib/_cm.py: E202, E203, E302 + lib/matplotlib/_cm.py: E122, E202, E203, E302 lib/matplotlib/_mathtext.py: E221, E251 - lib/matplotlib/_mathtext_data.py: E203, E261 - lib/matplotlib/animation.py: F401 + lib/matplotlib/_mathtext_data.py: E122, E203, E261 lib/matplotlib/_animation_data.py: E501 lib/matplotlib/axes/__init__.py: F401, F403 - lib/matplotlib/axes/_axes.py: F401 - lib/matplotlib/backends/backend_*.py: F401 + lib/matplotlib/backends/backend_template.py: F401 lib/matplotlib/backends/qt_editor/formlayout.py: F401, F403 - lib/matplotlib/cbook/__init__.py: F401 - lib/matplotlib/cbook/deprecation.py: F401 - lib/matplotlib/font_manager.py: E221, E251, E501 + lib/matplotlib/font_manager.py: E501 lib/matplotlib/image.py: F401, F403 - lib/matplotlib/lines.py: F401 lib/matplotlib/mathtext.py: E221, E251 lib/matplotlib/pylab.py: F401, F403 lib/matplotlib/pyplot.py: F401, F811 - lib/matplotlib/style/__init__.py: F401 - lib/matplotlib/testing/conftest.py: F401 - lib/matplotlib/tests/conftest.py: F401 - lib/matplotlib/tests/test_backend_qt.py: F401 lib/matplotlib/tests/test_mathtext.py: E501 - lib/matplotlib/text.py: F401 lib/matplotlib/transforms.py: E201, E202, E203 - lib/matplotlib/tri/__init__.py: F401, F403 lib/matplotlib/tri/triinterpolate.py: E201, E221 - lib/mpl_toolkits/axes_grid/*: F401, F403 - lib/mpl_toolkits/axes_grid1/__init__.py: F401 lib/mpl_toolkits/axes_grid1/axes_size.py: E272 lib/mpl_toolkits/axisartist/__init__.py: F401 lib/mpl_toolkits/axisartist/angle_helper.py: E221 - lib/mpl_toolkits/axisartist/axes_divider.py: F401 - lib/mpl_toolkits/axisartist/axes_rgb.py: F401 - lib/mpl_toolkits/axisartist/axislines.py: F401 - lib/mpl_toolkits/mplot3d/__init__.py: F401 - 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 @@ -97,15 +75,14 @@ per-file-ignores = tutorials/colors/colormap-manipulation.py: E402 tutorials/intermediate/artists.py: E402 tutorials/intermediate/constrainedlayout_guide.py: E402 - tutorials/intermediate/gridspec.py: E402 tutorials/intermediate/legend_guide.py: E402 tutorials/intermediate/tight_layout_guide.py: E402 tutorials/introductory/customizing.py: E501 tutorials/introductory/images.py: E402, E501 tutorials/introductory/pyplot.py: E402, E501 tutorials/introductory/sample_plots.py: E501 - tutorials/introductory/usage.py: E501 - tutorials/text/annotations.py: E501 + tutorials/introductory/quick_start.py: E703 + tutorials/text/annotations.py: E402, E501 tutorials/text/text_intro.py: E402 tutorials/text/text_props.py: E501 tutorials/text/usetex.py: E501 @@ -113,179 +90,26 @@ per-file-ignores = tutorials/toolkits/axisartist.py: E501 examples/animation/frame_grabbing_sgskip.py: E402 - examples/axes_grid1/inset_locator_demo.py: E402 - examples/axes_grid1/scatter_hist_locatable_axes.py: E402 - examples/axisartist/demo_curvelinear_grid.py: E402 - examples/color/color_by_yvalue.py: E402 - examples/color/color_cycle_default.py: E402 - examples/color/color_cycler.py: E402 - examples/color/color_demo.py: E402 - examples/color/colorbar_basics.py: E402 - examples/color/colormap_reference.py: E402 - examples/color/custom_cmap.py: E402 - examples/color/named_colors.py: E402 - examples/images_contours_and_fields/affine_image.py: E402 - examples/images_contours_and_fields/barb_demo.py: E402 - examples/images_contours_and_fields/barcode_demo.py: E402 - examples/images_contours_and_fields/contour_corner_mask.py: E402 - examples/images_contours_and_fields/contour_demo.py: E402 - examples/images_contours_and_fields/contour_image.py: E402 - examples/images_contours_and_fields/contourf_demo.py: E402 - examples/images_contours_and_fields/contourf_hatching.py: E402 - examples/images_contours_and_fields/contourf_log.py: E402 - examples/images_contours_and_fields/demo_bboximage.py: E402 - examples/images_contours_and_fields/image_antialiasing.py: E402 - examples/images_contours_and_fields/image_clip_path.py: E402 - examples/images_contours_and_fields/image_demo.py: E402 - examples/images_contours_and_fields/image_masked.py: E402 - examples/images_contours_and_fields/image_transparency_blend.py: E402 - examples/images_contours_and_fields/image_zcoord.py: E402 - examples/images_contours_and_fields/interpolation_methods.py: E402 - examples/images_contours_and_fields/irregulardatagrid.py: E402 - examples/images_contours_and_fields/layer_images.py: E402 - examples/images_contours_and_fields/matshow.py: E402 - examples/images_contours_and_fields/multi_image.py: E402 - examples/images_contours_and_fields/pcolor_demo.py: E402 - examples/images_contours_and_fields/plot_streamplot.py: E402 - examples/images_contours_and_fields/quadmesh_demo.py: E402 - examples/images_contours_and_fields/quiver_demo.py: E402 - examples/images_contours_and_fields/quiver_simple_demo.py: E402 - examples/images_contours_and_fields/shading_example.py: E402 - examples/images_contours_and_fields/specgram_demo.py: E402 - examples/images_contours_and_fields/spy_demos.py: E402 - examples/images_contours_and_fields/tricontour_demo.py: E201, E402 - examples/images_contours_and_fields/tricontour_smooth_delaunay.py: E402 - examples/images_contours_and_fields/tricontour_smooth_user.py: E402 - examples/images_contours_and_fields/trigradient_demo.py: E402 - examples/images_contours_and_fields/triinterp_demo.py: E402 - examples/images_contours_and_fields/tripcolor_demo.py: E201, E402 - examples/images_contours_and_fields/triplot_demo.py: E201, E402 - examples/images_contours_and_fields/watermark_image.py: E402 - examples/lines_bars_and_markers/curve_error_band.py: E402 - examples/lines_bars_and_markers/errorbar_limits_simple.py: E402 - examples/lines_bars_and_markers/fill.py: E402 - examples/lines_bars_and_markers/fill_between_demo.py: E402 - examples/lines_bars_and_markers/filled_step.py: E402 - examples/lines_bars_and_markers/stairs_demo.py: E402 - examples/lines_bars_and_markers/horizontal_barchart_distribution.py: E402 - examples/lines_bars_and_markers/joinstyle.py: E402 - examples/lines_bars_and_markers/scatter_hist.py: E402 - examples/lines_bars_and_markers/scatter_piecharts.py: E402 - examples/lines_bars_and_markers/scatter_with_legend.py: E402 - examples/lines_bars_and_markers/span_regions.py: E402 - examples/lines_bars_and_markers/stem_plot.py: E402 - examples/lines_bars_and_markers/step_demo.py: E402 - examples/lines_bars_and_markers/timeline.py: E402 - examples/lines_bars_and_markers/xcorr_acorr_demo.py: E402 - examples/misc/agg_buffer.py: E402 - examples/misc/histogram_path.py: E402 + examples/lines_bars_and_markers/marker_reference.py: E402 + examples/images_contours_and_fields/tricontour_demo.py: E201 + examples/images_contours_and_fields/tripcolor_demo.py: E201 + examples/images_contours_and_fields/triplot_demo.py: E201 examples/misc/print_stdout_sgskip.py: E402 - examples/misc/rasterization_demo.py: E402 - examples/misc/svg_filter_line.py: E402 - examples/misc/svg_filter_pie.py: E402 examples/misc/table_demo.py: E201 - examples/mplot3d/surface3d.py: E402 - examples/pie_and_polar_charts/bar_of_pie.py: E402 - examples/pie_and_polar_charts/nested_pie.py: E402 - examples/pie_and_polar_charts/pie_and_donut_labels.py: E402 - examples/pie_and_polar_charts/pie_demo2.py: E402 - examples/pie_and_polar_charts/pie_features.py: E402 - examples/pie_and_polar_charts/polar_bar.py: E402 - examples/pie_and_polar_charts/polar_demo.py: E402 - examples/pie_and_polar_charts/polar_legend.py: E402 - examples/pie_and_polar_charts/polar_scatter.py: E402 - examples/pyplots/align_ylabels.py: E402 - examples/pyplots/annotate_transform.py: E251, E402 - examples/pyplots/annotation_basic.py: E402 - examples/pyplots/annotation_polar.py: E402 - examples/pyplots/auto_subplots_adjust.py: E302, E402 - examples/pyplots/axline.py: E402 - examples/pyplots/boxplot_demo_pyplot.py: E402 - examples/pyplots/dollar_ticks.py: E402 - examples/pyplots/fig_axes_customize_simple.py: E402 - examples/pyplots/fig_axes_labels_simple.py: E402 - examples/pyplots/fig_x.py: E402 - examples/pyplots/pyplot_formatstr.py: E402 - examples/pyplots/pyplot_mathtext.py: E402 - examples/pyplots/pyplot_scales.py: E402 - examples/pyplots/pyplot_simple.py: E402 - examples/pyplots/pyplot_text.py: E402 - examples/pyplots/pyplot_three.py: E402 - examples/pyplots/pyplot_two_subplots.py: E402 - examples/pyplots/text_commands.py: E402 - examples/pyplots/text_layout.py: E402 - examples/pyplots/whats_new_1_subplot3d.py: E402 - examples/pyplots/whats_new_98_4_fill_between.py: E402 - examples/pyplots/whats_new_98_4_legend.py: E402 - examples/pyplots/whats_new_99_axes_grid.py: E402 - examples/pyplots/whats_new_99_mplot3d.py: E402 - examples/pyplots/whats_new_99_spines.py: E402 - examples/scales/power_norm.py: E402 - examples/scales/scales.py: E402 - examples/shapes_and_collections/artist_reference.py: E402 - examples/shapes_and_collections/collections.py: E402 - examples/shapes_and_collections/compound_path.py: E402 - examples/shapes_and_collections/dolphin.py: E402 - examples/shapes_and_collections/donut.py: E402 - examples/shapes_and_collections/ellipse_collection.py: E402 - examples/shapes_and_collections/ellipse_demo.py: E402 - examples/shapes_and_collections/fancybox_demo.py: E402 - examples/shapes_and_collections/hatch_demo.py: E402 - examples/shapes_and_collections/hatch_style_reference.py: E402 - examples/shapes_and_collections/line_collection.py: E402 - examples/shapes_and_collections/marker_path.py: E402 - examples/shapes_and_collections/patch_collection.py: E402 - examples/shapes_and_collections/path_patch.py: E402 - examples/shapes_and_collections/quad_bezier.py: E402 - examples/shapes_and_collections/scatter.py: E402 - examples/showcase/anatomy.py: E402 - examples/showcase/bachelors_degrees_by_gender.py: E402 - examples/showcase/firefox.py: E501 - examples/specialty_plots/anscombe.py: E402 - examples/specialty_plots/radar_chart.py: E402 - examples/specialty_plots/sankey_basics.py: E402 - examples/specialty_plots/sankey_links.py: E402 - examples/specialty_plots/sankey_rankine.py: E402 - examples/specialty_plots/skewt.py: E402 examples/style_sheets/bmh.py: E501 - examples/style_sheets/ggplot.py: E501 examples/style_sheets/plot_solarizedlight2.py: E501 - examples/subplots_axes_and_figures/axes_margins.py: E402 - examples/subplots_axes_and_figures/axes_zoom_effect.py: E402 - examples/subplots_axes_and_figures/custom_figure_class.py: E402 examples/subplots_axes_and_figures/demo_constrained_layout.py: E402 - examples/subplots_axes_and_figures/demo_tight_layout.py: E402 - examples/subplots_axes_and_figures/figure_size_units.py: E402 - examples/subplots_axes_and_figures/secondary_axis.py: E402 - examples/subplots_axes_and_figures/two_scales.py: E402 - examples/subplots_axes_and_figures/zoom_inset_axes.py: E402 - examples/text_labels_and_annotations/date_index_formatter.py: E402 - examples/text_labels_and_annotations/demo_text_rotation_mode.py: E402 examples/text_labels_and_annotations/custom_legends.py: E402 - examples/text_labels_and_annotations/fancyarrow_demo.py: E402 - examples/text_labels_and_annotations/font_family_rc_sgskip.py: E402 - examples/text_labels_and_annotations/font_file.py: E402 - examples/text_labels_and_annotations/legend.py: E402 - examples/text_labels_and_annotations/line_with_text.py: E402 - examples/text_labels_and_annotations/mathtext_asarray.py: E402 - examples/text_labels_and_annotations/tex_demo.py: E402 - examples/text_labels_and_annotations/watermark_text.py: E402 - examples/ticks_and_spines/custom_ticker1.py: E402 - examples/ticks_and_spines/date_concise_formatter.py: E402 - examples/ticks_and_spines/major_minor_demo.py: E402 - examples/ticks_and_spines/tick-formatters.py: E402 - examples/ticks_and_spines/tick_labels_from_values.py: E402 - examples/user_interfaces/canvasagg.py: E402 + examples/ticks/date_concise_formatter.py: E402 + examples/ticks/date_formatters_locators.py: F401 examples/user_interfaces/embedding_in_gtk3_panzoom_sgskip.py: E402 examples/user_interfaces/embedding_in_gtk3_sgskip.py: E402 - examples/user_interfaces/embedding_in_qt_sgskip.py: E402 - examples/user_interfaces/gtk_spreadsheet_sgskip.py: E402 - examples/user_interfaces/mathtext_wx_sgskip.py: E402 + examples/user_interfaces/embedding_in_gtk4_panzoom_sgskip.py: E402 + examples/user_interfaces/embedding_in_gtk4_sgskip.py: E402 + examples/user_interfaces/gtk3_spreadsheet_sgskip.py: E402 + examples/user_interfaces/gtk4_spreadsheet_sgskip.py: E402 examples/user_interfaces/mpl_with_glade3_sgskip.py: E402 - examples/user_interfaces/pylab_with_gtk_sgskip.py: E302, E402 + examples/user_interfaces/pylab_with_gtk3_sgskip.py: E402 + examples/user_interfaces/pylab_with_gtk4_sgskip.py: E402 examples/user_interfaces/toolmanager_sgskip.py: E402 - examples/userdemo/connectionstyle_demo.py: E402 - examples/userdemo/custom_boxstyle01.py: E402 examples/userdemo/pgf_preamble_sgskip.py: E402 - examples/widgets/*.py: E402 - examples/statistics/*.py: E402 diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 000000000000..33ff9446d8a6 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,11 @@ +# style: end-of-file-fixer pre-commit hook +c1a33a481b9c2df605bcb9bef9c19fe65c3dac21 + +# style: trailing-whitespace pre-commit hook +213061c0804530d04bbbd5c259f10dc8504e5b2b + +# style: check-docstring-first pre-commit hook +046533797725293dfc2a6edb9f536b25f08aa636 + +# chore: fix spelling errors +686c9e5a413e31c46bb049407d5eca285bcab76d diff --git a/.git_archival.txt b/.git_archival.txt new file mode 100644 index 000000000000..3994ec0a83ea --- /dev/null +++ b/.git_archival.txt @@ -0,0 +1,4 @@ +node: $Format:%H$ +node-date: $Format:%cI$ +describe-name: $Format:%(describe:tags=true)$ +ref-names: $Format:%D$ diff --git a/.gitattributes b/.gitattributes index 8a1657abfab3..a0c2c8627af7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,6 @@ * text=auto +*.m diff=objc *.ppm binary *.svg binary *.svg linguist-language=true -lib/matplotlib/_version.py export-subst +.git_archival.txt export-subst diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 2bef7ab95a56..5c9afed3c02b 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,3 @@ # These are supported funding model platforms -github: [numfocus] +github: [matplotlib, numfocus] custom: https://numfocus.org/donate-to-matplotlib diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 35a1beb9b2a6..000000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -name: Bug Report -about: Report a bug or issue with Matplotlib ---- - - - - -### Bug report - -**Bug summary** - - - -**Code for reproduction** - - - -```python -# Paste your code here -# -# -``` - -**Actual outcome** - - - -``` -# If applicable, paste the console output here -# -# -``` - -**Expected outcome** - - - - -**Matplotlib version** - - * Operating system: - * Matplotlib version (`import matplotlib; print(matplotlib.__version__)`): - * Matplotlib backend (`print(matplotlib.get_backend())`): - * Python version: - * Jupyter version (if applicable): - * Other libraries: - - - - diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000000..985762649b67 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,86 @@ +name: Bug Report +description: Report a bug or issue with Matplotlib. +title: "[Bug]: " +body: + - type: textarea + id: summary + attributes: + label: Bug summary + description: Describe the bug in 1-2 short sentences + placeholder: + value: + validations: + required: true + - type: textarea + id: reproduction + attributes: + label: Code for reproduction + description: | + If possible, please provide a minimum self-contained example. + placeholder: Paste your code here. This field is automatically formatted as Python code. + render: python + validations: + required: true + - type: textarea + id: actual + attributes: + label: Actual outcome + description: | + Paste the output produced by the code provided above, e.g. + console output, images/videos produced by the code, any relevant screenshots/screencasts, etc. + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected outcome + description: Describe (or provide a visual example of) the expected outcome from the code snippet. + validations: + required: true + - type: textarea + id: details + attributes: + label: Additional information + description: | + - What are the conditions under which this bug happens? input parameters, edge cases, etc? + - Has this worked in earlier versions? + - Do you know why this bug is happening? + - Do you maybe even know a fix? + - type: input + id: operating-system + attributes: + label: Operating system + description: Windows, OS/X, Arch, Debian, Ubuntu, etc. + - type: input + id: matplotlib-version + attributes: + label: Matplotlib Version + description: "From Python prompt: `import matplotlib; print(matplotlib.__version__)`" + validations: + required: true + - type: input + id: matplotlib-backend + attributes: + label: Matplotlib Backend + description: "From Python prompt: `import matplotlib; print(matplotlib.get_backend())`" + - type: input + id: python-version + attributes: + label: Python version + description: "In console: `python --version`" + - type: input + id: jupyter-version + attributes: + label: Jupyter version + description: "In console: `jupyter notebook --version` or `jupyter lab --version`" + - type: dropdown + id: install + attributes: + label: Installation + description: How did you install matplotlib? + options: + - pip + - conda + - Linux package manager + - from source (.tar.gz) + - git checkout diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md deleted file mode 100644 index c0857f636db6..000000000000 --- a/.github/ISSUE_TEMPLATE/documentation.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -name: Documentation improvement -about: Create a report to help us improve the documentation -labels: Documentation ---- - - - - -### Problem - - - - -### Suggested Improvement - - - -**Matplotlib version** - - * Operating system: - * Matplotlib version: (`import matplotlib; print(matplotlib.__version__)`) - * Matplotlib documentation version: (is listed under the logo) \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml new file mode 100644 index 000000000000..ea0eb385baaf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.yml @@ -0,0 +1,32 @@ +name: Documentation +description: Create a report to help us improve the documentation +title: "[Doc]: " +labels: [Documentation] +body: + - type: input + id: link + attributes: + label: Documentation Link + description: | + Link to any documentation or examples that you are referencing. + Suggested improvements should be based on the development version of the docs: https://matplotlib.org/devdocs/ + placeholder: https://matplotlib.org/devdocs/... + - type: textarea + id: problem + attributes: + label: Problem + description: What is missing, unclear, or wrong in the documentation? + placeholder: | + * I found [...] to be unclear because [...] + * [...] made me think that [...] when really it should be [...] + * There is no example showing how to do [...] + validations: + required: true + - type: textarea + id: improvement + attributes: + label: Suggested improvement + placeholder: | + * This line should be be changed to say [...] + * Include a paragraph explaining [...] + * Add a figure showing [...] diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 6ca57f1ce8fa..000000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -name: Feature Request -about: Suggest something to add to Matplotlib -labels: New feature ---- - - - -### Problem - - - -### Proposed Solution - - - -### Additional context and prior art - - \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 000000000000..5274c287569c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,25 @@ +name: Feature Request +description: Suggest something to add to Matplotlib! +title: "[ENH]: " +labels: [New feature] +body: + - type: markdown + attributes: + value: | + Please search the [issues](https://github.com/matplotlib/matplotlib/issues) for relevant feature requests before creating a new feature request. + - type: textarea + id: problem + attributes: + label: Problem + description: Briefly describe the problem this feature will solve. (2-4 sentences) + placeholder: | + * I'm always frustrated when [...] because [...] + * I would like it if [...] happened when I [...] because [...] + * Here is a sample image of what I am asking for [...] + validations: + required: true + - type: textarea + id: solution + attributes: + label: Proposed solution + description: Describe a way to accomplish the goals of this feature request. diff --git a/.github/ISSUE_TEMPLATE/maintenance.md b/.github/ISSUE_TEMPLATE/maintenance.md deleted file mode 100644 index a72282892d85..000000000000 --- a/.github/ISSUE_TEMPLATE/maintenance.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -name: Maintenance -about: Help improve performance, usability and/or consistency. -labels: Maintenance ---- - -### Describe the issue - -**Summary** - - - -### Proposed fix - diff --git a/.github/ISSUE_TEMPLATE/maintenance.yml b/.github/ISSUE_TEMPLATE/maintenance.yml new file mode 100644 index 000000000000..746ab55ef0e3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/maintenance.yml @@ -0,0 +1,17 @@ +name: Maintenance +description: Help improve performance, usability and/or consistency. +title: "[MNT]: " +labels: [Maintenance] +body: + - type: textarea + id: summary + attributes: + label: Summary + description: Please provide 1-2 short sentences that succinctly describes what could be improved. + validations: + required: true + - type: textarea + id: fix + attributes: + label: Proposed fix + description: Please describe how you think this could be improved. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 6c3479d74741..6ebdb4532dde 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -3,14 +3,15 @@ ## PR Checklist - +**Tests and Styling** - [ ] Has pytest style unit tests (and `pytest` passes). -- [ ] Is [Flake 8](https://flake8.pycqa.org/en/latest/) compliant (run `flake8` on changed files to check). +- [ ] Is [Flake 8](https://flake8.pycqa.org/en/latest/) compliant (install `flake8-docstrings` and run `flake8 --docstring-convention=all`). + +**Documentation** - [ ] New features are documented, with examples if plot related. -- [ ] Documentation is sphinx and numpydoc compliant (the docs should [build](https://matplotlib.org/devel/documenting_mpl.html#building-the-docs) without error). -- [ ] Conforms to Matplotlib style conventions (install `flake8-docstrings` and run `flake8 --docstring-convention=all`). - [ ] New features have an entry in `doc/users/next_whats_new/` (follow instructions in README.rst there). - [ ] API changes documented in `doc/api/next_api_changes/` (follow instructions in README.rst there). +- [ ] Documentation is sphinx and numpydoc compliant (the docs should [build](https://matplotlib.org/devel/documenting_mpl.html#building-the-docs) without error). + + + + + + + + image/svg+xml + + + + + + + + width + + + + + + + + + + + + + + + + + + + headaxislength + headlength + + + + + + + + + + + + headwidth + + + + + + + + + length + + + + + + + + + diff --git a/doc/_static/switcher.json b/doc/_static/switcher.json new file mode 100644 index 000000000000..00dd1ffa6c68 --- /dev/null +++ b/doc/_static/switcher.json @@ -0,0 +1,32 @@ +[ + { + "name": "3.6.1 (stable)", + "version": "stable", + "url": "https://matplotlib.org/stable/" + }, + { + "name": "3.7 (dev)", + "version": "dev", + "url": "https://matplotlib.org/devdocs/" + }, + { + "name": "3.5", + "version": "3.5.3", + "url": "https://matplotlib.org/3.5.3/" + }, + { + "name": "3.4", + "version": "3.4.3", + "url": "https://matplotlib.org/3.4.3/" + }, + { + "name": "3.3", + "version": "3.3.4", + "url": "https://matplotlib.org/3.3.4/" + }, + { + "name": "2.2", + "version": "2.2.4", + "url": "https://matplotlib.org/2.2.4/" + } +] diff --git a/doc/_static/zenodo_cache/4638398.svg b/doc/_static/zenodo_cache/4638398.svg new file mode 100644 index 000000000000..8b50f14790dd --- /dev/null +++ b/doc/_static/zenodo_cache/4638398.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.4638398 + + + 10.5281/zenodo.4638398 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/4649959.svg b/doc/_static/zenodo_cache/4649959.svg new file mode 100644 index 000000000000..a604de20cdd5 --- /dev/null +++ b/doc/_static/zenodo_cache/4649959.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.4649959 + + + 10.5281/zenodo.4649959 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/4743323.svg b/doc/_static/zenodo_cache/4743323.svg new file mode 100644 index 000000000000..204cf037ad27 --- /dev/null +++ b/doc/_static/zenodo_cache/4743323.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.4743323 + + + 10.5281/zenodo.4743323 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/5194481.svg b/doc/_static/zenodo_cache/5194481.svg new file mode 100644 index 000000000000..728ae0c15a6a --- /dev/null +++ b/doc/_static/zenodo_cache/5194481.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.5194481 + + + 10.5281/zenodo.5194481 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/5706396.svg b/doc/_static/zenodo_cache/5706396.svg new file mode 100644 index 000000000000..54718543c9c8 --- /dev/null +++ b/doc/_static/zenodo_cache/5706396.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.5706396 + + + 10.5281/zenodo.5706396 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/5773480.svg b/doc/_static/zenodo_cache/5773480.svg new file mode 100644 index 000000000000..431dbd803973 --- /dev/null +++ b/doc/_static/zenodo_cache/5773480.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.5773480 + + + 10.5281/zenodo.5773480 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/6513224.svg b/doc/_static/zenodo_cache/6513224.svg new file mode 100644 index 000000000000..fd54dfcb9abb --- /dev/null +++ b/doc/_static/zenodo_cache/6513224.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.6513224 + + + 10.5281/zenodo.6513224 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/6982547.svg b/doc/_static/zenodo_cache/6982547.svg new file mode 100644 index 000000000000..6eb000d892da --- /dev/null +++ b/doc/_static/zenodo_cache/6982547.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.6982547 + + + 10.5281/zenodo.6982547 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/7084615.svg b/doc/_static/zenodo_cache/7084615.svg new file mode 100644 index 000000000000..9bb362063414 --- /dev/null +++ b/doc/_static/zenodo_cache/7084615.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.7084615 + + + 10.5281/zenodo.7084615 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/7162185.svg b/doc/_static/zenodo_cache/7162185.svg new file mode 100644 index 000000000000..ea0966377194 --- /dev/null +++ b/doc/_static/zenodo_cache/7162185.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.7162185 + + + 10.5281/zenodo.7162185 + + + \ No newline at end of file diff --git a/doc/_templates/automodule.rst b/doc/_templates/automodule.rst index e9f2a755d413..984c12e00d03 100644 --- a/doc/_templates/automodule.rst +++ b/doc/_templates/automodule.rst @@ -5,7 +5,7 @@ treat this separately (sphinx-doc/sphinx/issues/4874) .. automodule:: {{ fullname }} :members: - + {% else %} .. automodule:: {{ fullname }} @@ -18,7 +18,7 @@ Classes ------- -.. autosummary:: +.. autosummary:: :template: autosummary.rst :toctree: {% for item in classes %}{% if item not in ['zip', 'map', 'reduce'] %} @@ -32,7 +32,7 @@ Classes Functions --------- -.. autosummary:: +.. autosummary:: :template: autosummary.rst :toctree: diff --git a/doc/_templates/autosummary.rst b/doc/_templates/autosummary.rst index bf318f1d9aff..c5f90e87f016 100644 --- a/doc/_templates/autosummary.rst +++ b/doc/_templates/autosummary.rst @@ -7,7 +7,7 @@ {% if objtype in ['class'] %} .. auto{{ objtype }}:: {{ objname }} :show-inheritance: - :special-members: + :special-members: __call__ {% else %} .. auto{{ objtype }}:: {{ objname }} diff --git a/doc/_templates/cheatsheet_sidebar.html b/doc/_templates/cheatsheet_sidebar.html new file mode 100644 index 000000000000..3f2b7c4f4db1 --- /dev/null +++ b/doc/_templates/cheatsheet_sidebar.html @@ -0,0 +1,9 @@ + + diff --git a/doc/_templates/donate_sidebar.html b/doc/_templates/donate_sidebar.html index fc7310b70088..071c92888c3c 100644 --- a/doc/_templates/donate_sidebar.html +++ b/doc/_templates/donate_sidebar.html @@ -1,6 +1,5 @@ - - - - - -
-

Matplotlib cheatsheets

- - Matplotlib cheatsheets - -
diff --git a/doc/api/_enums_api.rst b/doc/api/_enums_api.rst index c9e283305967..c38d535f3573 100644 --- a/doc/api/_enums_api.rst +++ b/doc/api/_enums_api.rst @@ -12,4 +12,3 @@ .. autoclass:: CapStyle :members: demo :exclude-members: butt, round, projecting, input_description - diff --git a/doc/api/afm_api.rst b/doc/api/afm_api.rst index 7e6d307cca33..bcae04150909 100644 --- a/doc/api/afm_api.rst +++ b/doc/api/afm_api.rst @@ -2,7 +2,12 @@ ``matplotlib.afm`` ****************** -.. automodule:: matplotlib.afm +.. attention:: + This module is considered internal. + + Its use is deprecated and it will be removed in a future version. + +.. automodule:: matplotlib._afm :members: :undoc-members: :show-inheritance: diff --git a/doc/api/animation_api.rst b/doc/api/animation_api.rst index c8edde884046..d1b81e20b5c8 100644 --- a/doc/api/animation_api.rst +++ b/doc/api/animation_api.rst @@ -11,12 +11,16 @@ :local: :backlinks: entry + Animation ========= -The easiest way to make a live animation in matplotlib is to use one of the +The easiest way to make a live animation in Matplotlib is to use one of the `Animation` classes. +.. inheritance-diagram:: matplotlib.animation.FuncAnimation matplotlib.animation.ArtistAnimation + :parts: 1 + .. autosummary:: :toctree: _as_gen :nosignatures: @@ -29,10 +33,11 @@ In both cases it is critical to keep a reference to the instance object. The animation is advanced by a timer (typically from the host GUI framework) which the `Animation` object holds the only reference to. If you do not hold a reference to the `Animation` object, it (and -hence the timers), will be garbage collected which will stop the +hence the timers) will be garbage collected which will stop the animation. -To save an animation to disk use `Animation.save` or `Animation.to_html5_video` +To save an animation use `Animation.save`, `Animation.to_html5_video`, +or `Animation.to_jshtml`. See :ref:`ani_writer_classes` below for details about what movie formats are supported. @@ -46,9 +51,9 @@ supported. The inner workings of `FuncAnimation` is more-or-less:: for d in frames: - artists = func(d, *fargs) - fig.canvas.draw_idle() - fig.canvas.start_event_loop(interval) + artists = func(d, *fargs) + fig.canvas.draw_idle() + fig.canvas.start_event_loop(interval) with details to handle 'blitting' (to dramatically improve the live performance), to be non-blocking, not repeatedly start/stop the GUI @@ -92,13 +97,18 @@ this hopefully minimalist example gives a sense of how ``init_func`` and ``func`` are used inside of `FuncAnimation` and the theory of how 'blitting' works. +.. note:: + + The zorder of artists is not taken into account when 'blitting' + because the 'blitted' artists are always drawn on top. + The expected signature on ``func`` and ``init_func`` is very simple to keep `FuncAnimation` out of your book keeping and plotting logic, but this means that the callable objects you pass in must know what artists they should be working on. There are several approaches to handling this, of varying complexity and encapsulation. The simplest approach, which works quite well in the case of a script, is to define the -artist at a global scope and let Python sort things out. For example :: +artist at a global scope and let Python sort things out. For example:: import numpy as np import matplotlib.pyplot as plt @@ -106,7 +116,7 @@ artist at a global scope and let Python sort things out. For example :: fig, ax = plt.subplots() xdata, ydata = [], [] - ln, = plt.plot([], [], 'ro') + ln, = ax.plot([], [], 'ro') def init(): ax.set_xlim(0, 2*np.pi) @@ -123,8 +133,36 @@ artist at a global scope and let Python sort things out. For example :: init_func=init, blit=True) plt.show() -The second method is to use `functools.partial` to 'bind' artists to -function. A third method is to use closures to build up the required +The second method is to use `functools.partial` to pass arguments to the +function:: + + import numpy as np + import matplotlib.pyplot as plt + from matplotlib.animation import FuncAnimation + from functools import partial + + fig, ax = plt.subplots() + line1, = ax.plot([], [], 'ro') + + def init(): + ax.set_xlim(0, 2*np.pi) + ax.set_ylim(-1, 1) + return line1, + + def update(frame, ln, x, y): + x.append(frame) + y.append(np.sin(frame)) + ln.set_data(x, y) + return ln, + + ani = FuncAnimation( + fig, partial(update, ln=line1, x=[], y=[]), + frames=np.linspace(0, 2*np.pi, 128), + init_func=init, blit=True) + + plt.show() + +A third method is to use closures to build up the required artists and functions. A fourth method is to create a class. Examples @@ -157,6 +195,10 @@ Examples Writer Classes ============== +.. inheritance-diagram:: matplotlib.animation.FFMpegFileWriter matplotlib.animation.FFMpegWriter matplotlib.animation.ImageMagickFileWriter matplotlib.animation.ImageMagickWriter matplotlib.animation.PillowWriter matplotlib.animation.HTMLWriter + :top-classes: matplotlib.animation.AbstractMovieWriter + :parts: 1 + The provided writers fall into a few broad categories. The Pillow writer relies on the Pillow library to write the animation, keeping @@ -186,7 +228,6 @@ on all systems. FFMpegWriter ImageMagickWriter - AVConvWriter The file-based writers save temporary files for each frame which are stitched into a single file at the end. Although slower, these writers can be easier to @@ -198,18 +239,19 @@ debug. FFMpegFileWriter ImageMagickFileWriter - AVConvFileWriter -Fundamentally, a `MovieWriter` provides a way to grab sequential frames -from the same underlying `~matplotlib.figure.Figure` object. The base -class `MovieWriter` implements 3 methods and a context manager. The -only difference between the pipe-based and file-based writers is in the -arguments to their respective ``setup`` methods. +The writer classes provide a way to grab sequential frames from the same +underlying `~matplotlib.figure.Figure`. They all provide three methods that +must be called in sequence: -The ``setup()`` method is used to prepare the writer (possibly opening -a pipe), successive calls to ``grab_frame()`` capture a single frame -at a time and ``finish()`` finalizes the movie and writes the output -file to disk. For example :: +- `~.AbstractMovieWriter.setup` prepares the writer (e.g. opening a pipe). + Pipe-based and file-based writers take different arguments to ``setup()``. +- `~.AbstractMovieWriter.grab_frame` can then be called as often as + needed to capture a single frame at a time +- `~.AbstractMovieWriter.finish` finalizes the movie and writes the output + file to disk. + +Example:: moviewriter = MovieWriter(...) moviewriter.setup(fig, 'my_movie.ext', dpi=100) @@ -219,14 +261,14 @@ file to disk. For example :: moviewriter.finish() If using the writer classes directly (not through `Animation.save`), it is -strongly encouraged to use the `~MovieWriter.saving` context manager :: +strongly encouraged to use the `~.AbstractMovieWriter.saving` context manager:: with moviewriter.saving(fig, 'myfile.mp4', dpi=100): for j in range(n): update_figure(j) moviewriter.grab_frame() -to ensures that setup and cleanup are performed as necessary. +to ensure that setup and cleanup are performed as necessary. Examples -------- @@ -283,21 +325,9 @@ and mixins :toctree: _as_gen :nosignatures: - AVConvBase FFMpegBase ImageMagickBase are provided. See the source code for how to easily implement new `MovieWriter` classes. - -Inheritance Diagrams -==================== - -.. inheritance-diagram:: matplotlib.animation.FuncAnimation matplotlib.animation.ArtistAnimation - :private-bases: - :parts: 1 - -.. inheritance-diagram:: matplotlib.animation.AVConvFileWriter matplotlib.animation.AVConvWriter matplotlib.animation.FFMpegFileWriter matplotlib.animation.FFMpegWriter matplotlib.animation.ImageMagickFileWriter matplotlib.animation.ImageMagickWriter - :private-bases: - :parts: 1 diff --git a/doc/api/api_changes.rst b/doc/api/api_changes.rst deleted file mode 100644 index b861326911a9..000000000000 --- a/doc/api/api_changes.rst +++ /dev/null @@ -1,41 +0,0 @@ - -=========== -API Changes -=========== - -If updating Matplotlib breaks your scripts, this list may help you figure out -what caused the breakage and how to fix it by updating your code. - -For API changes in older versions see - -.. toctree:: - :maxdepth: 1 - - api_changes_old - -Changes for the latest version are listed below. For new features that were -added to Matplotlib, see :ref:`whats-new` - -.. ifconfig:: releaselevel == 'dev' - - .. note:: - - The list below is a table of contents of individual files from the - most recent :file:`api_changes_X.Y` folder. - - When a release is made - - - The include directive below should be changed to point to the new file - created in the previous step. - - - .. toctree:: - :glob: - :maxdepth: 1 - - next_api_changes/behavior/* - next_api_changes/deprecations/* - next_api_changes/development/* - next_api_changes/removals/* - -.. include:: prev_api_changes/api_changes_3.4.0.rst diff --git a/doc/api/api_changes_old.rst b/doc/api/api_changes_old.rst deleted file mode 100644 index ab9381680498..000000000000 --- a/doc/api/api_changes_old.rst +++ /dev/null @@ -1,11 +0,0 @@ - -================ - Old API Changes -================ - -.. toctree:: - :glob: - :reversed: - :maxdepth: 1 - - prev_api_changes/* diff --git a/doc/api/artist_api.rst b/doc/api/artist_api.rst index d77f27f0960f..b99842372bbe 100644 --- a/doc/api/artist_api.rst +++ b/doc/api/artist_api.rst @@ -4,16 +4,17 @@ ``matplotlib.artist`` ********************* -.. inheritance-diagram:: matplotlib.axes._axes.Axes matplotlib.axes._base._AxesBase matplotlib.axis.Axis matplotlib.axis.Tick matplotlib.axis.XAxis matplotlib.axis.XTick matplotlib.axis.YAxis matplotlib.axis.YTick matplotlib.collections.AsteriskPolygonCollection matplotlib.collections.BrokenBarHCollection matplotlib.collections.CircleCollection matplotlib.collections.Collection matplotlib.collections.EllipseCollection matplotlib.collections.EventCollection matplotlib.collections.LineCollection matplotlib.collections.PatchCollection matplotlib.collections.PathCollection matplotlib.collections.PolyCollection matplotlib.collections.QuadMesh matplotlib.collections.RegularPolyCollection matplotlib.collections.StarPolygonCollection matplotlib.collections.TriMesh matplotlib.collections._CollectionWithSizes matplotlib.contour.ClabelText matplotlib.figure.Figure matplotlib.image.AxesImage matplotlib.image.BboxImage matplotlib.image.FigureImage matplotlib.image.NonUniformImage matplotlib.image.PcolorImage matplotlib.image._ImageBase matplotlib.legend.Legend matplotlib.lines.Line2D matplotlib.offsetbox.AnchoredOffsetbox matplotlib.offsetbox.AnchoredText matplotlib.offsetbox.AnnotationBbox matplotlib.offsetbox.AuxTransformBox matplotlib.offsetbox.DrawingArea matplotlib.offsetbox.HPacker matplotlib.offsetbox.OffsetBox matplotlib.offsetbox.OffsetImage matplotlib.offsetbox.PackerBase matplotlib.offsetbox.PaddedBox matplotlib.offsetbox.TextArea matplotlib.offsetbox.VPacker matplotlib.patches.Arc matplotlib.patches.Arrow matplotlib.patches.Circle matplotlib.patches.CirclePolygon matplotlib.patches.ConnectionPatch matplotlib.patches.Ellipse matplotlib.patches.FancyArrow matplotlib.patches.FancyArrowPatch matplotlib.patches.FancyBboxPatch matplotlib.patches.Patch matplotlib.patches.PathPatch matplotlib.patches.StepPatch matplotlib.patches.Polygon matplotlib.patches.Rectangle matplotlib.patches.RegularPolygon matplotlib.patches.Shadow matplotlib.patches.Wedge matplotlib.projections.geo.AitoffAxes matplotlib.projections.geo.GeoAxes matplotlib.projections.geo.HammerAxes matplotlib.projections.geo.LambertAxes matplotlib.projections.geo.MollweideAxes matplotlib.projections.polar.PolarAxes matplotlib.quiver.Barbs matplotlib.quiver.Quiver matplotlib.quiver.QuiverKey matplotlib.spines.Spine matplotlib.table.Cell matplotlib.table.CustomCell matplotlib.table.Table matplotlib.text.Annotation matplotlib.text.Text - :parts: 1 - :private-bases: - - - .. automodule:: matplotlib.artist :no-members: :no-undoc-members: +Inheritance Diagrams +==================== + +.. inheritance-diagram:: matplotlib.axes._axes.Axes matplotlib.axes._base._AxesBase matplotlib.axis.Axis matplotlib.axis.Tick matplotlib.axis.XAxis matplotlib.axis.XTick matplotlib.axis.YAxis matplotlib.axis.YTick matplotlib.collections.AsteriskPolygonCollection matplotlib.collections.BrokenBarHCollection matplotlib.collections.CircleCollection matplotlib.collections.Collection matplotlib.collections.EllipseCollection matplotlib.collections.EventCollection matplotlib.collections.LineCollection matplotlib.collections.PatchCollection matplotlib.collections.PathCollection matplotlib.collections.PolyCollection matplotlib.collections.QuadMesh matplotlib.collections.RegularPolyCollection matplotlib.collections.StarPolygonCollection matplotlib.collections.TriMesh matplotlib.collections._CollectionWithSizes matplotlib.contour.ClabelText matplotlib.figure.Figure matplotlib.image.AxesImage matplotlib.image.BboxImage matplotlib.image.FigureImage matplotlib.image.NonUniformImage matplotlib.image.PcolorImage matplotlib.image._ImageBase matplotlib.legend.Legend matplotlib.lines.Line2D matplotlib.offsetbox.AnchoredOffsetbox matplotlib.offsetbox.AnchoredText matplotlib.offsetbox.AnnotationBbox matplotlib.offsetbox.AuxTransformBox matplotlib.offsetbox.DrawingArea matplotlib.offsetbox.HPacker matplotlib.offsetbox.OffsetBox matplotlib.offsetbox.OffsetImage matplotlib.offsetbox.PackerBase matplotlib.offsetbox.PaddedBox matplotlib.offsetbox.TextArea matplotlib.offsetbox.VPacker matplotlib.patches.Arc matplotlib.patches.Arrow matplotlib.patches.Circle matplotlib.patches.CirclePolygon matplotlib.patches.ConnectionPatch matplotlib.patches.Ellipse matplotlib.patches.FancyArrow matplotlib.patches.FancyArrowPatch matplotlib.patches.FancyBboxPatch matplotlib.patches.Patch matplotlib.patches.PathPatch matplotlib.patches.StepPatch matplotlib.patches.Polygon matplotlib.patches.Rectangle matplotlib.patches.RegularPolygon matplotlib.patches.Shadow matplotlib.patches.Wedge matplotlib.projections.geo.AitoffAxes matplotlib.projections.geo.GeoAxes matplotlib.projections.geo.HammerAxes matplotlib.projections.geo.LambertAxes matplotlib.projections.geo.MollweideAxes matplotlib.projections.polar.PolarAxes matplotlib.quiver.Barbs matplotlib.quiver.Quiver matplotlib.quiver.QuiverKey matplotlib.spines.Spine matplotlib.table.Cell matplotlib.table.CustomCell matplotlib.table.Table matplotlib.text.Annotation matplotlib.text.Text + :parts: 1 + :private-bases: + ``Artist`` class ================ @@ -26,6 +27,7 @@ Interactive ----------- .. autosummary:: + :template: autosummary.rst :toctree: _as_gen :nosignatures: @@ -34,10 +36,10 @@ Interactive Artist.pchanged Artist.get_cursor_data Artist.format_cursor_data + Artist.set_mouseover + Artist.get_mouseover Artist.mouseover Artist.contains - Artist.set_contains - Artist.get_contains Artist.pick Artist.pickable Artist.set_picker @@ -47,6 +49,7 @@ Clipping -------- .. autosummary:: + :template: autosummary.rst :toctree: _as_gen :nosignatures: @@ -61,6 +64,7 @@ Bulk Properties --------------- .. autosummary:: + :template: autosummary.rst :toctree: _as_gen :nosignatures: @@ -73,6 +77,7 @@ Drawing ------- .. autosummary:: + :template: autosummary.rst :toctree: _as_gen :nosignatures: @@ -100,12 +105,14 @@ Drawing Artist.get_agg_filter Artist.get_window_extent + Artist.get_tightbbox Artist.get_transformed_clip_path_and_affine Figure and Axes --------------- .. autosummary:: + :template: autosummary.rst :toctree: _as_gen :nosignatures: @@ -120,6 +127,7 @@ Children -------- .. autosummary:: + :template: autosummary.rst :toctree: _as_gen :nosignatures: @@ -130,6 +138,7 @@ Transform --------- .. autosummary:: + :template: autosummary.rst :toctree: _as_gen :nosignatures: @@ -141,6 +150,7 @@ Units ----- .. autosummary:: + :template: autosummary.rst :toctree: _as_gen :nosignatures: @@ -152,6 +162,7 @@ Metadata -------- .. autosummary:: + :template: autosummary.rst :toctree: _as_gen :nosignatures: @@ -166,6 +177,7 @@ Miscellaneous ------------- .. autosummary:: + :template: autosummary.rst :toctree: _as_gen :nosignatures: @@ -178,6 +190,7 @@ Functions ========= .. autosummary:: + :template: autosummary.rst :toctree: _as_gen :nosignatures: diff --git a/doc/api/axes_api.rst b/doc/api/axes_api.rst index 6024a79bd81c..231e5be98ddf 100644 --- a/doc/api/axes_api.rst +++ b/doc/api/axes_api.rst @@ -295,7 +295,6 @@ Axis limits and direction Axes.get_ylim Axes.update_datalim - Axes.update_datalim_bounds Axes.set_xbound Axes.get_xbound @@ -607,3 +606,6 @@ Other Axes.get_default_bbox_extra_artists Axes.get_transformed_clip_path_and_affine Axes.has_data + Axes.set + +.. autoclass:: matplotlib.axes.Axes.ArtistList diff --git a/doc/api/axis_api.rst b/doc/api/axis_api.rst index 4bc9ad0c6e89..d950d1e253a4 100644 --- a/doc/api/axis_api.rst +++ b/doc/api/axis_api.rst @@ -41,7 +41,6 @@ Inheritance :nosignatures: Axis.clear - Axis.cla Axis.get_scale @@ -151,6 +150,7 @@ Interactive :nosignatures: Axis.contains + Axis.pickradius Axis.get_pickradius Axis.set_pickradius @@ -169,17 +169,6 @@ Units Axis.update_units -Incremental navigation ----------------------- - -.. autosummary:: - :toctree: _as_gen - :template: autosummary.rst - :nosignatures: - - Axis.pan - Axis.zoom - XAxis Specific -------------- @@ -192,6 +181,7 @@ XAxis Specific XAxis.get_text_heights XAxis.get_ticks_position XAxis.set_ticks_position + XAxis.set_label_position XAxis.tick_bottom XAxis.tick_top @@ -208,6 +198,7 @@ YAxis Specific YAxis.get_ticks_position YAxis.set_offset_position YAxis.set_ticks_position + YAxis.set_label_position YAxis.tick_left YAxis.tick_right @@ -266,8 +257,6 @@ specify a matching series of labels. Calling ``set_ticks`` makes a :template: autosummary.rst :nosignatures: - - Tick.apply_tickdir Tick.get_loc Tick.get_pad Tick.get_pad_pixels @@ -277,4 +266,5 @@ specify a matching series of labels. Calling ``set_ticks`` makes a Tick.set_label1 Tick.set_label2 Tick.set_pad + Tick.set_url Tick.update_position diff --git a/doc/api/backend_agg_api.rst b/doc/api/backend_agg_api.rst index 40c8cd4bce6a..134a0d83cde0 100644 --- a/doc/api/backend_agg_api.rst +++ b/doc/api/backend_agg_api.rst @@ -1,6 +1,5 @@ - -:mod:`matplotlib.backends.backend_agg` -====================================== +:mod:`.backend_agg` +=================== .. automodule:: matplotlib.backends.backend_agg :members: diff --git a/doc/api/backend_cairo_api.rst b/doc/api/backend_cairo_api.rst index 2623270c6781..0ef9d6903007 100644 --- a/doc/api/backend_cairo_api.rst +++ b/doc/api/backend_cairo_api.rst @@ -1,6 +1,5 @@ - -:mod:`matplotlib.backends.backend_cairo` -======================================== +:mod:`.backend_cairo` +===================== .. automodule:: matplotlib.backends.backend_cairo :members: diff --git a/doc/api/backend_gtk3_api.rst b/doc/api/backend_gtk3_api.rst index 5e17df66d602..7340ba003ee7 100644 --- a/doc/api/backend_gtk3_api.rst +++ b/doc/api/backend_gtk3_api.rst @@ -1,16 +1,11 @@ +:mod:`.backend_gtk3agg`, :mod:`.backend_gtk3cairo` +================================================== + **NOTE** These backends are not documented here, to avoid adding a dependency to building the docs. .. redirect-from:: /api/backend_gtk3agg_api .. redirect-from:: /api/backend_gtk3cairo_api - -:mod:`matplotlib.backends.backend_gtk3agg` -========================================== - .. module:: matplotlib.backends.backend_gtk3agg - -:mod:`matplotlib.backends.backend_gtk3cairo` -============================================ - .. module:: matplotlib.backends.backend_gtk3cairo diff --git a/doc/api/backend_gtk4_api.rst b/doc/api/backend_gtk4_api.rst new file mode 100644 index 000000000000..81c33c9a6ffd --- /dev/null +++ b/doc/api/backend_gtk4_api.rst @@ -0,0 +1,11 @@ +:mod:`.backend_gtk4agg`, :mod:`.backend_gtk4cairo` +================================================== + +**NOTE** These backends are not documented here, to avoid adding a dependency +to building the docs. + +.. redirect-from:: /api/backend_gtk4agg_api +.. redirect-from:: /api/backend_gtk4cairo_api + +.. module:: matplotlib.backends.backend_gtk4agg +.. module:: matplotlib.backends.backend_gtk4cairo diff --git a/doc/api/backend_mixed_api.rst b/doc/api/backend_mixed_api.rst index 7457f6684f94..35f7a49a08c4 100644 --- a/doc/api/backend_mixed_api.rst +++ b/doc/api/backend_mixed_api.rst @@ -1,6 +1,5 @@ - -:mod:`matplotlib.backends.backend_mixed` -======================================== +:mod:`.backend_mixed` +===================== .. automodule:: matplotlib.backends.backend_mixed :members: diff --git a/doc/api/backend_nbagg_api.rst b/doc/api/backend_nbagg_api.rst index 977eabce8db0..dddeb7ac3c74 100644 --- a/doc/api/backend_nbagg_api.rst +++ b/doc/api/backend_nbagg_api.rst @@ -1,6 +1,5 @@ - -:mod:`matplotlib.backends.backend_nbagg` -======================================== +:mod:`.backend_nbagg` +===================== .. automodule:: matplotlib.backends.backend_nbagg :members: diff --git a/doc/api/backend_pdf_api.rst b/doc/api/backend_pdf_api.rst index ded143ddcf8d..1743083b4de8 100644 --- a/doc/api/backend_pdf_api.rst +++ b/doc/api/backend_pdf_api.rst @@ -1,6 +1,5 @@ - -:mod:`matplotlib.backends.backend_pdf` -====================================== +:mod:`.backend_pdf` +=================== .. automodule:: matplotlib.backends.backend_pdf :members: diff --git a/doc/api/backend_pgf_api.rst b/doc/api/backend_pgf_api.rst index ec7440080eb0..2902742c744b 100644 --- a/doc/api/backend_pgf_api.rst +++ b/doc/api/backend_pgf_api.rst @@ -1,6 +1,5 @@ - -:mod:`matplotlib.backends.backend_pgf` -====================================== +:mod:`.backend_pgf` +=================== .. automodule:: matplotlib.backends.backend_pgf :members: diff --git a/doc/api/backend_ps_api.rst b/doc/api/backend_ps_api.rst index 9d585be7a0ad..d97e024b9d5c 100644 --- a/doc/api/backend_ps_api.rst +++ b/doc/api/backend_ps_api.rst @@ -1,6 +1,5 @@ - -:mod:`matplotlib.backends.backend_ps` -===================================== +:mod:`.backend_ps` +================== .. automodule:: matplotlib.backends.backend_ps :members: diff --git a/doc/api/backend_qt_api.rst b/doc/api/backend_qt_api.rst index 90fe9bb95539..622889c10e5c 100644 --- a/doc/api/backend_qt_api.rst +++ b/doc/api/backend_qt_api.rst @@ -1,27 +1,70 @@ -**NOTE** These backends are not documented here, to avoid adding a dependency -to building the docs. +:mod:`.backend_qtagg`, :mod:`.backend_qtcairo` +============================================== + +**NOTE** These backends are not (auto) documented here, to avoid adding a +dependency to building the docs. .. redirect-from:: /api/backend_qt4agg_api .. redirect-from:: /api/backend_qt4cairo_api .. redirect-from:: /api/backend_qt5agg_api .. redirect-from:: /api/backend_qt5cairo_api -:mod:`matplotlib.backends.backend_qt4agg` -========================================= +.. module:: matplotlib.backends.qt_compat +.. module:: matplotlib.backends.backend_qt +.. module:: matplotlib.backends.backend_qtagg +.. module:: matplotlib.backends.backend_qtcairo +.. module:: matplotlib.backends.backend_qt5agg +.. module:: matplotlib.backends.backend_qt5cairo -.. module:: matplotlib.backends.backend_qt4agg +.. _QT_bindings: -:mod:`matplotlib.backends.backend_qt4cairo` -=========================================== +Qt Bindings +----------- -.. module:: matplotlib.backends.backend_qt4cairo +There are currently 2 actively supported Qt versions, Qt5 and Qt6, and two +supported Python bindings per version -- `PyQt5 +`_ and `PySide2 +`_ for Qt5 and `PyQt6 +`_ and `PySide6 +`_ for Qt6 [#]_. While both PyQt +and Qt for Python (aka PySide) closely mirror the underlying C++ API they are +wrapping, they are not drop-in replacements for each other [#]_. To account +for this, Matplotlib has an internal API compatibility layer in +`matplotlib.backends.qt_compat` which covers our needs. Despite being a public +module, we do not consider this to be a stable user-facing API and it may +change without warning [#]_. -:mod:`matplotlib.backends.backend_qt5agg` -========================================= +Previously Matplotlib's Qt backends had the Qt version number in the name, both +in the module and the :rc:`backend` value +(e.g. ``matplotlib.backends.backend_qt4agg`` and +``matplotlib.backends.backend_qt5agg``). However as part of adding support for +Qt6 we were able to support both Qt5 and Qt6 with a single implementation with +all of the Qt version and binding support handled in +`~matplotlib.backends.qt_compat`. A majority of the renderer agnostic Qt code +is now in `matplotlib.backends.backend_qt` with specialization for AGG in +``backend_qtagg`` and cairo in ``backend_qtcairo``. -.. module:: matplotlib.backends.backend_qt5agg +The binding is selected at run time based on what bindings are already imported +(by checking for the ``QtCore`` sub-package), then by the :envvar:`QT_API` +environment variable, and finally by the :rc:`backend`. In all cases when we +need to search, the order is ``PyQt6``, ``PySide6``, ``PyQt5``, ``PySide2``. +See :ref:`QT_API-usage` for usage instructions. -:mod:`matplotlib.backends.backend_qt5cairo` -=========================================== +The ``backend_qt5``, ``backend_qt5agg``, and ``backend_qt5cairo`` are provided +and force the use of a Qt5 binding for backwards compatibility. Their use is +discouraged (but not deprecated) and ``backend_qt``, ``backend_qtagg``, or +``backend_qtcairo`` should be preferred instead. However, these modules will +not be deprecated until we drop support for Qt5. -.. module:: matplotlib.backends.backend_qt5cairo + + + +.. [#] There is also `PyQt4 + `_ and `PySide + `_ for Qt4 but these are no + longer supported by Matplotlib and upstream support for Qt4 ended + in 2015. +.. [#] Despite the slight API differences, the more important distinction + between the PyQt and Qt for Python series of bindings is licensing. +.. [#] If you are looking for a general purpose compatibility library please + see `qtpy `_. diff --git a/doc/api/backend_svg_api.rst b/doc/api/backend_svg_api.rst index 0b26d11e8818..a6208a897431 100644 --- a/doc/api/backend_svg_api.rst +++ b/doc/api/backend_svg_api.rst @@ -1,6 +1,5 @@ - -:mod:`matplotlib.backends.backend_svg` -====================================== +:mod:`.backend_svg` +=================== .. automodule:: matplotlib.backends.backend_svg :members: diff --git a/doc/api/backend_template_api.rst b/doc/api/backend_template_api.rst index 892f5b696d93..0f7bffee4b74 100644 --- a/doc/api/backend_template_api.rst +++ b/doc/api/backend_template_api.rst @@ -1,6 +1,5 @@ - -:mod:`matplotlib.backends.backend_template` -=========================================== +:mod:`.backend_template` +======================== .. automodule:: matplotlib.backends.backend_template :members: diff --git a/doc/api/backend_tk_api.rst b/doc/api/backend_tk_api.rst index 48131a48ce46..5eaa1873cebe 100644 --- a/doc/api/backend_tk_api.rst +++ b/doc/api/backend_tk_api.rst @@ -1,15 +1,11 @@ - -:mod:`matplotlib.backends.backend_tkagg` -======================================== +:mod:`.backend_tkagg`, :mod:`.backend_tkcairo` +============================================== .. automodule:: matplotlib.backends.backend_tkagg :members: :undoc-members: :show-inheritance: -:mod:`matplotlib.backends.backend_tkcairo` -========================================== - .. automodule:: matplotlib.backends.backend_tkcairo :members: :undoc-members: diff --git a/doc/api/backend_webagg_api.rst b/doc/api/backend_webagg_api.rst index c71f84e31606..a4089473c903 100644 --- a/doc/api/backend_webagg_api.rst +++ b/doc/api/backend_webagg_api.rst @@ -1,9 +1,7 @@ +:mod:`.backend_webagg` +====================== -:mod:`matplotlib.backends.backend_webagg` -========================================= - -.. note:: - The WebAgg backend is not documented here, in order to avoid adding Tornado - to the doc build requirements. - -.. module:: matplotlib.backends.backend_webagg +.. automodule:: matplotlib.backends.backend_webagg + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/backend_wx_api.rst b/doc/api/backend_wx_api.rst index 3ae3bc502e69..a3b6d4155c11 100644 --- a/doc/api/backend_wx_api.rst +++ b/doc/api/backend_wx_api.rst @@ -1,14 +1,10 @@ +:mod:`.backend_wxagg`, :mod:`.backend_wxcairo` +============================================== + **NOTE** These backends are not documented here, to avoid adding a dependency to building the docs. .. redirect-from:: /api/backend_wxagg_api -:mod:`matplotlib.backends.backend_wxagg` -======================================== - .. module:: matplotlib.backends.backend_wxagg - -:mod:`matplotlib.backends.backend_wxcairo` -========================================== - .. module:: matplotlib.backends.backend_wxcairo diff --git a/doc/api/colors_api.rst b/doc/api/colors_api.rst index 4302e289530c..970986ff4438 100644 --- a/doc/api/colors_api.rst +++ b/doc/api/colors_api.rst @@ -2,8 +2,11 @@ ``matplotlib.colors`` ********************* -The Color :ref:`tutorials ` and :ref:`examples -` demonstrate how to set colors and colormaps. +.. note:: + + The Color :ref:`tutorials ` and :ref:`examples + ` demonstrate how to set colors and colormaps. You may want + to read those instead. .. currentmodule:: matplotlib.colors @@ -11,26 +14,44 @@ The Color :ref:`tutorials ` and :ref:`examples :no-members: :no-inherited-members: -Classes -------- +Color norms +----------- .. autosummary:: :toctree: _as_gen/ :template: autosummary.rst + Normalize + NoNorm + AsinhNorm BoundaryNorm - Colormap CenteredNorm - LightSource - LinearSegmentedColormap - ListedColormap + FuncNorm LogNorm - NoNorm - Normalize PowerNorm SymLogNorm TwoSlopeNorm - FuncNorm + +Colormaps +--------- + +.. autosummary:: + :toctree: _as_gen/ + :template: autosummary.rst + + Colormap + LinearSegmentedColormap + ListedColormap + +Other classes +------------- + +.. autosummary:: + :toctree: _as_gen/ + :template: autosummary.rst + + ColorSequenceRegistry + LightSource Functions --------- @@ -49,3 +70,4 @@ Functions is_color_like same_color get_named_colors_mapping + make_norm_from_scale diff --git a/doc/api/dates_api.rst b/doc/api/dates_api.rst index 1afeaaeac3cd..7a3e3bcf4a95 100644 --- a/doc/api/dates_api.rst +++ b/doc/api/dates_api.rst @@ -4,8 +4,10 @@ .. inheritance-diagram:: matplotlib.dates :parts: 1 + :top-classes: matplotlib.ticker.Formatter, matplotlib.ticker.Locator .. automodule:: matplotlib.dates :members: :undoc-members: + :exclude-members: rrule :show-inheritance: diff --git a/doc/api/docstring_api.rst b/doc/api/docstring_api.rst index 853ff93494cf..38a73a2e83d1 100644 --- a/doc/api/docstring_api.rst +++ b/doc/api/docstring_api.rst @@ -2,7 +2,12 @@ ``matplotlib.docstring`` ************************ -.. automodule:: matplotlib.docstring +.. attention:: + This module is considered internal. + + Its use is deprecated and it will be removed in a future version. + +.. automodule:: matplotlib._docstring :members: :undoc-members: :show-inheritance: diff --git a/doc/api/font_manager_api.rst b/doc/api/font_manager_api.rst index 24bfefe00d32..3e043112380b 100644 --- a/doc/api/font_manager_api.rst +++ b/doc/api/font_manager_api.rst @@ -7,5 +7,9 @@ :undoc-members: :show-inheritance: + .. data:: fontManager + The global instance of `FontManager`. +.. autoclass:: FontEntry + :no-undoc-members: diff --git a/doc/api/ft2font.rst b/doc/api/ft2font.rst new file mode 100644 index 000000000000..a1f984abdda5 --- /dev/null +++ b/doc/api/ft2font.rst @@ -0,0 +1,8 @@ +********************** +``matplotlib.ft2font`` +********************** + +.. automodule:: matplotlib.ft2font + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/hatch_api.rst b/doc/api/hatch_api.rst new file mode 100644 index 000000000000..b706be379a15 --- /dev/null +++ b/doc/api/hatch_api.rst @@ -0,0 +1,8 @@ +******************** +``matplotlib.hatch`` +******************** + +.. automodule:: matplotlib.hatch + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/index.rst b/doc/api/index.rst index c247a169e304..c92d22240ff0 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -1,44 +1,52 @@ -API Overview -============ +API Reference +============= -.. toctree:: - :hidden: +When using the library you will typically create +:doc:`Figure ` and :doc:`Axes ` objects and +call their methods to add content and modify the appearance. - api_changes +- :mod:`matplotlib.figure`: axes creation, figure-level content +- :mod:`matplotlib.axes`: most plotting methods, Axes labels, access to axis + styling, etc. -.. contents:: :local: +Example: We create a Figure ``fig`` and Axes ``ax``. Then we call +methods on them to plot data, add axis labels and a figure title. -See also the :doc:`api_changes`. +.. plot:: + :include-source: + :align: center -Usage patterns --------------- + import matplotlib.pyplot as plt + import numpy as np -Below we describe several common approaches to plotting with Matplotlib. + x = np.arange(0, 4, 0.05) + y = np.sin(x*np.pi) -The pyplot API -^^^^^^^^^^^^^^ + fig, ax = plt.subplots(figsize=(3,2), constrained_layout=True) + ax.plot(x, y) + ax.set_xlabel('t [s]') + ax.set_ylabel('S [V]') + ax.set_title('Sine wave') + fig.set_facecolor('lightsteelblue') -`matplotlib.pyplot` is a collection of command style functions that make -Matplotlib work like MATLAB. Each pyplot function makes some change to a -figure: e.g., creates a figure, creates a plotting area in a figure, plots -some lines in a plotting area, decorates the plot with labels, etc. -`.pyplot` is mainly intended for interactive plots and simple cases of -programmatic plot generation. -Further reading: +.. _usage_patterns: -- The `matplotlib.pyplot` function reference -- :doc:`/tutorials/introductory/pyplot` -- :ref:`Pyplot examples ` +Usage patterns +-------------- + +Below we describe several common approaches to plotting with Matplotlib. See +:ref:`api_interfaces` for an explanation of the trade-offs between the supported user +APIs. -.. _api-index: -The object-oriented API -^^^^^^^^^^^^^^^^^^^^^^^ +The explicit API +^^^^^^^^^^^^^^^^ -At its core, Matplotlib is object-oriented. We recommend directly working -with the objects, if you need more control and customization of your plots. +At its core, Matplotlib is an object-oriented library. We recommend directly +working with the objects if you need more control and customization of your +plots. In many cases you will create a `.Figure` and one or more `~matplotlib.axes.Axes` using `.pyplot.subplots` and from then on only work @@ -52,7 +60,27 @@ Further reading: - Most of the :ref:`examples ` use the object-oriented approach (except for the pyplot section) -The pylab API (disapproved) + +The implicit API +^^^^^^^^^^^^^^^^ + +`matplotlib.pyplot` is a collection of functions that make +Matplotlib work like MATLAB. Each pyplot function makes some change to a +figure: e.g., creates a figure, creates a plotting area in a figure, plots +some lines in a plotting area, decorates the plot with labels, etc. + +`.pyplot` is mainly intended for interactive plots and simple cases of +programmatic plot generation. + +Further reading: + +- The `matplotlib.pyplot` function reference +- :doc:`/tutorials/introductory/pyplot` +- :ref:`Pyplot examples ` + +.. _api-index: + +The pylab API (discouraged) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. automodule:: pylab @@ -61,7 +89,7 @@ The pylab API (disapproved) Modules ------- -Matplotlib consists of the following submodules: +Alphabetical list of modules: .. toctree:: :maxdepth: 1 @@ -92,8 +120,11 @@ Matplotlib consists of the following submodules: figure_api.rst font_manager_api.rst fontconfig_pattern_api.rst + ft2font.rst gridspec_api.rst + hatch_api.rst image_api.rst + layout_engine_api.rst legend_api.rst legend_handler_api.rst lines_api.rst @@ -110,6 +141,7 @@ Matplotlib consists of the following submodules: rcsetup_api.rst sankey_api.rst scale_api.rst + sphinxext_mathmpl_api.rst sphinxext_plot_directive_api.rst spines_api.rst style_api.rst @@ -128,22 +160,6 @@ Matplotlib consists of the following submodules: widgets_api.rst _api_api.rst _enums_api.rst - -Toolkits --------- - -:ref:`toolkits-index` are collections of application-specific functions that extend -Matplotlib. The following toolkits are included: - -.. toctree:: - :hidden: - - toolkits/index.rst - -.. toctree:: - :maxdepth: 1 - toolkits/mplot3d.rst toolkits/axes_grid1.rst toolkits/axisartist.rst - toolkits/axes_grid.rst diff --git a/doc/api/index_backend_api.rst b/doc/api/index_backend_api.rst index ad2febf8dc38..639d96a9a0dd 100644 --- a/doc/api/index_backend_api.rst +++ b/doc/api/index_backend_api.rst @@ -2,6 +2,8 @@ ``matplotlib.backends`` *********************** +.. module:: matplotlib.backends + .. toctree:: :maxdepth: 1 @@ -10,6 +12,7 @@ backend_agg_api.rst backend_cairo_api.rst backend_gtk3_api.rst + backend_gtk4_api.rst backend_nbagg_api.rst backend_pdf_api.rst backend_pgf_api.rst diff --git a/doc/api/layout_engine_api.rst b/doc/api/layout_engine_api.rst new file mode 100644 index 000000000000..8890061e0979 --- /dev/null +++ b/doc/api/layout_engine_api.rst @@ -0,0 +1,9 @@ +**************************** +``matplotlib.layout_engine`` +**************************** + +.. currentmodule:: matplotlib.layout_engine + +.. automodule:: matplotlib.layout_engine + :members: + :inherited-members: diff --git a/doc/api/lines_api.rst b/doc/api/lines_api.rst index 808df726d118..4cde67c7e656 100644 --- a/doc/api/lines_api.rst +++ b/doc/api/lines_api.rst @@ -26,4 +26,3 @@ Functions :template: autosummary.rst segment_hits - \ No newline at end of file diff --git a/doc/api/mathtext_api.rst b/doc/api/mathtext_api.rst index c0f4941414ed..295ed0382c61 100644 --- a/doc/api/mathtext_api.rst +++ b/doc/api/mathtext_api.rst @@ -9,4 +9,3 @@ :members: :undoc-members: :show-inheritance: - :exclude-members: Box, Char, ComputerModernFontConstants, DejaVuSansFontConstants, DejaVuSerifFontConstants, FontConstantsBase, Fonts, Glue, Kern, Node, Parser, STIXFontConstants, STIXSansFontConstants, Ship, StandardPsFonts, TruetypeFonts diff --git a/doc/api/matplotlib_configuration_api.rst b/doc/api/matplotlib_configuration_api.rst index 5fa27bbc6723..d5dc60c80613 100644 --- a/doc/api/matplotlib_configuration_api.rst +++ b/doc/api/matplotlib_configuration_api.rst @@ -4,6 +4,11 @@ .. py:currentmodule:: matplotlib +.. automodule:: matplotlib + :no-members: + :no-undoc-members: + :noindex: + Backend management ================== @@ -26,6 +31,7 @@ Default values and styling :no-members: .. automethod:: find_all + .. automethod:: copy .. autofunction:: rc_context @@ -52,7 +58,18 @@ Logging .. autofunction:: set_loglevel +Colormaps and color sequences +============================= + +.. autodata:: colormaps + :no-value: + +.. autodata:: color_sequences + :no-value: + Miscellaneous ============= +.. autoclass:: MatplotlibDeprecationWarning + .. autofunction:: get_cachedir diff --git a/doc/api/next_api_changes.rst b/doc/api/next_api_changes.rst new file mode 100644 index 000000000000..d33c8014f735 --- /dev/null +++ b/doc/api/next_api_changes.rst @@ -0,0 +1,15 @@ + +================ +Next API changes +================ + +.. ifconfig:: releaselevel == 'dev' + + .. toctree:: + :glob: + :maxdepth: 1 + + next_api_changes/behavior/* + next_api_changes/deprecations/* + next_api_changes/development/* + next_api_changes/removals/* diff --git a/doc/api/next_api_changes/behavior/00001-ABC.rst b/doc/api/next_api_changes/behavior/00001-ABC.rst index 236f672c1123..f6d8c1d8b351 100644 --- a/doc/api/next_api_changes/behavior/00001-ABC.rst +++ b/doc/api/next_api_changes/behavior/00001-ABC.rst @@ -1,7 +1,7 @@ -Behavior Change template +Behavior change template ~~~~~~~~~~~~~~~~~~~~~~~~ Enter description here.... Please rename file with PR number and your initials i.e. "99999-ABC.rst" -and ``git add`` the new file. +and ``git add`` the new file. diff --git a/doc/api/next_api_changes/development/00001-ABC.rst b/doc/api/next_api_changes/development/00001-ABC.rst index 4c60d3db185b..6db90a13e44c 100644 --- a/doc/api/next_api_changes/development/00001-ABC.rst +++ b/doc/api/next_api_changes/development/00001-ABC.rst @@ -1,7 +1,7 @@ -Development Change template +Development change template ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Enter description here.... Please rename file with PR number and your initials i.e. "99999-ABC.rst" -and ``git add`` the new file. +and ``git add`` the new file. diff --git a/doc/api/next_api_changes/removals/00001-ABC.rst b/doc/api/next_api_changes/removals/00001-ABC.rst index 5c68eda9698a..3cc5b6344f7f 100644 --- a/doc/api/next_api_changes/removals/00001-ABC.rst +++ b/doc/api/next_api_changes/removals/00001-ABC.rst @@ -1,7 +1,7 @@ -Removal Change template +Removal change template ~~~~~~~~~~~~~~~~~~~~~~~ Enter description of methods/classes removed here.... Please rename file with PR number and your initials i.e. "99999-ABC.rst" -and ``git add`` the new file. +and ``git add`` the new file. diff --git a/doc/api/patches_api.rst b/doc/api/patches_api.rst index ac21a644ccab..5b1eefa91971 100644 --- a/doc/api/patches_api.rst +++ b/doc/api/patches_api.rst @@ -2,7 +2,9 @@ ``matplotlib.patches`` ********************** -.. currentmodule:: matplotlib.patches + +.. inheritance-diagram:: matplotlib.patches + :parts: 1 .. automodule:: matplotlib.patches :no-members: @@ -15,6 +17,7 @@ Classes :toctree: _as_gen/ :template: autosummary.rst + Annulus Arc Arrow ArrowStyle @@ -45,4 +48,3 @@ Functions bbox_artist draw_bbox - diff --git a/doc/api/prev_api_changes/api_changes_0.65.rst b/doc/api/prev_api_changes/api_changes_0.65.rst index 43fffb1bcf4e..f9b9af732010 100644 --- a/doc/api/prev_api_changes/api_changes_0.65.rst +++ b/doc/api/prev_api_changes/api_changes_0.65.rst @@ -8,5 +8,5 @@ Changes for 0.65 connect and disconnect Did away with the text methods for angle since they were ambiguous. - fontangle could mean fontstyle (obligue, etc) or the rotation of the + fontangle could mean fontstyle (oblique, etc) or the rotation of the text. Use style and rotation instead. diff --git a/doc/api/prev_api_changes/api_changes_0.70.rst b/doc/api/prev_api_changes/api_changes_0.70.rst index b8094658b249..e30dfbb64954 100644 --- a/doc/api/prev_api_changes/api_changes_0.70.rst +++ b/doc/api/prev_api_changes/api_changes_0.70.rst @@ -6,4 +6,4 @@ Changes for 0.70 MplEvent factored into a base class Event and derived classes MouseEvent and KeyEvent - Removed definct set_measurement in wx toolbar + Removed defunct set_measurement in wx toolbar diff --git a/doc/api/prev_api_changes/api_changes_0.72.rst b/doc/api/prev_api_changes/api_changes_0.72.rst index 9529e396f356..bfb6fc124658 100644 --- a/doc/api/prev_api_changes/api_changes_0.72.rst +++ b/doc/api/prev_api_changes/api_changes_0.72.rst @@ -6,7 +6,7 @@ Changes for 0.72 - Line2D, Text, and Patch copy_properties renamed update_from and moved into artist base class - - LineCollecitons.color renamed to LineCollections.set_color for + - LineCollections.color renamed to LineCollections.set_color for consistency with set/get introspection mechanism, - pylab figure now defaults to num=None, which creates a new figure diff --git a/doc/api/prev_api_changes/api_changes_0.91.0.rst b/doc/api/prev_api_changes/api_changes_0.91.0.rst index b33fcb50f0ad..32760554522a 100644 --- a/doc/api/prev_api_changes/api_changes_0.91.0.rst +++ b/doc/api/prev_api_changes/api_changes_0.91.0.rst @@ -25,18 +25,18 @@ Changes for 0.91.0 * The :mod:`matplotlib.dviread` file now has a parser for files like psfonts.map and pdftex.map, to map TeX font names to external files. -* The file :mod:`matplotlib.type1font` contains a new class for Type 1 +* The file ``matplotlib.type1font`` contains a new class for Type 1 fonts. Currently it simply reads pfa and pfb format files and stores the data in a way that is suitable for embedding in pdf files. In the future the class might actually parse the font to allow e.g., subsetting. -* :mod:`matplotlib.ft2font` now supports ``FT_Attach_File``. In +* ``matplotlib.ft2font`` now supports ``FT_Attach_File``. In practice this can be used to read an afm file in addition to a pfa/pfb file, to get metrics and kerning information for a Type 1 font. -* The :class:`.AFM` class now supports querying CapHeight and stem +* The ``AFM`` class now supports querying CapHeight and stem widths. The get_name_char method now has an isord kwarg like get_width_char. diff --git a/doc/api/prev_api_changes/api_changes_0.98.0.rst b/doc/api/prev_api_changes/api_changes_0.98.0.rst index 0c73628e1651..ba22e5f4fb0a 100644 --- a/doc/api/prev_api_changes/api_changes_0.98.0.rst +++ b/doc/api/prev_api_changes/api_changes_0.98.0.rst @@ -126,8 +126,8 @@ with a verb in the present tense. | ``lbwh_to_bbox(l, b, w, h)`` | `Bbox.from_bounds(x0, y0, w, h) <.Bbox.from_bounds>` | | | [It is a staticmethod.] | +--------------------------------------------+------------------------------------------------------+ -| ``inverse_transform_bbox(trans, bbox)`` | `Bbox.inverse_transformed(trans) | -| | <.BboxBase.inverse_transformed>` | +| ``inverse_transform_bbox(trans, bbox)`` | ``bbox.inverse_transformed(trans)`` | +| | | +--------------------------------------------+------------------------------------------------------+ | ``Interval.contains_open(v)`` | `interval_contains_open(tuple, v) | | | <.interval_contains_open>` | @@ -181,7 +181,7 @@ The ``Polar`` class has moved to :mod:`matplotlib.projections.polar`. .. [3] :meth:`matplotlib.axes.Axes.set_position` now accepts either four scalars or a :class:`matplotlib.transforms.Bbox` instance. -.. [4] Since the recfactoring allows for more than two scale types +.. [4] Since the refactoring allows for more than two scale types ('log' or 'linear'), it no longer makes sense to have a toggle. ``Axes.toggle_log_lineary()`` has been removed. diff --git a/doc/api/prev_api_changes/api_changes_0.98.x.rst b/doc/api/prev_api_changes/api_changes_0.98.x.rst index 41ee63502254..053fd908a03e 100644 --- a/doc/api/prev_api_changes/api_changes_0.98.x.rst +++ b/doc/api/prev_api_changes/api_changes_0.98.x.rst @@ -63,8 +63,8 @@ Changes for 0.98.x :meth:`matplotlib.axes.Axes.set_ylim` now return a copy of the ``viewlim`` array to avoid modify-in-place surprises. -* :meth:`matplotlib.afm.AFM.get_fullname` and - :meth:`matplotlib.afm.AFM.get_familyname` no longer raise an +* ``matplotlib.afm.AFM.get_fullname`` and + ``matplotlib.afm.AFM.get_familyname`` no longer raise an exception if the AFM file does not specify these optional attributes, but returns a guess based on the required FontName attribute. @@ -87,13 +87,13 @@ Changes for 0.98.x :class:`~matplotlib.collections.Collection` base class. * ``matplotlib.figure.Figure.figurePatch`` renamed - :attr:`matplotlib.figure.Figure.patch`; + ``matplotlib.figure.Figure.patch``; ``matplotlib.axes.Axes.axesPatch`` renamed - :attr:`matplotlib.axes.Axes.patch`; + ``matplotlib.axes.Axes.patch``; ``matplotlib.axes.Axes.axesFrame`` renamed - :attr:`matplotlib.axes.Axes.frame`. + ``matplotlib.axes.Axes.frame``. ``matplotlib.axes.Axes.get_frame``, which returns - :attr:`matplotlib.axes.Axes.patch`, is deprecated. + ``matplotlib.axes.Axes.patch``, is deprecated. * Changes in the :class:`matplotlib.contour.ContourLabeler` attributes (:func:`matplotlib.pyplot.clabel` function) so that they all have a diff --git a/doc/api/prev_api_changes/api_changes_0.99.x.rst b/doc/api/prev_api_changes/api_changes_0.99.x.rst index 03596e93dac3..4736d066d43e 100644 --- a/doc/api/prev_api_changes/api_changes_0.99.x.rst +++ b/doc/api/prev_api_changes/api_changes_0.99.x.rst @@ -21,8 +21,8 @@ Changes beyond 0.99.x on or off, and applies it. + :meth:`matplotlib.axes.Axes.margins` sets margins used to - autoscale the :attr:`matplotlib.axes.Axes.viewLim` based on - the :attr:`matplotlib.axes.Axes.dataLim`. + autoscale the ``matplotlib.axes.Axes.viewLim`` based on + the ``matplotlib.axes.Axes.dataLim``. + :meth:`matplotlib.axes.Axes.locator_params` allows one to adjust axes locator parameters such as *nbins*. diff --git a/doc/api/prev_api_changes/api_changes_1.1.x.rst b/doc/api/prev_api_changes/api_changes_1.1.x.rst index 8320e2c4fc09..790b669081b7 100644 --- a/doc/api/prev_api_changes/api_changes_1.1.x.rst +++ b/doc/api/prev_api_changes/api_changes_1.1.x.rst @@ -1,6 +1,6 @@ -Changes in 1.1.x -================ +API Changes in 1.1.x +==================== * Added new :class:`matplotlib.sankey.Sankey` for generating Sankey diagrams. diff --git a/doc/api/prev_api_changes/api_changes_1.2.x.rst b/doc/api/prev_api_changes/api_changes_1.2.x.rst index cb6b2071f79b..45a2f35cf29e 100644 --- a/doc/api/prev_api_changes/api_changes_1.2.x.rst +++ b/doc/api/prev_api_changes/api_changes_1.2.x.rst @@ -1,5 +1,5 @@ -Changes in 1.2.x -================ +API Changes in 1.2.x +==================== * The ``classic`` option of the rc parameter ``toolbar`` is deprecated and will be removed in the next release. @@ -66,7 +66,7 @@ Changes in 1.2.x This change means that third party objects can expose themselves as Matplotlib axes by providing a ``_as_mpl_axes`` method. See - :ref:`adding-new-scales` for more detail. + :mod:`matplotlib.projections` for more detail. * A new keyword *extendfrac* in :meth:`~matplotlib.pyplot.colorbar` and :class:`~matplotlib.colorbar.ColorbarBase` allows one to control the size of diff --git a/doc/api/prev_api_changes/api_changes_1.3.x.rst b/doc/api/prev_api_changes/api_changes_1.3.x.rst index 5b596d83b5e2..edf4106fc564 100644 --- a/doc/api/prev_api_changes/api_changes_1.3.x.rst +++ b/doc/api/prev_api_changes/api_changes_1.3.x.rst @@ -1,8 +1,8 @@ .. _changes_in_1_3: -Changes in 1.3.x -================ +API Changes in 1.3.x +==================== Changes in 1.3.1 ---------------- @@ -33,7 +33,7 @@ Code removal functionality on `matplotlib.path.Path.contains_point` and friends instead. - - Instead of ``axes.Axes.get_frame``, use `.axes.Axes.patch`. + - Instead of ``axes.Axes.get_frame``, use ``axes.Axes.patch``. - The following keyword arguments to the `~.axes.Axes.legend` function have been renamed: @@ -102,7 +102,7 @@ Code deprecation be used. In previous Matplotlib versions this attribute was an undocumented tuple of ``(colorbar_instance, colorbar_axes)`` but is now just ``colorbar_instance``. To get the colorbar axes it is possible to just use - the :attr:`~matplotlib.colorbar.ColorbarBase.ax` attribute on a colorbar + the ``matplotlib.colorbar.ColorbarBase.ax`` attribute on a colorbar instance. * The ``matplotlib.mpl`` module is now deprecated. Those who relied on this diff --git a/doc/api/prev_api_changes/api_changes_1.4.x.rst b/doc/api/prev_api_changes/api_changes_1.4.x.rst index 2d49b4b6651a..c12d40a67991 100644 --- a/doc/api/prev_api_changes/api_changes_1.4.x.rst +++ b/doc/api/prev_api_changes/api_changes_1.4.x.rst @@ -1,5 +1,5 @@ -Changes in 1.4.x -================ +API Changes in 1.4.x +==================== Code changes ------------ @@ -82,7 +82,7 @@ original location: * The artist used to draw the outline of a `.Figure.colorbar` has been changed from a `matplotlib.lines.Line2D` to `matplotlib.patches.Polygon`, thus - `.colorbar.ColorbarBase.outline` is now a `matplotlib.patches.Polygon` + ``colorbar.ColorbarBase.outline`` is now a `matplotlib.patches.Polygon` object. * The legend handler interface has changed from a callable, to any object @@ -149,9 +149,9 @@ original location: ``drawRect`` from ``FigureCanvasQTAgg``; they were always an implementation detail of the (preserved) ``drawRectangle()`` function. -* The function signatures of `.tight_bbox.adjust_bbox` and - `.tight_bbox.process_figure_for_rasterizing` have been changed. A new - *fixed_dpi* parameter allows for overriding the ``figure.dpi`` setting +* The function signatures of ``matplotlib.tight_bbox.adjust_bbox`` and + ``matplotlib.tight_bbox.process_figure_for_rasterizing`` have been changed. + A new *fixed_dpi* parameter allows for overriding the ``figure.dpi`` setting instead of trying to deduce the intended behaviour from the file format. * Added support for horizontal/vertical axes padding to diff --git a/doc/api/prev_api_changes/api_changes_1.5.0.rst b/doc/api/prev_api_changes/api_changes_1.5.0.rst index 4ebb5c182c4b..1248b1dfd394 100644 --- a/doc/api/prev_api_changes/api_changes_1.5.0.rst +++ b/doc/api/prev_api_changes/api_changes_1.5.0.rst @@ -1,6 +1,6 @@ -Changes in 1.5.0 -================ +API Changes in 1.5.0 +==================== Code Changes ------------ @@ -41,7 +41,7 @@ fully delegate to `.Quiver`). Previously any input matching 'mid.*' would be interpreted as 'middle', 'tip.*' as 'tip' and any string not matching one of those patterns as 'tail'. -The value of `.Quiver.pivot` is normalized to be in the set {'tip', 'tail', +The value of ``Quiver.pivot`` is normalized to be in the set {'tip', 'tail', 'middle'} in `.Quiver`. Reordered ``Axes.get_children`` @@ -60,7 +60,7 @@ by the new keyword argument *corner_mask*, or if this is not specified then the new :rc:`contour.corner_mask` instead. The new default behaviour is equivalent to using ``corner_mask=True``; the previous behaviour can be obtained using ``corner_mask=False`` or by changing the rcParam. The example -http://matplotlib.org/examples/pylab_examples/contour_corner_mask.html +https://matplotlib.org/examples/pylab_examples/contour_corner_mask.html demonstrates the difference. Use of the old contouring algorithm, which is obtained with ``corner_mask='legacy'``, is now deprecated. @@ -116,10 +116,10 @@ In either case to update the data in the `.Line2D` object you must update both the ``x`` and ``y`` data. -Removed *args* and *kwargs* from `.MicrosecondLocator.__call__` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Removed *args* and *kwargs* from ``MicrosecondLocator.__call__`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The call signature of :meth:`~matplotlib.dates.MicrosecondLocator.__call__` +The call signature of ``matplotlib.dates.MicrosecondLocator.__call__`` has changed from ``__call__(self, *args, **kwargs)`` to ``__call__(self)``. This is consistent with the superclass :class:`~matplotlib.ticker.Locator` and also all the other Locators derived from this superclass. @@ -374,7 +374,7 @@ directly. patheffects.svg ~~~~~~~~~~~~~~~ - - remove ``get_proxy_renderer`` method from ``AbstarctPathEffect`` class + - remove ``get_proxy_renderer`` method from ``AbstractPathEffect`` class - remove ``patch_alpha`` and ``offset_xy`` from ``SimplePatchShadow`` diff --git a/doc/api/prev_api_changes/api_changes_1.5.2.rst b/doc/api/prev_api_changes/api_changes_1.5.2.rst index d2ee33546314..85c504fa6f12 100644 --- a/doc/api/prev_api_changes/api_changes_1.5.2.rst +++ b/doc/api/prev_api_changes/api_changes_1.5.2.rst @@ -1,5 +1,5 @@ -Changes in 1.5.2 -================ +API Changes in 1.5.2 +==================== Default Behavior Changes diff --git a/doc/api/prev_api_changes/api_changes_1.5.3.rst b/doc/api/prev_api_changes/api_changes_1.5.3.rst index 0dc025111eae..ff5d6a9cf996 100644 --- a/doc/api/prev_api_changes/api_changes_1.5.3.rst +++ b/doc/api/prev_api_changes/api_changes_1.5.3.rst @@ -1,5 +1,5 @@ -Changes in 1.5.3 -================ +API Changes in 1.5.3 +==================== ``ax.plot(..., marker=None)`` gives default marker -------------------------------------------------- diff --git a/doc/api/prev_api_changes/api_changes_2.1.0.rst b/doc/api/prev_api_changes/api_changes_2.1.0.rst index 90f9e00eecd1..9673d24c719f 100644 --- a/doc/api/prev_api_changes/api_changes_2.1.0.rst +++ b/doc/api/prev_api_changes/api_changes_2.1.0.rst @@ -20,7 +20,7 @@ Previously they were clipped to a very small number and shown. Matplotlib uses instances of :obj:`~matplotlib.cbook.CallbackRegistry` as a bridge between user input event from the GUI and user callbacks. Previously, any exceptions raised in a user call back would bubble out -of of the ``process`` method, which is typically in the GUI event +of the ``process`` method, which is typically in the GUI event loop. Most GUI frameworks simple print the traceback to the screen and continue as there is not always a clear method of getting the exception back to the user. However PyQt5 now exits the process when diff --git a/doc/api/prev_api_changes/api_changes_2.2.0.rst b/doc/api/prev_api_changes/api_changes_2.2.0.rst index 29ed03649fd8..f13fe2a246f0 100644 --- a/doc/api/prev_api_changes/api_changes_2.2.0.rst +++ b/doc/api/prev_api_changes/api_changes_2.2.0.rst @@ -74,7 +74,7 @@ rcParam. Deprecated ``Axis.unit_data`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Use `.Axis.units` (which has long existed) instead. +Use ``Axis.units`` (which has long existed) instead. Removals @@ -159,7 +159,7 @@ If `.MovieWriterRegistry` can't find the requested `.MovieWriter`, a more helpful `RuntimeError` message is now raised instead of the previously raised `KeyError`. -`~.tight_layout.auto_adjust_subplotpars` now raises `ValueError` +``matplotlib.tight_layout.auto_adjust_subplotpars`` now raises `ValueError` instead of `RuntimeError` when sizes of input lists don't match @@ -169,7 +169,7 @@ instead of `RuntimeError` when sizes of input lists don't match `matplotlib.figure.Figure.set_figwidth` and `matplotlib.figure.Figure.set_figheight` had the keyword argument ``forward=False`` by default, but `.figure.Figure.set_size_inches` now defaults -to ``forward=True``. This makes these functions conistent. +to ``forward=True``. This makes these functions consistent. Do not truncate svg sizes to nearest point @@ -198,11 +198,11 @@ Changes to Qt backend class MRO To support both Agg and cairo rendering for Qt backends all of the non-Agg specific code previously in ``backend_qt5agg.FigureCanvasQTAggBase`` has been -moved to :class:`.backend_qt5.FigureCanvasQT` so it can be shared with the +moved to ``backend_qt5.FigureCanvasQT`` so it can be shared with the cairo implementation. The ``FigureCanvasQTAggBase.paintEvent``, ``FigureCanvasQTAggBase.blit``, and ``FigureCanvasQTAggBase.print_figure`` -methods have moved to :meth:`.FigureCanvasQTAgg.paintEvent`, -:meth:`.FigureCanvasQTAgg.blit`, and :meth:`.FigureCanvasQTAgg.print_figure`. +methods have moved to ``FigureCanvasQTAgg.paintEvent``, +``FigureCanvasQTAgg.blit``, and ``FigureCanvasQTAgg.print_figure``. The first two methods assume that the instance is also a ``QWidget`` so to use ``FigureCanvasQTAggBase`` it was required to multiple inherit from a ``QWidget`` sub-class. @@ -210,9 +210,9 @@ The first two methods assume that the instance is also a ``QWidget`` so to use Having moved all of its methods either up or down the class hierarchy ``FigureCanvasQTAggBase`` has been deprecated. To do this without warning and to preserve as much API as possible, ``.backend_qt5agg.FigureCanvasQTAggBase`` -now inherits from :class:`.backend_qt5.FigureCanvasQTAgg`. +now inherits from ``backend_qt5.FigureCanvasQTAgg``. -The MRO for :class:`.FigureCanvasQTAgg` and ``FigureCanvasQTAggBase`` used to +The MRO for ``FigureCanvasQTAgg`` and ``FigureCanvasQTAggBase`` used to be :: diff --git a/doc/api/prev_api_changes/api_changes_3.0.0.rst b/doc/api/prev_api_changes/api_changes_3.0.0.rst index a6224c352179..47d6f413367d 100644 --- a/doc/api/prev_api_changes/api_changes_3.0.0.rst +++ b/doc/api/prev_api_changes/api_changes_3.0.0.rst @@ -197,7 +197,7 @@ Contour color autoscaling improvements Selection of contour levels is now the same for contour and contourf; previously, for contour, levels outside the data range were deleted. (Exception: if no contour levels are found within the -data range, the `levels` attribute is replaced with a list holding +data range, the ``levels`` attribute is replaced with a list holding only the minimum of the data range.) When contour is called with levels specified as a target number rather @@ -297,7 +297,7 @@ Blacklisted rcparams no longer updated by `~matplotlib.rcdefaults`, `~matplotlib The rc modifier functions `~matplotlib.rcdefaults`, `~matplotlib.rc_file_defaults` and `~matplotlib.rc_file` -now ignore rcParams in the `matplotlib.style.core.STYLE_BLACKLIST` set. In +now ignore rcParams in the ``matplotlib.style.core.STYLE_BLACKLIST`` set. In particular, this prevents the ``backend`` and ``interactive`` rcParams from being incorrectly modified by these functions. @@ -324,12 +324,12 @@ instead of ``\usepackage{ucs}\usepackage[utf8x]{inputenc}``. Return type of ArtistInspector.get_aliases changed -------------------------------------------------- -`ArtistInspector.get_aliases` previously returned the set of aliases as +``ArtistInspector.get_aliases`` previously returned the set of aliases as ``{fullname: {alias1: None, alias2: None, ...}}``. The dict-to-None mapping was used to simulate a set in earlier versions of Python. It has now been replaced by a set, i.e. ``{fullname: {alias1, alias2, ...}}``. -This value is also stored in `.ArtistInspector.aliasd`, which has likewise +This value is also stored in ``ArtistInspector.aliasd``, which has likewise changed. @@ -471,7 +471,7 @@ Hold machinery Setting or unsetting ``hold`` (:ref:`deprecated in version 2.0`) has now been completely removed. Matplotlib now always behaves as if ``hold=True``. To clear an axes you can manually use :meth:`~.axes.Axes.cla()`, -or to clear an entire figure use :meth:`~.figure.Figure.clf()`. +or to clear an entire figure use :meth:`~.figure.Figure.clear()`. Removal of deprecated backends diff --git a/doc/api/prev_api_changes/api_changes_3.0.1.rst b/doc/api/prev_api_changes/api_changes_3.0.1.rst index d214ae9e6652..4b203cd04596 100644 --- a/doc/api/prev_api_changes/api_changes_3.0.1.rst +++ b/doc/api/prev_api_changes/api_changes_3.0.1.rst @@ -1,10 +1,10 @@ API Changes for 3.0.1 ===================== -`.tight_layout.auto_adjust_subplotpars` can return ``None`` now if the new -subplotparams will collapse axes to zero width or height. This prevents -``tight_layout`` from being executed. Similarly -`.tight_layout.get_tight_layout_figure` will return None. +``matplotlib.tight_layout.auto_adjust_subplotpars`` can return ``None`` now if +the new subplotparams will collapse axes to zero width or height. +This prevents ``tight_layout`` from being executed. Similarly +``matplotlib.tight_layout.get_tight_layout_figure`` will return None. To improve import (startup) time, private modules are now imported lazily. These modules are no longer available at these locations: diff --git a/doc/api/prev_api_changes/api_changes_3.1.0.rst b/doc/api/prev_api_changes/api_changes_3.1.0.rst index f3737889841f..3f41900abb53 100644 --- a/doc/api/prev_api_changes/api_changes_3.1.0.rst +++ b/doc/api/prev_api_changes/api_changes_3.1.0.rst @@ -293,9 +293,9 @@ where the `.cm.ScalarMappable` passed to `matplotlib.colorbar.Colorbar` (`~.Figure.colorbar`) had a ``set_norm`` method, as did the colorbar. The colorbar is now purely a follower to the `.ScalarMappable` norm and colormap, and the old inherited methods -`~matplotlib.colorbar.ColorbarBase.set_norm`, -`~matplotlib.colorbar.ColorbarBase.set_cmap`, -`~matplotlib.colorbar.ColorbarBase.set_clim` are deprecated, as are +``matplotlib.colorbar.ColorbarBase.set_norm``, +``matplotlib.colorbar.ColorbarBase.set_cmap``, +``matplotlib.colorbar.ColorbarBase.set_clim`` are deprecated, as are the getter versions of those calls. To set the norm associated with a colorbar do ``colorbar.mappable.set_norm()`` etc. @@ -308,7 +308,7 @@ FreeType or libpng are not in the compiler or linker's default path, set the standard environment variables ``CFLAGS``/``LDFLAGS`` on Linux or OSX, or ``CL``/``LINK`` on Windows, to indicate the relevant paths. -See details in :doc:`/users/installing`. +See details in :doc:`/users/installing/index`. Setting artist properties twice or more in the same call ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -337,7 +337,7 @@ match the array value type of the ``Path.codes`` array. LaTeX code in matplotlibrc file ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Previously, the rc file keys ``pgf.preamble`` and ``text.latex.preamble`` were -parsed using commmas as separators. This would break valid LaTeX code, such as:: +parsed using commas as separators. This would break valid LaTeX code, such as:: \usepackage[protrusion=true, expansion=false]{microtype} @@ -394,11 +394,11 @@ returned. `matplotlib.font_manager.win32InstalledFonts` returns an empty list instead of None if no fonts are found. -`.Axes.fmt_xdata` and `.Axes.fmt_ydata` error handling -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``Axes.fmt_xdata`` and ``Axes.fmt_ydata`` error handling +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Previously, if the user provided a `.Axes.fmt_xdata` or -`.Axes.fmt_ydata` function that raised a `TypeError` (or set them to a +Previously, if the user provided a ``Axes.fmt_xdata`` or +``Axes.fmt_ydata`` function that raised a `TypeError` (or set them to a non-callable), the exception would be silently ignored and the default formatter be used instead. This is no longer the case; the exception is now propagated out. @@ -476,7 +476,7 @@ Exception changes - `.Axes.streamplot` does not support irregularly gridded ``x`` and ``y`` values. So far, it used to silently plot an incorrect result. This has been changed to raise a `ValueError` instead. -- The `.streamplot.Grid` class, which is internally used by streamplot +- The ``streamplot.Grid`` class, which is internally used by streamplot code, also throws a `ValueError` when irregularly gridded values are passed in. @@ -496,7 +496,7 @@ Classes and methods - ``backend_wx.Toolbar`` (use ``backend_wx.NavigationToolbar2Wx`` instead) - ``cbook.align_iterators`` (no replacement) - ``contour.ContourLabeler.get_real_label_width`` (no replacement) -- ``legend.Legend.draggable`` (use `legend.Legend.set_draggable()` instead) +- ``legend.Legend.draggable`` (use `.legend.Legend.set_draggable()` instead) - ``texmanager.TexManager.postscriptd``, ``texmanager.TexManager.pscnt``, ``texmanager.TexManager.make_ps``, ``texmanager.TexManager.get_ps_bbox`` (no replacements) @@ -566,7 +566,7 @@ in Matplotlib 2.2 has been removed. See below for a list: - ``mlab.safe_isnan`` (use `numpy.isnan` instead) - ``mlab.cohere_pairs`` (use `scipy.signal.coherence` instead) - ``mlab.entropy`` (use `scipy.stats.entropy` instead) -- ``mlab.normpdf`` (use `scipy.stats.norm.pdf` instead) +- ``mlab.normpdf`` (use ``scipy.stats.norm.pdf`` instead) - ``mlab.find`` (use ``np.nonzero(np.ravel(condition))`` instead) - ``mlab.longest_contiguous_ones`` - ``mlab.longest_ones`` @@ -652,7 +652,7 @@ no longer available in the `pylab` module: - ``longest_ones`` - ``movavg`` - ``norm_flat`` (use ``numpy.linalg.norm(a.flat, ord=2)`` instead) -- ``normpdf`` (use `scipy.stats.norm.pdf` instead) +- ``normpdf`` (use ``scipy.stats.norm.pdf`` instead) - ``path_length`` - ``poly_below`` - ``poly_between`` @@ -705,7 +705,7 @@ now a no-op). The image comparison test decorators now skip (rather than xfail) the test for uncomparable formats. The affected decorators are `~.image_comparison` and -`~.check_figures_equal`. The deprecated `~.ImageComparisonTest` class is +`~.check_figures_equal`. The deprecated ``ImageComparisonTest`` class is likewise changed. Dependency changes @@ -825,7 +825,7 @@ This has not been used in the codebase since its addition in 2009. This has never been used internally, there is no equivalent method exists on the 2D Axis classes, and despite the similar name, it has a completely - different behavior from the 2D Axis' `axis.Axis.get_ticks_position` method. + different behavior from the 2D Axis' ``axis.Axis.get_ticks_position`` method. - ``.backend_pgf.LatexManagerFactory`` - ``mpl_toolkits.axisartist.axislines.SimpleChainedObjects`` @@ -936,8 +936,8 @@ Axes3D - `.axes3d.Axes3D.w_yaxis` - `.axes3d.Axes3D.w_zaxis` -Use `.axes3d.Axes3D.xaxis`, `.axes3d.Axes3D.yaxis` and `.axes3d.Axes3D.zaxis` -instead. +Use ``axes3d.Axes3D.xaxis``, ``axes3d.Axes3D.yaxis`` and +``axes3d.Axes3D.zaxis`` instead. Testing ~~~~~~~ @@ -945,7 +945,7 @@ Testing - ``matplotlib.testing.decorators.switch_backend`` decorator Test functions should use ``pytest.mark.backend``, and the mark will be -picked up by the `matplotlib.testing.conftest.mpl_test_settings` fixture. +picked up by the ``matplotlib.testing.conftest.mpl_test_settings`` fixture. Quiver ~~~~~~ @@ -1069,7 +1069,7 @@ Axis - ``Axis.iter_ticks`` -This only served as a helper to the private `.Axis._update_ticks` +This only served as a helper to the private ``Axis._update_ticks`` Undeprecations @@ -1123,10 +1123,10 @@ The `.Formatter` class gained a new `~.Formatter.format_ticks` method, which takes the list of all tick locations as a single argument and returns the list of all formatted values. It is called by the axis tick handling code and, by default, first calls `~.Formatter.set_locs` with all locations, then repeatedly -calls `~.Formatter.__call__` for each location. +calls ``Formatter.__call__`` for each location. Tick-handling code in the codebase that previously performed this sequence -(`~.Formatter.set_locs` followed by repeated `~.Formatter.__call__`) have been +(`~.Formatter.set_locs` followed by repeated ``Formatter.__call__``) have been updated to use `~.Formatter.format_ticks`. `~.Formatter.format_ticks` is intended to be overridden by `.Formatter` diff --git a/doc/api/prev_api_changes/api_changes_3.2.0/behavior.rst b/doc/api/prev_api_changes/api_changes_3.2.0/behavior.rst index 8e76a047e348..dc47740890be 100644 --- a/doc/api/prev_api_changes/api_changes_3.2.0/behavior.rst +++ b/doc/api/prev_api_changes/api_changes_3.2.0/behavior.rst @@ -46,9 +46,9 @@ highest. Code that worked around the normalization between 0 and 1 will need to be modified. -`MovieWriterRegistry` -~~~~~~~~~~~~~~~~~~~~~ -`MovieWriterRegistry` now always checks the availability of the writer classes +``MovieWriterRegistry`` +~~~~~~~~~~~~~~~~~~~~~~~ +`.MovieWriterRegistry` now always checks the availability of the writer classes before returning them. If one wishes, for example, to get the first available writer, without performing the availability check on subsequent writers, it is now possible to iterate over the registry, which will yield the names of the @@ -88,13 +88,13 @@ enough to be accommodated by the default data limit margins. While the new behavior is algorithmically simpler, it is conditional on properties of the `.Collection` object: - 1. ``offsets = None``, ``transform`` is a child of `.Axes.transData`: use the paths + 1. ``offsets = None``, ``transform`` is a child of ``Axes.transData``: use the paths for the automatic limits (i.e. for `.LineCollection` in `.Axes.streamplot`). - 2. ``offsets != None``, and ``offset_transform`` is child of `.Axes.transData`: + 2. ``offsets != None``, and ``offset_transform`` is child of ``Axes.transData``: - a) ``transform`` is child of `.Axes.transData`: use the ``path + offset`` for + a) ``transform`` is child of ``Axes.transData``: use the ``path + offset`` for limits (i.e., for `.Axes.bar`). - b) ``transform`` is not a child of `.Axes.transData`: just use the offsets + b) ``transform`` is not a child of ``Axes.transData``: just use the offsets for the limits (i.e. for scatter) 3. otherwise return a null `.Bbox`. @@ -294,7 +294,7 @@ Exception changes ~~~~~~~~~~~~~~~~~ Various APIs that raised a `ValueError` for incorrectly typed inputs now raise `TypeError` instead: `.backend_bases.GraphicsContextBase.set_clip_path`, -`.blocking_input.BlockingInput.__call__`, `.cm.register_cmap`, `.dviread.DviFont`, +``blocking_input.BlockingInput.__call__``, `.cm.register_cmap`, `.dviread.DviFont`, `.rcsetup.validate_hatch`, ``.rcsetup.validate_animation_writer_path``, `.spines.Spine`, many classes in the :mod:`matplotlib.transforms` module and :mod:`matplotlib.tri` package, and Axes methods that take a ``norm`` parameter. diff --git a/doc/api/prev_api_changes/api_changes_3.2.0/deprecations.rst b/doc/api/prev_api_changes/api_changes_3.2.0/deprecations.rst index 42de9ffccd77..65b72c7e0558 100644 --- a/doc/api/prev_api_changes/api_changes_3.2.0/deprecations.rst +++ b/doc/api/prev_api_changes/api_changes_3.2.0/deprecations.rst @@ -118,7 +118,7 @@ The unused ``Locator.autoscale`` method is deprecated (pass the axis limits to Animation ~~~~~~~~~ -The following methods and attributes of the `MovieWriterRegistry` class are +The following methods and attributes of the `.MovieWriterRegistry` class are deprecated: ``set_dirty``, ``ensure_not_dirty``, ``reset_available_writers``, ``avail``. diff --git a/doc/api/prev_api_changes/api_changes_3.2.0/development.rst b/doc/api/prev_api_changes/api_changes_3.2.0/development.rst index 470b594d522c..9af7fb8fb561 100644 --- a/doc/api/prev_api_changes/api_changes_3.2.0/development.rst +++ b/doc/api/prev_api_changes/api_changes_3.2.0/development.rst @@ -22,7 +22,7 @@ is desired. Packaging DLLs ~~~~~~~~~~~~~~ -Previously, it was possible to package Windows DLLs into the Maptlotlib +Previously, it was possible to package Windows DLLs into the Matplotlib wheel (or sdist) by copying them into the source tree and setting the ``package_data.dlls`` entry in ``setup.cfg``. diff --git a/doc/api/prev_api_changes/api_changes_3.3.0/behaviour.rst b/doc/api/prev_api_changes/api_changes_3.3.0/behaviour.rst index 2b21794ede6b..65807a184fbc 100644 --- a/doc/api/prev_api_changes/api_changes_3.3.0/behaviour.rst +++ b/doc/api/prev_api_changes/api_changes_3.3.0/behaviour.rst @@ -262,7 +262,7 @@ most common operations remain available), and the list-of-one `.Polygon` is returned as is. This makes the `repr` of the returned artist more accurate: it is now :: - # "bar", "barstacked" + # "bar", "barstacked" [] # "step", "stepfilled" instead of :: diff --git a/doc/api/prev_api_changes/api_changes_3.3.0/deprecations.rst b/doc/api/prev_api_changes/api_changes_3.3.0/deprecations.rst index 4eb73b16dd54..22aa93f89931 100644 --- a/doc/api/prev_api_changes/api_changes_3.3.0/deprecations.rst +++ b/doc/api/prev_api_changes/api_changes_3.3.0/deprecations.rst @@ -72,7 +72,7 @@ Revert deprecation \*min, \*max keyword arguments to ``set_x/y/zlim_3d()`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These keyword arguments were deprecated in 3.0, alongside with the respective parameters in ``set_xlim()`` / ``set_ylim()``. The deprecations of the 2D -versions were already reverted in in 3.1. +versions were already reverted in 3.1. ``cbook.local_over_kwdict`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -93,7 +93,7 @@ Parameters *norm* and *vmin*/*vmax* should not be used simultaneously ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Passing parameters *norm* and *vmin*/*vmax* simultaneously to functions using colormapping such as ``scatter()`` and ``imshow()`` is deprecated. -Inestead of ``norm=LogNorm(), vmin=min_val, vmax=max_val`` pass +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*. @@ -162,7 +162,7 @@ The ``on_mappable_changed`` and ``update_bruteforce`` methods of `~matplotlib.colorbar.Colorbar` are deprecated; both can be replaced by calls to `~matplotlib.colorbar.Colorbar.update_normal`. -``OldScalarFormatter``, ``IndexFormatter`` and ``DateIndexFormatter`` +``OldScalarFormatter``, ``IndexFormatter`` and ``IndexDateFormatter`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These formatters are deprecated. Their functionality can be implemented using e.g. `.FuncFormatter`. @@ -238,7 +238,7 @@ The following validators, defined in `.rcsetup`, are deprecated: ``validate_axes_titlelocation``, ``validate_toolbar``, ``validate_ps_papersize``, ``validate_legend_loc``, ``validate_bool_maybe_none``, ``validate_hinting``, -``validate_movie_writers``, ``validate_webagg_address``, +``validate_movie_writer``, ``validate_webagg_address``, ``validate_nseq_float``, ``validate_nseq_int``. To test whether an rcParam value would be acceptable, one can test e.g. ``rc = RcParams(); rc[k] = v`` raises an exception. @@ -351,7 +351,7 @@ PGF backend cleanups ~~~~~~~~~~~~~~~~~~~~ The *dummy* parameter of `.RendererPgf` is deprecated. -`.GraphicsContextPgf` is deprecated (use `.GraphicsContextBase` instead). +``GraphicsContextPgf`` is deprecated (use `.GraphicsContextBase` instead). ``set_factor`` method of :mod:`mpl_toolkits.axisartist` locators ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -575,7 +575,7 @@ This is deprecated; pass keys as a list of strings instead. Statusbar classes and attributes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The ``statusbar`` attribute of `.FigureManagerBase`, `.StatusbarBase` and all +The ``statusbar`` attribute of `.FigureManagerBase`, ``StatusbarBase`` and all its subclasses, and ``StatusBarWx``, are deprecated, as messages are now displayed in the toolbar instead. @@ -609,8 +609,8 @@ accepted. Qt modifier keys ~~~~~~~~~~~~~~~~ The ``MODIFIER_KEYS``, ``SUPER``, ``ALT``, ``CTRL``, and ``SHIFT`` -global variables of the :mod:`matplotlib.backends.backend_qt4agg`, -:mod:`matplotlib.backends.backend_qt4cairo`, +global variables of the ``matplotlib.backends.backend_qt4agg``, +``matplotlib.backends.backend_qt4cairo``, :mod:`matplotlib.backends.backend_qt5agg` and :mod:`matplotlib.backends.backend_qt5cairo` modules are deprecated. diff --git a/doc/api/prev_api_changes/api_changes_3.3.0/removals.rst b/doc/api/prev_api_changes/api_changes_3.3.0/removals.rst index 3f7c232e9800..36b63c6dcfc8 100644 --- a/doc/api/prev_api_changes/api_changes_3.3.0/removals.rst +++ b/doc/api/prev_api_changes/api_changes_3.3.0/removals.rst @@ -202,7 +202,7 @@ Arguments renamed to ``manage_ticks``. - The ``normed`` parameter of `~.Axes.hist2d` has been renamed to ``density``. - The ``s`` parameter of `.Annotation` has been renamed to ``text``. -- For all functions in `.bezier` that supported a ``tolerence`` parameter, this +- For all functions in `.bezier` that supported a ``tolerance`` parameter, this parameter has been renamed to ``tolerance``. - ``axis("normal")`` is not supported anymore. Use the equivalent ``axis("auto")`` instead. diff --git a/doc/api/prev_api_changes/api_changes_3.4.0/deprecations.rst b/doc/api/prev_api_changes/api_changes_3.4.0/deprecations.rst index 120797270954..3de8959bb3ef 100644 --- a/doc/api/prev_api_changes/api_changes_3.4.0/deprecations.rst +++ b/doc/api/prev_api_changes/api_changes_3.4.0/deprecations.rst @@ -63,12 +63,6 @@ similar replacements. ``STYLE_FILE_PATTERN``, ``load_base_library``, and ``iter_user_libraries`` are deprecated. -``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. - ``dpi_cor`` property of `.FancyArrowPatch` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/api/prev_api_changes/api_changes_3.4.0/development.rst b/doc/api/prev_api_changes/api_changes_3.4.0/development.rst index ab5e118de9e8..982046c3869e 100644 --- a/doc/api/prev_api_changes/api_changes_3.4.0/development.rst +++ b/doc/api/prev_api_changes/api_changes_3.4.0/development.rst @@ -4,7 +4,7 @@ Development changes Increase to minimum supported versions of Python and dependencies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -For Maptlotlib 3.4, the :ref:`minimum supported versions ` are +For Matplotlib 3.4, the :ref:`minimum supported versions ` are being bumped: +------------+-----------------+---------------+ diff --git a/doc/api/prev_api_changes/api_changes_3.4.2.rst b/doc/api/prev_api_changes/api_changes_3.4.2.rst new file mode 100644 index 000000000000..2de543be37d6 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.4.2.rst @@ -0,0 +1,16 @@ +API Changes for 3.4.2 +===================== + +Behaviour changes +----------------- + +Rename first argument 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/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..69e38270ca76 --- /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 +`~matplotlib.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 `~matplotlib.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 ``matplotlib.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 `~.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..7ce5132bc7fa --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.5.0/deprecations.rst @@ -0,0 +1,379 @@ +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.datetime64(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. +- ``Collection.__init__`` previously ignored *transOffset* without *offsets* also + being specified. In the future, *transOffset* will begin having an effect + regardless of *offsets*. In the meantime, if you wish to set *transOffset*, + call `.Collection.set_offset_transform` explicitly. +- ``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..2db21237a699 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.5.0/development.rst @@ -0,0 +1,82 @@ +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 wheel architectures +~~~~~~~~~~~~~~~~~~~~~~~ + +Wheels have been added for: + +- Python 3.10 +- PyPy 3.7 +- macOS on Apple Silicon (both arm64 and universal2) + +New build dependencies +~~~~~~~~~~~~~~~~~~~~~~ + +Versioning has been switched from bundled versioneer to `setuptools-scm +`__ using the +``release-branch-semver`` version scheme. The latter is well-maintained, but +may require slight modification to packaging scripts. + +The `setuptools-scm-git-archive +`__ plugin is also used +for consistent version export. + +Data directory is no longer optional +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Historically, the ``mpl-data`` directory has been optional (example files were +unnecessary, and fonts could be deleted if a suitable dependency on a system +font were provided). Though example files are still optional, they have been +substantially pared down, and we now consider the directory to be required. + +Specifically, the ``matplotlibrc`` file found there is used for runtime +verifications and must exist. Packagers may still symlink fonts to system +versions if needed. + +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``. The :file:`setup.cfg.template` has been +correspondingly been renamed to :file:`mplsetup.cfg.template`. + +Note that the path to this configuration file can still be set via the +:envvar:`MPLSETUPCFG` environment variable, which allows one to keep using the +same file before and after this change. 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..3ea9b338026d --- /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 ``docstring.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/api/prev_api_changes/api_changes_3.5.2.rst b/doc/api/prev_api_changes/api_changes_3.5.2.rst new file mode 100644 index 000000000000..47b000de0350 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.5.2.rst @@ -0,0 +1,13 @@ +API Changes for 3.5.2 +===================== + +.. contents:: + :local: + :depth: 1 + +QuadMesh mouseover defaults to False +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +New in 3.5, `.QuadMesh.get_cursor_data` allows display of data values +under the cursor. However, this can be very slow for large meshes, so +by ``.QuadMesh.set_mouseover`` defaults to *False*. diff --git a/doc/api/prev_api_changes/api_changes_3.5.3.rst b/doc/api/prev_api_changes/api_changes_3.5.3.rst new file mode 100644 index 000000000000..03d1f476513e --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.5.3.rst @@ -0,0 +1,13 @@ +API Changes for 3.5.3 +===================== + +.. contents:: + :local: + :depth: 1 + +Passing *linefmt* positionally is undeprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Positional use of all formatting parameters in `~.Axes.stem` has been +deprecated since Matplotlib 3.5. This deprecation is relaxed so that one can +still pass *linefmt* positionally, i.e. ``stem(x, y, 'r')``. diff --git a/doc/api/prev_api_changes/api_changes_3.6.0.rst b/doc/api/prev_api_changes/api_changes_3.6.0.rst new file mode 100644 index 000000000000..1bba4506fd7d --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.6.0.rst @@ -0,0 +1,14 @@ +API Changes for 3.6.0 +===================== + +.. contents:: + :local: + :depth: 1 + +.. include:: /api/prev_api_changes/api_changes_3.6.0/behaviour.rst + +.. include:: /api/prev_api_changes/api_changes_3.6.0/deprecations.rst + +.. include:: /api/prev_api_changes/api_changes_3.6.0/removals.rst + +.. include:: /api/prev_api_changes/api_changes_3.6.0/development.rst diff --git a/doc/api/prev_api_changes/api_changes_3.6.0/behaviour.rst b/doc/api/prev_api_changes/api_changes_3.6.0/behaviour.rst new file mode 100644 index 000000000000..a35584b04961 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.6.0/behaviour.rst @@ -0,0 +1,248 @@ +Behaviour changes +----------------- + +``plt.get_cmap`` and ``matplotlib.cm.get_cmap`` return a copy +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Formerly, `~.pyplot.get_cmap` and `.cm.get_cmap` returned a global version of a +`.Colormap`. This was prone to errors as modification of the colormap would +propagate from one location to another without warning. Now, a new copy of the +colormap is returned. + +Large ``imshow`` images are now downsampled +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When showing an image using `~matplotlib.axes.Axes.imshow` that has more than +:math:`2^{24}` columns or :math:`2^{23}` rows, the image will now be +downsampled to below this resolution before being resampled for display by the +AGG renderer. Previously such a large image would be shown incorrectly. To +prevent this downsampling and the warning it raises, manually downsample your +data before handing it to `~matplotlib.axes.Axes.imshow`. + +Default date limits changed to 1970-01-01 – 1970-01-02 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously the default limits for an empty axis set up for dates +(`.Axis.axis_date`) was 2000-01-01 to 2010-01-01. This has been changed to +1970-01-01 to 1970-01-02. With the default epoch, this makes the numeric limit +for date axes the same as for other axes (0.0-1.0), and users are less likely +to set a locator with far too many ticks. + +*markerfmt* argument to ``stem`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The behavior of the *markerfmt* parameter of `~.Axes.stem` has changed: + +- If *markerfmt* does not contain a color, the color is taken from *linefmt*. +- If *markerfmt* does not contain a marker, the default is 'o'. + +Before, *markerfmt* was passed unmodified to ``plot(..., fmt)``, which had a +number of unintended side-effects; e.g. only giving a color switched to a solid +line without markers. + +For a simple call ``stem(x, y)`` without parameters, the new rules still +reproduce the old behavior. + +``get_ticklabels`` now always populates labels +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously `.Axis.get_ticklabels` (and `.Axes.get_xticklabels`, +`.Axes.get_yticklabels`) would only return empty strings unless a draw had +already been performed. Now the ticks and their labels are updated when the +labels are requested. + +Warning when scatter plot color settings discarded +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When making an animation of a scatter plot, if you don't set *c* (the color +value parameter) when initializing the artist, the color settings are ignored. +`.Axes.scatter` now raises a warning if color-related settings are changed +without setting *c*. + +3D ``contourf`` polygons placed between levels +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The polygons used in a 3D `~.Axes3D.contourf` plot are now placed halfway +between the contour levels, as each polygon represents the location of values +that lie between two levels. + +Axes title now avoids y-axis offset +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, Axes titles could overlap the y-axis offset text, which is often in +the upper left corner of the axes. Now titles are moved above the offset text +if overlapping when automatic title positioning is in effect (i.e. if *y* in +`.Axes.set_title` is *None* and :rc:`axes.titley` is also *None*). + +Dotted operators gain extra space in mathtext +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In mathtext, ``\doteq \doteqdot \dotminus \dotplus \dots`` are now surrounded +by extra space because they are correctly treated as relational or binary +operators. + +*math* parameter of ``mathtext.get_unicode_index`` defaults to False +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In math mode, ASCII hyphens (U+002D) are now replaced by Unicode minus signs +(U+2212) at the parsing stage. + +``ArtistList`` proxies copy contents on iteration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When iterating over the contents of the dynamically generated proxy lists for +the Artist-type accessors (see :ref:`Behavioural API Changes 3.5 - Axes +children combined`), a copy of the contents is made. This ensure that artists +can safely be added or removed from the Axes while iterating over their +children. + +This is a departure from the expected behavior of mutable iterable data types +in Python — iterating over a list while mutating it has surprising consequences +and dictionaries will error if they change size during iteration. Because all +of the accessors are filtered views of the same underlying list, it is possible +for seemingly unrelated changes, such as removing a Line, to affect the +iteration over any of the other accessors. In this case, we have opted to make +a copy of the relevant children before yielding them to the user. + +This change is also consistent with our plan to make these accessors immutable +in Matplotlib 3.7. + +``AxesImage`` string representation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The string representation of `.AxesImage` changes from stating the position in +the figure ``"AxesImage(80,52.8;496x369.6)"`` to giving the number of pixels +``"AxesImage(size=(300, 200))"``. + +Improved autoscaling for Bézier curves +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Bézier curves are now autoscaled to their extents - previously they were +autoscaled to their ends and control points, which in some cases led to +unnecessarily large limits. + +``QuadMesh`` mouseover defaults to False +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +New in 3.5, `.QuadMesh.get_cursor_data` allows display of data values under the +cursor. However, this can be very slow for large meshes, so mouseover now +defaults to *False*. + +Changed pgf backend document class +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The pgf backend now uses the ``article`` document class as basis for +compilation. + +``MathtextBackendAgg.get_results`` no longer returns ``used_characters`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The last item (``used_characters``) in the tuple returned by +``MathtextBackendAgg.get_results`` has been removed. In order to unpack this +tuple in a backward and forward-compatible way, use e.g. ``ox, oy, width, +height, descent, image, *_ = parse(...)``, which will ignore +``used_characters`` if it was present. + +``Type1Font`` objects include more properties +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``matplotlib._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 +``matplotlib._type1font.Type1Font.transform`` method now correctly removes +``UniqueID`` properties from the font. + +``rcParams.copy()`` returns ``RcParams`` rather than ``dict`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Returning an `.RcParams` instance from `.RcParams.copy` makes the copy still +validate inputs, and additionally avoids emitting deprecation warnings when +using a previously copied instance to update the global instance (even if some +entries are deprecated). + +``rc_context`` no longer resets the value of ``'backend'`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`matplotlib.rc_context` incorrectly reset the value of :rc:`backend` if backend +resolution was triggered in the context. This affected only the value. The +actual backend was not changed. Now, `matplotlib.rc_context` does not reset +:rc:`backend` anymore. + +Default ``rcParams["animation.convert_args"]`` changed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It now defaults to ``["-layers", "OptimizePlus"]`` to try to generate smaller +GIFs. Set it back to an empty list to recover the previous behavior. + +Style file encoding now specified to be UTF-8 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It has been impossible to import Matplotlib with a non UTF-8 compatible locale +encoding because we read the style library at import time. This change is +formalizing and documenting the status quo so there is no deprecation period. + +MacOSX backend uses sRGB instead of GenericRGB color space +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +MacOSX backend now display sRGB tagged image instead of GenericRGB which is an +older (now deprecated) Apple color space. This is the source color space used +by ColorSync to convert to the current display profile. + +Renderer optional for ``get_tightbbox`` and ``get_window_extent`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The `.Artist.get_tightbbox` and `.Artist.get_window_extent` methods no longer +require the *renderer* keyword argument, saving users from having to query it +from ``fig.canvas.get_renderer``. If the *renderer* keyword argument is not +supplied, these methods first check if there is a cached renderer from a +previous draw and use that. If there is no cached renderer, then the methods +will use ``fig.canvas.get_renderer()`` as a fallback. + +``FigureFrameWx`` constructor, subclasses, and ``get_canvas`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``FigureCanvasWx`` constructor gained a *canvas_class* keyword-only +parameter which specifies the canvas class that should be used. This parameter +will become required in the future. The ``get_canvas`` method, which was +previously used to customize canvas creation, is deprecated. The +``FigureFrameWxAgg`` and ``FigureFrameWxCairo`` subclasses, which overrode +``get_canvas``, are deprecated. + +``FigureFrameWx.sizer`` +~~~~~~~~~~~~~~~~~~~~~~~ + +... has been removed. The frame layout is no longer based on a sizer, as the +canvas is now the sole child widget; the toolbar is now a regular toolbar added +using ``SetToolBar``. + +Incompatible layout engines raise +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You cannot switch between ``tight_layout`` and ``constrained_layout`` if a +colorbar has already been added to a figure. Invoking the incompatible layout +engine used to warn, but now raises with a `RuntimeError`. + +``CallbackRegistry`` raises on unknown signals +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When Matplotlib instantiates a `.CallbackRegistry`, it now limits callbacks to +the signals that the registry knows about. In practice, this means that calling +`~.FigureCanvasBase.mpl_connect` with an invalid signal name now raises a +`ValueError`. + +Changed exception type for incorrect SVG date metadata +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Providing date metadata with incorrect type to the SVG backend earlier resulted +in a `ValueError`. Now, a `TypeError` is raised instead. + +Specified exception types in ``Grid`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In a few cases an `Exception` was thrown when an incorrect argument value was +set in the `mpl_toolkits.axes_grid1.axes_grid.Grid` (= +`mpl_toolkits.axisartist.axes_grid.Grid`) constructor. These are replaced as +follows: + +* Providing an incorrect value for *ngrids* now raises a `ValueError` +* Providing an incorrect type for *rect* now raises a `TypeError` diff --git a/doc/api/prev_api_changes/api_changes_3.6.0/deprecations.rst b/doc/api/prev_api_changes/api_changes_3.6.0/deprecations.rst new file mode 100644 index 000000000000..3a9e91e12289 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.6.0/deprecations.rst @@ -0,0 +1,414 @@ +Deprecations +------------ + +Parameters to ``plt.figure()`` and the ``Figure`` constructor +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All parameters to `.pyplot.figure` and the `.Figure` constructor, other than +*num*, *figsize*, and *dpi*, will become keyword-only after a deprecation +period. + +Deprecation aliases in cbook +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The module ``matplotlib.cbook.deprecation`` was previously deprecated in +Matplotlib 3.4, along with deprecation-related API in ``matplotlib.cbook``. Due +to technical issues, ``matplotlib.cbook.MatplotlibDeprecationWarning`` and +``matplotlib.cbook.mplDeprecation`` did not raise deprecation warnings on use. +Changes in Python have now made it possible to warn when these aliases are +being used. + +In order to avoid downstream breakage, these aliases will now warn, and their +removal has been pushed from 3.6 to 3.8 to give time to notice said warnings. +As replacement, please use `matplotlib.MatplotlibDeprecationWarning`. + +``Axes`` subclasses should override ``clear`` instead of ``cla`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For clarity, `.axes.Axes.clear` is now preferred over `.Axes.cla`. However, for +backwards compatibility, the latter will remain as an alias for the former. + +For additional compatibility with third-party libraries, Matplotlib will +continue to call the ``cla`` method of any `~.axes.Axes` subclasses if they +define it. In the future, this will no longer occur, and Matplotlib will only +call the ``clear`` method in `~.axes.Axes` subclasses. + +It is recommended to define only the ``clear`` method when on Matplotlib 3.6, +and only ``cla`` for older versions. + +Pending deprecation top-level cmap registration and access functions in ``mpl.cm`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As part of a `multi-step process +`_ we are refactoring +the global state for managing the registered colormaps. + +In Matplotlib 3.5 we added a `.ColormapRegistry` class and exposed an instance +at the top level as ``matplotlib.colormaps``. The existing top level functions +in `matplotlib.cm` (``get_cmap``, ``register_cmap``, ``unregister_cmap``) were +changed to be aliases around the same instance. + +In Matplotlib 3.6 we have marked those top level functions as pending +deprecation with the intention of deprecation in Matplotlib 3.7. The following +functions have been marked for pending deprecation: + +- ``matplotlib.cm.get_cmap``; use ``matplotlib.colormaps[name]`` instead if you + have a `str`. + + **Added 3.6.1** Use `matplotlib.cm.ColormapRegistry.get_cmap` if you + have a string, `None` or a `matplotlib.colors.Colormap` object that you want + to convert to a `matplotlib.colors.Colormap` instance. +- ``matplotlib.cm.register_cmap``; use `matplotlib.colormaps.register + <.ColormapRegistry.register>` instead +- ``matplotlib.cm.unregister_cmap``; use `matplotlib.colormaps.unregister + <.ColormapRegistry.unregister>` instead +- ``matplotlib.pyplot.register_cmap``; use `matplotlib.colormaps.register + <.ColormapRegistry.register>` instead + +The `matplotlib.pyplot.get_cmap` function will stay available for backward +compatibility. + +Pending deprecation of layout methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The methods `~.Figure.set_tight_layout`, `~.Figure.set_constrained_layout`, are +discouraged, and now emit a `PendingDeprecationWarning` in favor of explicitly +referencing the layout engine via ``figure.set_layout_engine('tight')`` and +``figure.set_layout_engine('constrained')``. End users should not see the +warning, but library authors should adjust. + +The methods `~.Figure.set_constrained_layout_pads` and +`~.Figure.get_constrained_layout_pads` are will be deprecated in favor of +``figure.get_layout_engine().set()`` and ``figure.get_layout_engine().get()``, +and currently emit a `PendingDeprecationWarning`. + +seaborn styles renamed +~~~~~~~~~~~~~~~~~~~~~~ + +Matplotlib currently ships many style files inspired from the seaborn library +("seaborn", "seaborn-bright", "seaborn-colorblind", etc.) but they have gone +out of sync with the library itself since the release of seaborn 0.9. To +prevent confusion, the style files have been renamed "seaborn-v0_8", +"seaborn-v0_8-bright", "seaborn-v0_8-colorblind", etc. Users are encouraged to +directly use seaborn to access the up-to-date styles. + +Auto-removal of overlapping Axes by ``plt.subplot`` and ``plt.subplot2grid`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, `.pyplot.subplot` and `.pyplot.subplot2grid` would automatically +remove preexisting Axes that overlap with the newly added Axes. This behavior +was deemed confusing, and is now deprecated. Explicitly call ``ax.remove()`` on +Axes that need to be removed. + +Passing *linefmt* positionally to ``stem`` is undeprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Positional use of all formatting parameters in `~.Axes.stem` has been +deprecated since Matplotlib 3.5. This deprecation is relaxed so that one can +still pass *linefmt* positionally, i.e. ``stem(x, y, 'r')``. + +``stem(..., use_line_collection=False)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... is deprecated with no replacement. This was a compatibility fallback to a +former more inefficient representation of the stem lines. + +Positional / keyword arguments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Passing all but the very few first arguments positionally in the constructors +of Artists is deprecated. Most arguments will become keyword-only in a future +version. + +Passing too many positional arguments to ``tripcolor`` is now deprecated (extra +arguments were previously silently ignored). + +Passing *emit* and *auto* parameters of ``set_xlim``, ``set_ylim``, +``set_zlim``, ``set_rlim`` positionally is deprecated; they will become +keyword-only in a future release. + +The *transOffset* parameter of `.Collection.set_offset_transform` and the +various ``create_collection`` methods of legend handlers has been renamed to +*offset_transform* (consistently with the property name). + +Calling ``MarkerStyle()`` with no arguments or ``MarkerStyle(None)`` is +deprecated; use ``MarkerStyle("")`` to construct an empty marker style. + +``Axes.get_window_extent`` / ``Figure.get_window_extent`` accept only +*renderer*. This aligns the API with the general `.Artist.get_window_extent` +API. All other parameters were ignored anyway. + +The *cleared* parameter of ``get_renderer``, which only existed for AGG-based +backends, has been deprecated. Use ``renderer.clear()`` instead to explicitly +clear the renderer buffer. + +Methods to set parameters in ``LogLocator`` and ``LogFormatter*`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In `~.LogFormatter` and derived subclasses, the methods ``base`` and +``label_minor`` for setting the respective parameter are deprecated and +replaced by ``set_base`` and ``set_label_minor``, respectively. + +In `~.LogLocator`, the methods ``base`` and ``subs`` for setting the respective +parameter are deprecated. Instead, use ``set_params(base=..., subs=...)``. + +``Axes.get_renderer_cache`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The canvas now takes care of the renderer and whether to cache it or not. The +alternative is to call ``axes.figure.canvas.get_renderer()``. + +Groupers from ``get_shared_x_axes`` / ``get_shared_y_axes`` will be immutable +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Modifications to the Groupers returned by ``get_shared_x_axes`` and +``get_shared_y_axes`` are deprecated. In the future, these methods will return +immutable views on the grouper structures. Note that previously, calling e.g. +``join()`` would already fail to set up the correct structures for sharing +axes; use `.Axes.sharex` or `.Axes.sharey` instead. + +Unused methods in ``Axis``, ``Tick``, ``XAxis``, and ``YAxis`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``Tick.label`` has been pending deprecation since 3.1 and is now deprecated. +Use ``Tick.label1`` instead. + +The following methods are no longer used and deprecated without a replacement: + +- ``Axis.get_ticklabel_extents`` +- ``Tick.get_pad_pixels`` +- ``XAxis.get_text_heights`` +- ``YAxis.get_text_widths`` + +``mlab.stride_windows`` +~~~~~~~~~~~~~~~~~~~~~~~ + +... is deprecated. Use ``np.lib.stride_tricks.sliding_window_view`` instead (or +``np.lib.stride_tricks.as_strided`` on NumPy < 1.20). + +Event handlers +~~~~~~~~~~~~~~ + +The ``draw_event``, ``resize_event``, ``close_event``, ``key_press_event``, +``key_release_event``, ``pick_event``, ``scroll_event``, +``button_press_event``, ``button_release_event``, ``motion_notify_event``, +``enter_notify_event`` and ``leave_notify_event`` methods of +`.FigureCanvasBase` are deprecated. They had inconsistent signatures across +backends, and made it difficult to improve event metadata. + +In order to trigger an event on a canvas, directly construct an `.Event` object +of the correct class and call ``canvas.callbacks.process(event.name, event)``. + +Widgets +~~~~~~~ + +All parameters to ``MultiCursor`` starting from *useblit* are becoming +keyword-only (passing them positionally is deprecated). + +The ``canvas`` and ``background`` attributes of ``MultiCursor`` are deprecated +with no replacement. + +The *visible* attribute of Selector widgets has been deprecated; use +``set_visible`` or ``get_visible`` instead. + +The *state_modifier_keys* attribute of Selector widgets has been privatized and +the modifier keys must be set when creating the widget. + +``Axes3D.dist`` +~~~~~~~~~~~~~~~ + +... has been privatized. Use the *zoom* keyword argument in +`.Axes3D.set_box_aspect` instead. + +3D Axis +~~~~~~~ + +The previous constructor of `.axis3d.Axis`, with signature ``(self, adir, +v_intervalx, d_intervalx, axes, *args, rotate_label=None, **kwargs)`` is +deprecated in favor of a new signature closer to the one of 2D Axis; it is now +``(self, axes, *, rotate_label=None, **kwargs)`` where ``kwargs`` are forwarded +to the 2D Axis constructor. The axis direction is now inferred from the axis +class' ``axis_name`` attribute (as in the 2D case); the ``adir`` attribute is +deprecated. + +The ``init3d`` method of 3D Axis is also deprecated; all the relevant +initialization is done as part of the constructor. + +The ``d_interval`` and ``v_interval`` attributes of 3D Axis are deprecated; use +``get_data_interval`` and ``get_view_interval`` instead. + +The ``w_xaxis``, ``w_yaxis``, and ``w_zaxis`` attributes of ``Axis3D`` have +been pending deprecation since 3.1. They are now deprecated. Instead use +``xaxis``, ``yaxis``, and ``zaxis``. + +``mplot3d.axis3d.Axis.set_pane_pos`` is deprecated. This is an internal method +where the provided values are overwritten during drawing. Hence, it does not +serve any purpose to be directly accessible. + +The two helper functions ``mplot3d.axis3d.move_from_center`` and +``mplot3d.axis3d.tick_update_position`` are considered internal and deprecated. +If these are required, please vendor the code from the corresponding private +methods ``_move_from_center`` and ``_tick_update_position``. + +``Figure.callbacks`` is deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Figure ``callbacks`` property is deprecated. The only signal was +"dpi_changed", which can be replaced by connecting to the "resize_event" on the +canvas ``figure.canvas.mpl_connect("resize_event", func)`` instead. + +``FigureCanvas`` without a ``required_interactive_framework`` attribute +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Support for such canvas classes is deprecated. Note that canvas classes which +inherit from ``FigureCanvasBase`` always have such an attribute. + +Backend-specific deprecations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- ``backend_gtk3.FigureManagerGTK3Agg`` and + ``backend_gtk4.FigureManagerGTK4Agg``; directly use + ``backend_gtk3.FigureManagerGTK3`` and ``backend_gtk4.FigureManagerGTK4`` + instead. +- The *window* parameter to ``backend_gtk3.NavigationToolbar2GTK3`` had no + effect, and is now deprecated. +- ``backend_gtk3.NavigationToolbar2GTK3.win`` +- ``backend_gtk3.RendererGTK3Cairo`` and ``backend_gtk4.RendererGTK4Cairo``; + use `.RendererCairo` instead, which has gained the ``set_context`` method, + which also auto-infers the size of the underlying surface. +- ``backend_cairo.RendererCairo.set_ctx_from_surface`` and + ``backend_cairo.RendererCairo.set_width_height`` in favor of + `.RendererCairo.set_context`. +- ``backend_gtk3.error_msg_gtk`` +- ``backend_gtk3.icon_filename`` and ``backend_gtk3.window_icon`` +- ``backend_macosx.NavigationToolbar2Mac.prepare_configure_subplots`` has been + replaced by ``configure_subplots()``. +- ``backend_pdf.Name.hexify`` +- ``backend_pdf.Operator`` and ``backend_pdf.Op.op`` are deprecated in favor of + a single standard `enum.Enum` interface on `.backend_pdf.Op`. +- ``backend_pdf.fill``; vendor the code of the similarly named private + functions if you rely on these functions. +- ``backend_pgf.LatexManager.texcommand`` and + ``backend_pgf.LatexManager.latex_header`` +- ``backend_pgf.NO_ESCAPE`` +- ``backend_pgf.common_texification`` +- ``backend_pgf.get_fontspec`` +- ``backend_pgf.get_preamble`` +- ``backend_pgf.re_mathsep`` +- ``backend_pgf.writeln`` +- ``backend_ps.convert_psfrags`` +- ``backend_ps.quote_ps_string``; vendor the code of the similarly named + private functions if you rely on it. +- ``backend_qt.qApp``; use ``QtWidgets.QApplication.instance()`` instead. +- ``backend_svg.escape_attrib``; vendor the code of the similarly named private + functions if you rely on it. +- ``backend_svg.escape_cdata``; vendor the code of the similarly named private + functions if you rely on it. +- ``backend_svg.escape_comment``; vendor the code of the similarly named + private functions if you rely on it. +- ``backend_svg.short_float_fmt``; vendor the code of the similarly named + private functions if you rely on it. +- ``backend_svg.generate_transform`` and ``backend_svg.generate_css`` +- ``backend_tk.NavigationToolbar2Tk.lastrect`` and + ``backend_tk.RubberbandTk.lastrect`` +- ``backend_tk.NavigationToolbar2Tk.window``; use ``toolbar.master`` instead. +- ``backend_tools.ToolBase.destroy``; To run code upon tool removal, connect to + the ``tool_removed_event`` event. +- ``backend_wx.RendererWx.offset_text_height`` +- ``backend_wx.error_msg_wx`` + +- ``FigureCanvasBase.pick``; directly call `.Figure.pick`, which has taken over + the responsibility of checking the canvas widget lock as well. +- ``FigureCanvasBase.resize``, which has no effect; use + ``FigureManagerBase.resize`` instead. + +- ``FigureManagerMac.close`` + +- ``FigureFrameWx.sizer``; use ``frame.GetSizer()`` instead. +- ``FigureFrameWx.figmgr`` and ``FigureFrameWx.get_figure_manager``; use + ``frame.canvas.manager`` instead. +- ``FigureFrameWx.num``; use ``frame.canvas.manager.num`` instead. +- ``FigureFrameWx.toolbar``; use ``frame.GetToolBar()`` instead. +- ``FigureFrameWx.toolmanager``; use ``frame.canvas.manager.toolmanager`` + instead. + +Modules +~~~~~~~ + +The modules ``matplotlib.afm``, ``matplotlib.docstring``, +``matplotlib.fontconfig_pattern``, ``matplotlib.tight_bbox``, +``matplotlib.tight_layout``, and ``matplotlib.type1font`` are considered +internal and public access is deprecated. + +``checkdep_usetex`` deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This method was only intended to disable tests in case no latex install was +found. As such, it is considered to be private and for internal use only. + +Please vendor the code if you need this. + +``date_ticker_factory`` deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``date_ticker_factory`` method in the `matplotlib.dates` module is +deprecated. Instead use `~.AutoDateLocator` and `~.AutoDateFormatter` for a +more flexible and scalable locator and formatter. + +If you need the exact ``date_ticker_factory`` behavior, please copy the code. + +``dviread.find_tex_file`` will raise ``FileNotFoundError`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +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. + +``transforms.Affine2D.identity()`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... is deprecated in favor of directly calling the `.Affine2D` constructor with +no arguments. + +Deprecations in ``testing.decorators`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The unused class ``CleanupTestCase`` and decorator ``cleanup`` are deprecated +and will be removed. Vendor the code, including the private function +``_cleanup_cm``. + +The function ``check_freetype_version`` is considered internal and deprecated. +Vendor the code of the private function ``_check_freetype_version``. + +``text.get_rotation()`` +~~~~~~~~~~~~~~~~~~~~~~~ + +... is deprecated with no replacement. Copy the original implementation if +needed. + +Miscellaneous internals +~~~~~~~~~~~~~~~~~~~~~~~ + +- ``axes_grid1.axes_size.AddList``; use ``sum(sizes, start=Fixed(0))`` (for + example) to sum multiple size objects. +- ``axes_size.Padded``; use ``size + pad`` instead +- ``axes_size.SizeFromFunc``, ``axes_size.GetExtentHelper`` +- ``AxisArtistHelper.delta1`` and ``AxisArtistHelper.delta2`` +- ``axislines.GridHelperBase.new_gridlines`` and + ``axislines.Axes.new_gridlines`` +- ``cbook.maxdict``; use the standard library ``functools.lru_cache`` instead. +- ``_DummyAxis.dataLim`` and ``_DummyAxis.viewLim``; use + ``get_data_interval()``, ``set_data_interval()``, ``get_view_interval()``, + and ``set_view_interval()`` instead. +- ``GridSpecBase.get_grid_positions(..., raw=True)`` +- ``ImageMagickBase.delay`` and ``ImageMagickBase.output_args`` +- ``MathtextBackend``, ``MathtextBackendAgg``, ``MathtextBackendPath``, + ``MathTextWarning`` +- ``TexManager.get_font_config``; it previously returned an internal hashed key + for used for caching purposes. +- ``TextToPath.get_texmanager``; directly construct a `.texmanager.TexManager` + instead. +- ``ticker.is_close_to_int``; use ``math.isclose(x, round(x))`` instead. +- ``ticker.is_decade``; use ``y = numpy.log(x)/numpy.log(base); + numpy.isclose(y, numpy.round(y))`` instead. diff --git a/doc/api/prev_api_changes/api_changes_3.6.0/development.rst b/doc/api/prev_api_changes/api_changes_3.6.0/development.rst new file mode 100644 index 000000000000..fb9f1f3e21c5 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.6.0/development.rst @@ -0,0 +1,42 @@ +Development changes +------------------- + +Increase to minimum supported versions of dependencies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For Matplotlib 3.6, the :ref:`minimum supported versions ` are +being bumped: + ++------------+-----------------+---------------+ +| Dependency | min in mpl3.5 | min in mpl3.6 | ++============+=================+===============+ +| Python | 3.7 | 3.8 | ++------------+-----------------+---------------+ +| NumPy | 1.17 | 1.19 | ++------------+-----------------+---------------+ + +This is consistent with our :ref:`min_deps_policy` and `NEP29 +`__ + +Build setup options changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``gui_support.macosx`` setup option has been renamed to +``packages.macosx``. + +New wheel architectures +~~~~~~~~~~~~~~~~~~~~~~~ + +Wheels have been added for: + +- Python 3.11 +- PyPy 3.8 and 3.9 + +Increase to required versions of documentation dependencies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`sphinx`_ >= 3.0 and `numpydoc`_ >= 1.0 are now required for building the +documentation. + +.. _numpydoc: https://pypi.org/project/numpydoc/ +.. _sphinx: https://pypi.org/project/Sphinx/ diff --git a/doc/api/prev_api_changes/api_changes_3.6.0/removals.rst b/doc/api/prev_api_changes/api_changes_3.6.0/removals.rst new file mode 100644 index 000000000000..60b1771eea09 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.6.0/removals.rst @@ -0,0 +1,222 @@ +Removals +-------- + +The following deprecated APIs have been removed: + +Removed behaviour +~~~~~~~~~~~~~~~~~ + +Stricter validation of function parameters +.......................................... + +- Unknown keyword arguments to `.Figure.savefig`, `.pyplot.savefig`, and the + ``FigureCanvas.print_*`` methods now raise a `TypeError`, instead of being + ignored. +- Extra parameters to the `~.axes.Axes` constructor, i.e., those other than + *fig* and *rect*, are now keyword only. +- Passing arguments not specifically listed in the signatures of + `.Axes3D.plot_surface` and `.Axes3D.plot_wireframe` is no longer supported; + pass any extra arguments as keyword arguments instead. +- Passing positional arguments to `.LineCollection` has been removed; use + specific keyword argument names now. + +``imread`` no longer accepts URLs +................................. + +Passing a URL to `~.pyplot.imread()` has been removed. Please open the URL for +reading and directly use the Pillow API (e.g., +``PIL.Image.open(urllib.request.urlopen(url))``, or +``PIL.Image.open(io.BytesIO(requests.get(url).content))``) instead. + +MarkerStyle is immutable +........................ + +The methods ``MarkerStyle.set_fillstyle`` and ``MarkerStyle.set_marker`` have +been removed. Create a new `.MarkerStyle` with the respective parameters +instead. + +Passing bytes to ``FT2Font.set_text`` +..................................... + +... is no longer supported. Pass `str` instead. + +Support for passing tool names to ``ToolManager.add_tool`` +.......................................................... + +... has been removed. The second parameter to `.ToolManager.add_tool` must now +always be a tool class. + +``backend_tools.ToolFullScreen`` now inherits from ``ToolBase``, not from ``ToolToggleBase`` +............................................................................................ + +`.ToolFullScreen` can only switch between the non-fullscreen and fullscreen +states, but not unconditionally put the window in a given state; hence the +``enable`` and ``disable`` methods were misleadingly named. Thus, the +`.ToolToggleBase`-related API (``enable``, ``disable``, etc.) was removed. + +``BoxStyle._Base`` and ``transmute`` method of box styles +......................................................... + +... have been removed. Box styles implemented as classes no longer need to +inherit from a base class. + +Loaded modules logging +...................... + +The list of currently loaded modules is no longer logged at the DEBUG level at +Matplotlib import time, because it can produce extensive output and make other +valuable DEBUG statements difficult to find. If you were relying on this +output, please arrange for your own logging (the built-in `sys.modules` can be +used to get the currently loaded modules). + +Modules +~~~~~~~ + +- The ``cbook.deprecation`` module has been removed from the public API as it + is considered internal. +- The ``mpl_toolkits.axes_grid`` module has been removed. All functionality from + ``mpl_toolkits.axes_grid`` can be found in either `mpl_toolkits.axes_grid1` + or `mpl_toolkits.axisartist`. Axes classes from ``mpl_toolkits.axes_grid`` + based on ``Axis`` from `mpl_toolkits.axisartist` can be found in + `mpl_toolkits.axisartist`. + +Classes, methods and attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following module-level classes/variables have been removed: + +- ``cm.cmap_d`` +- ``colorbar.colorbar_doc``, ``colorbar.colorbar_kw_doc`` +- ``ColorbarPatch`` +- ``mathtext.Fonts`` and all its subclasses +- ``mathtext.FontConstantsBase`` and all its subclasses +- ``mathtext.latex_to_bakoma``, ``mathtext.latex_to_cmex``, + ``mathtext.latex_to_standard`` +- ``mathtext.MathtextBackendPdf``, ``mathtext.MathtextBackendPs``, + ``mathtext.MathtextBackendSvg``, ``mathtext.MathtextBackendCairo``; use + `.MathtextBackendPath` instead. +- ``mathtext.Node`` and all its subclasses +- ``mathtext.NUM_SIZE_LEVELS`` +- ``mathtext.Parser`` +- ``mathtext.Ship`` +- ``mathtext.SHRINK_FACTOR`` and ``mathtext.GROW_FACTOR`` +- ``mathtext.stix_virtual_fonts``, +- ``mathtext.tex2uni`` +- ``backend_pgf.TmpDirCleaner`` +- ``backend_ps.GraphicsContextPS``; use ``GraphicsContextBase`` instead. +- ``backend_wx.IDLE_DELAY`` +- ``axes_grid1.parasite_axes.ParasiteAxesAuxTransBase``; use + `.ParasiteAxesBase` instead. +- ``axes_grid1.parasite_axes.ParasiteAxesAuxTrans``; use `.ParasiteAxes` + instead. + +The following class attributes have been removed: + +- ``Line2D.validCap`` and ``Line2D.validJoin``; validation is centralized in + ``rcsetup``. +- ``Patch.validCap`` and ``Patch.validJoin``; validation is centralized in + ``rcsetup``. +- ``renderer.M``, ``renderer.eye``, ``renderer.vvec``, + ``renderer.get_axis_position`` placed on the Renderer during 3D Axes draw; + these attributes are all available via `.Axes3D`, which can be accessed via + ``self.axes`` on all `.Artist`\s. +- ``RendererPdf.mathtext_parser``, ``RendererPS.mathtext_parser``, + ``RendererSVG.mathtext_parser``, ``RendererCairo.mathtext_parser`` +- ``StandardPsFonts.pswriter`` +- ``Subplot.figbox``; use `.Axes.get_position` instead. +- ``Subplot.numRows``; ``ax.get_gridspec().nrows`` instead. +- ``Subplot.numCols``; ``ax.get_gridspec().ncols`` instead. +- ``SubplotDivider.figbox`` +- ``cids``, ``cnt``, ``observers``, ``change_observers``, and + ``submit_observers`` on all `.Widget`\s + +The following class methods have been removed: + +- ``Axis.cla()``; use `.Axis.clear` instead. +- ``RadialAxis.cla()`` and ``ThetaAxis.cla()``; use `.RadialAxis.clear` or + `.ThetaAxis.clear` instead. +- ``Spine.cla()``; use `.Spine.clear` instead. +- ``ContourLabeler.get_label_coords()``; there is no replacement as it was + considered an internal helper. +- ``FancyArrowPatch.get_dpi_cor`` and ``FancyArrowPatch.set_dpi_cor`` + +- ``FigureCanvas.get_window_title()`` and ``FigureCanvas.set_window_title()``; + use `.FigureManagerBase.get_window_title` or + `.FigureManagerBase.set_window_title` if using pyplot, or use GUI-specific + methods if embedding. +- ``FigureManager.key_press()`` and ``FigureManager.button_press()``; trigger + the events directly on the canvas using + ``canvas.callbacks.process(event.name, event)`` for key and button events. + +- ``RendererAgg.get_content_extents()`` and + ``RendererAgg.tostring_rgba_minimized()`` +- ``NavigationToolbar2Wx.get_canvas()`` + +- ``ParasiteAxesBase.update_viewlim()``; use `.ParasiteAxesBase.apply_aspect` + instead. +- ``Subplot.get_geometry()``; use `.SubplotBase.get_subplotspec` instead. +- ``Subplot.change_geometry()``; use `.SubplotBase.set_subplotspec` instead. +- ``Subplot.update_params()``; this method did nothing. +- ``Subplot.is_first_row()``; use ``ax.get_subplotspec().is_first_row`` + instead. +- ``Subplot.is_first_col()``; use ``ax.get_subplotspec().is_first_col`` + instead. +- ``Subplot.is_last_row()``; use ``ax.get_subplotspec().is_last_row`` instead. +- ``Subplot.is_last_col()``; use ``ax.get_subplotspec().is_last_col`` instead. +- ``SubplotDivider.change_geometry()``; use `.SubplotDivider.set_subplotspec` + instead. +- ``SubplotDivider.get_geometry()``; use `.SubplotDivider.get_subplotspec` + instead. +- ``SubplotDivider.update_params()`` +- ``get_depth``, ``parse``, ``to_mask``, ``to_rgba``, and ``to_png`` of + `.MathTextParser`; use `.mathtext.math_to_image` instead. + +- ``MovieWriter.cleanup()``; the cleanup logic is instead fully implemented in + `.MovieWriter.finish` and ``cleanup`` is no longer called. + +Functions +~~~~~~~~~ + +The following functions have been removed; + +- ``backend_template.new_figure_manager()``, + ``backend_template.new_figure_manager_given_figure()``, and + ``backend_template.draw_if_interactive()`` have been removed, as part of the + introduction of the simplified backend API. +- Deprecation-related re-imports ``cbook.deprecated()``, and + ``cbook.warn_deprecated()``. +- ``colorbar.colorbar_factory()``; use `.Colorbar` instead. + ``colorbar.make_axes_kw_doc()`` +- ``mathtext.Error()`` +- ``mathtext.ship()`` +- ``mathtext.tex2uni()`` +- ``axes_grid1.parasite_axes.parasite_axes_auxtrans_class_factory()``; use + `.parasite_axes_class_factory` instead. +- ``sphinext.plot_directive.align()``; use + ``docutils.parsers.rst.directives.images.Image.align`` instead. + +Arguments +~~~~~~~~~ + +The following arguments have been removed: + +- *dpi* from ``print_ps()`` in the PS backend and ``print_pdf()`` in the PDF + backend. Instead, the methods will obtain the DPI from the ``savefig`` + machinery. +- *dpi_cor* from `~.FancyArrowPatch` +- *minimum_descent* from ``TextArea``; it is now effectively always True +- *origin* from ``FigureCanvasWx.gui_repaint()`` +- *project* from ``Line3DCollection.draw()`` +- *renderer* from `.Line3DCollection.do_3d_projection`, + `.Patch3D.do_3d_projection`, `.PathPatch3D.do_3d_projection`, + `.Path3DCollection.do_3d_projection`, `.Patch3DCollection.do_3d_projection`, + `.Poly3DCollection.do_3d_projection` +- *resize_callback* from the Tk backend; use + ``get_tk_widget().bind('', ..., True)`` instead. +- *return_all* from ``gridspec.get_position()`` +- Keyword arguments to ``gca()``; there is no replacement. + +rcParams +~~~~~~~~ + +The setting :rc:`ps.useafm` no longer has any effect on `matplotlib.mathtext`. diff --git a/doc/api/prev_api_changes/api_changes_3.6.1.rst b/doc/api/prev_api_changes/api_changes_3.6.1.rst new file mode 100644 index 000000000000..ad929d426885 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.6.1.rst @@ -0,0 +1,15 @@ +API Changes for 3.6.1 +===================== + +Deprecations +------------ + +Colorbars for orphaned mappables are deprecated, but no longer raise +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before 3.6.0, Colorbars for mappables that do not have a parent Axes would +steal space from the current Axes. 3.6.0 raised an error on this, but without a +deprecation cycle. For 3.6.1 this is reverted; the current Axes is used, but a +deprecation warning is shown instead. In this undetermined case, users and +libraries should explicitly specify what Axes they want space to be stolen +from: ``fig.colorbar(mappable, ax=plt.gca())``. diff --git a/doc/api/projections_api.rst b/doc/api/projections_api.rst index e7c807957925..ff12a2be8623 100644 --- a/doc/api/projections_api.rst +++ b/doc/api/projections_api.rst @@ -6,11 +6,16 @@ :members: :show-inheritance: - -******************************** ``matplotlib.projections.polar`` -******************************** +================================ .. automodule:: matplotlib.projections.polar :members: :show-inheritance: + +``matplotlib.projections.geo`` +============================== + +.. automodule:: matplotlib.projections.geo + :members: + :show-inheritance: diff --git a/doc/api/pyplot_summary.rst b/doc/api/pyplot_summary.rst index f3f4c88b78e8..d329d495709c 100644 --- a/doc/api/pyplot_summary.rst +++ b/doc/api/pyplot_summary.rst @@ -2,31 +2,185 @@ ``matplotlib.pyplot`` ********************* -Pyplot function overview ------------------------- +.. currentmodule:: matplotlib.pyplot + +.. automodule:: matplotlib.pyplot + :no-members: + :no-undoc-members: -.. currentmodule:: matplotlib + +Plotting commands +----------------- .. autosummary:: :toctree: _as_gen - :template: autofunctions.rst + :template: autosummary.rst + :nosignatures: - pyplot + acorr + angle_spectrum + annotate + arrow + autoscale + axes + axhline + axhspan + axis + axline + axvline + axvspan + bar + bar_label + barbs + barh + box + boxplot + broken_barh + cla + clabel + clf + clim + close + cohere + colorbar + contour + contourf + csd + delaxes + draw + draw_if_interactive + errorbar + eventplot + figimage + figlegend + fignum_exists + figtext + figure + fill + fill_between + fill_betweenx + findobj + gca + gcf + gci + get + get_cmap + get_figlabels + get_fignums + getp + grid + hexbin + hist + hist2d + hlines + imread + imsave + imshow + install_repl_displayhook + ioff + ion + isinteractive + legend + locator_params + loglog + magnitude_spectrum + margins + matshow + minorticks_off + minorticks_on + pause + pcolor + pcolormesh + phase_spectrum + pie + plot + plot_date + polar + psd + quiver + quiverkey + rc + rc_context + rcdefaults + rgrids + savefig + sca + scatter + sci + semilogx + semilogy + set_cmap + set_loglevel + setp + show + specgram + spy + stackplot + stairs + stem + step + streamplot + subplot + subplot2grid + subplot_mosaic + subplot_tool + subplots + subplots_adjust + suptitle + switch_backend + table + text + thetagrids + tick_params + ticklabel_format + tight_layout + title + tricontour + tricontourf + tripcolor + triplot + twinx + twiny + uninstall_repl_displayhook + violinplot + vlines + xcorr + xkcd + xlabel + xlim + xscale + xticks + ylabel + ylim + yscale + yticks -.. currentmodule:: matplotlib.pyplot -.. autofunction:: plotting +Other commands +-------------- +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + connect + disconnect + get_current_fig_manager + ginput + new_figure_manager + waitforbuttonpress -Colors in Matplotlib --------------------- -There are many colormaps you can use to map data onto color values. -Below we list several ways in which color can be utilized in Matplotlib. +Colormaps +--------- +Colormaps are available via the colormap registry `matplotlib.colormaps`. For +convenience this registry is available in ``pyplot`` as -For a more in-depth look at colormaps, see the -:doc:`/tutorials/colors/colormaps` tutorial. +.. autodata:: colormaps + :no-value: -.. currentmodule:: matplotlib.pyplot +Additionally, there are shortcut functions to set builtin colormaps; e.g. +``plt.viridis()`` is equivalent to ``plt.set_cmap('viridis')``. -.. autofunction:: colormaps +.. autodata:: color_sequences + :no-value: diff --git a/doc/api/sphinxext_mathmpl_api.rst b/doc/api/sphinxext_mathmpl_api.rst new file mode 100644 index 000000000000..839334ca39fe --- /dev/null +++ b/doc/api/sphinxext_mathmpl_api.rst @@ -0,0 +1,7 @@ +================================ +``matplotlib.sphinxext.mathmpl`` +================================ + +.. automodule:: matplotlib.sphinxext.mathmpl + :exclude-members: latex_math + :no-undoc-members: diff --git a/doc/api/text_api.rst b/doc/api/text_api.rst index c88d45f2832b..8bed3173ebdb 100644 --- a/doc/api/text_api.rst +++ b/doc/api/text_api.rst @@ -3,6 +3,19 @@ ******************* .. automodule:: matplotlib.text + :no-members: + +.. autoclass:: matplotlib.text.Text + :members: + :undoc-members: + :show-inheritance: + +.. autoclass:: matplotlib.text.Annotation + :members: + :undoc-members: + :show-inheritance: + +.. autoclass:: matplotlib.text.OffsetFrom :members: :undoc-members: :show-inheritance: diff --git a/doc/api/tight_bbox_api.rst b/doc/api/tight_bbox_api.rst index 3a96b5b6d027..9e8dd2fa66f9 100644 --- a/doc/api/tight_bbox_api.rst +++ b/doc/api/tight_bbox_api.rst @@ -2,7 +2,12 @@ ``matplotlib.tight_bbox`` ************************* -.. automodule:: matplotlib.tight_bbox +.. attention:: + This module is considered internal. + + Its use is deprecated and it will be removed in a future version. + +.. automodule:: matplotlib._tight_bbox :members: :undoc-members: :show-inheritance: diff --git a/doc/api/tight_layout_api.rst b/doc/api/tight_layout_api.rst index 1f1a32281aa0..35f92e3ddced 100644 --- a/doc/api/tight_layout_api.rst +++ b/doc/api/tight_layout_api.rst @@ -2,7 +2,12 @@ ``matplotlib.tight_layout`` *************************** -.. automodule:: matplotlib.tight_layout +.. attention:: + This module is considered internal. + + Its use is deprecated and it will be removed in a future version. + +.. automodule:: matplotlib._tight_layout :members: :undoc-members: :show-inheritance: diff --git a/doc/api/toolkits/axes_grid.rst b/doc/api/toolkits/axes_grid.rst deleted file mode 100644 index 991b0ff6813a..000000000000 --- a/doc/api/toolkits/axes_grid.rst +++ /dev/null @@ -1,26 +0,0 @@ -.. _axes_grid-api-index: - -Matplotlib axes_grid Toolkit -============================ - -.. currentmodule:: mpl_toolkits - - -.. note:: - AxesGrid toolkit has been a part of matplotlib since v - 0.99. Originally, the toolkit had a single namespace of - *axes_grid*. In more recent version, the toolkit - has divided into two separate namespace (*axes_grid1* and *axisartist*). - While *axes_grid* namespace is maintained for the backward compatibility, - use of *axes_grid1* and *axisartist* is recommended. - For the documentation on ``axes_grid``, - see the `previous version of the docs - `_. - -.. toctree:: - :maxdepth: 1 - - axes_grid1 - axisartist - - diff --git a/doc/api/toolkits/axes_grid1.rst b/doc/api/toolkits/axes_grid1.rst index 3abbaf8f22c0..c48a6a31af90 100644 --- a/doc/api/toolkits/axes_grid1.rst +++ b/doc/api/toolkits/axes_grid1.rst @@ -1,13 +1,14 @@ .. module:: mpl_toolkits.axes_grid1 -Matplotlib axes_grid1 Toolkit -============================= +.. redirect-from:: /api/toolkits/axes_grid -The matplotlib :mod:`mpl_toolkits.axes_grid1` toolkit is a collection of -helper classes to ease displaying multiple images in matplotlib. While the -aspect parameter in matplotlib adjust the position of the single axes, -axes_grid1 toolkit provides a framework to adjust the position of -multiple axes according to their aspects. +``mpl_toolkits.axes_grid1`` +=========================== + +:mod:`mpl_toolkits.axes_grid1` provides a framework of helper classes to adjust +the positioning of multiple fixed-aspect Axes (e.g., displaying images). It +can be contrasted with the ``aspect`` property of Matplotlib Axes, which +adjusts the position of a single Axes. See :ref:`axes_grid1_users-guide-index` for a guide on the usage of axes_grid1. @@ -16,6 +17,13 @@ See :ref:`axes_grid1_users-guide-index` for a guide on the usage of axes_grid1. :align: center :scale: 50 +.. note:: + + This module contains classes and function that were formerly part of the + ``mpl_toolkits.axes_grid`` module that was removed in 3.6. Additional + classes from that older module may also be found in + `mpl_toolkits.axisartist`. + .. currentmodule:: mpl_toolkits **The submodules of the axes_grid1 API are:** @@ -32,5 +40,3 @@ See :ref:`axes_grid1_users-guide-index` for a guide on the usage of axes_grid1. axes_grid1.inset_locator axes_grid1.mpl_axes axes_grid1.parasite_axes - - diff --git a/doc/api/toolkits/axisartist.rst b/doc/api/toolkits/axisartist.rst index f18246fef128..8b7db9b7a9fe 100644 --- a/doc/api/toolkits/axisartist.rst +++ b/doc/api/toolkits/axisartist.rst @@ -1,15 +1,15 @@ .. module:: mpl_toolkits.axisartist -Matplotlib axisartist Toolkit -============================= +``mpl_toolkits.axisartist`` +=========================== -The *axisartist* namespace includes a derived Axes implementation ( -:class:`mpl_toolkits.axisartist.Axes`). The -biggest difference is that the artists that are responsible for drawing -axis lines, ticks, ticklabels, and axis labels are separated out from the -mpl's Axis class. This change was strongly motivated to support curvilinear grid. +The *axisartist* namespace provides a derived Axes implementation +(:class:`mpl_toolkits.axisartist.Axes`), designed to support curvilinear +grids. The biggest difference is that the artists that are responsible for +drawing axis lines, ticks, ticklabels, and axis labels are separated out from +Matplotlib's Axis class. -You can find a tutorial describing usage of axisartist at the +You can find a tutorial describing usage of axisartist at the :ref:`axisartist_users-guide-index` user guide. .. figure:: ../../gallery/axisartist/images/sphx_glr_demo_curvelinear_grid_001.png @@ -17,6 +17,13 @@ You can find a tutorial describing usage of axisartist at the :align: center :scale: 50 +.. note:: + + This module contains classes and function that were formerly part of the + ``mpl_toolkits.axes_grid`` module that was removed in 3.6. Additional + classes from that older module may also be found in + `mpl_toolkits.axes_grid1`. + .. currentmodule:: mpl_toolkits **The submodules of the axisartist API are:** @@ -37,4 +44,3 @@ You can find a tutorial describing usage of axisartist at the axisartist.grid_finder axisartist.grid_helper_curvelinear axisartist.parasite_axes - diff --git a/doc/api/toolkits/index.rst b/doc/api/toolkits/index.rst deleted file mode 100644 index 59c01ab21a69..000000000000 --- a/doc/api/toolkits/index.rst +++ /dev/null @@ -1,46 +0,0 @@ -.. _toolkits-index: - -.. _toolkits: - -######## -Toolkits -######## - -Toolkits are collections of application-specific functions that extend -Matplotlib. - -.. _toolkit_mplot3d: - -mplot3d -======= - -:mod:`mpl_toolkits.mplot3d` provides some basic 3D -plotting (scatter, surf, line, mesh) tools. Not the fastest or most feature -complete 3D library out there, but it ships with Matplotlib and thus may be a -lighter weight solution for some use cases. Check out the -:doc:`mplot3d tutorial ` for more -information. - -.. figure:: ../../gallery/mplot3d/images/sphx_glr_contourf3d_2_001.png - :target: ../../gallery/mplot3d/contourf3d_2.html - :align: center - :scale: 50 - -.. toctree:: - :maxdepth: 2 - - mplot3d/index.rst - mplot3d/faq.rst - -Links ------ -* mpl3d API: :ref:`toolkit_mplot3d-api` - -.. include:: axes_grid1.rst - :start-line: 1 - -.. include:: axisartist.rst - :start-line: 1 - -.. include:: axes_grid.rst - :start-line: 1 diff --git a/doc/api/toolkits/mplot3d.rst b/doc/api/toolkits/mplot3d.rst index 97d3bf13246f..19005ddd383d 100644 --- a/doc/api/toolkits/mplot3d.rst +++ b/doc/api/toolkits/mplot3d.rst @@ -1,8 +1,34 @@ -.. _toolkit_mplot3d-api: +.. _toolkit_mplot3d-index: +.. currentmodule:: mpl_toolkits.mplot3d + +************************ +``mpl_toolkits.mplot3d`` +************************ + +The mplot3d toolkit adds simple 3D plotting capabilities (scatter, surface, +line, mesh, etc.) to Matplotlib by supplying an Axes object that can create +a 2D projection of a 3D scene. The resulting graph will have the same look +and feel as regular 2D plots. Not the fastest or most feature complete 3D +library out there, but it ships with Matplotlib and thus may be a lighter +weight solution for some use cases. + +See the :doc:`mplot3d tutorial ` for +more information. + +.. image:: /_static/demo_mplot3d.png + :align: center + +The interactive backends also provide the ability to rotate and zoom the 3D +scene. One can rotate the 3D scene by simply clicking-and-dragging the scene. +Panning is done by clicking the middle mouse button, and zooming is done by +right-clicking the scene and dragging the mouse up and down. Unlike 2D plots, +the toolbar pan and zoom buttons are not used. + +.. toctree:: + :maxdepth: 2 -*********** -mplot3d API -*********** + mplot3d/faq.rst + mplot3d/view_angles.rst .. note:: `.pyplot` cannot be used to add content to 3D plots, because its function diff --git a/doc/api/toolkits/mplot3d/faq.rst b/doc/api/toolkits/mplot3d/faq.rst index dfc23b55e069..7e53cabc6e9a 100644 --- a/doc/api/toolkits/mplot3d/faq.rst +++ b/doc/api/toolkits/mplot3d/faq.rst @@ -49,4 +49,3 @@ Work is being done to eliminate this issue. For matplotlib v1.1.0, there is a semi-official manner to modify these parameters. See the note in the :mod:`.mplot3d.axis3d` section of the mplot3d API documentation for more information. - diff --git a/doc/api/toolkits/mplot3d/index.rst b/doc/api/toolkits/mplot3d/index.rst deleted file mode 100644 index 8b153c06903f..000000000000 --- a/doc/api/toolkits/mplot3d/index.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. _toolkit_mplot3d-index: -.. currentmodule:: mpl_toolkits.mplot3d - -******* -mplot3d -******* - -Matplotlib mplot3d toolkit -========================== -The mplot3d toolkit adds simple 3D plotting capabilities to matplotlib by -supplying an axes object that can create a 2D projection of a 3D scene. -The resulting graph will have the same look and feel as regular 2D plots. - -See the :doc:`mplot3d tutorial ` for -more information on how to use this toolkit. - -.. image:: /_static/demo_mplot3d.png - -The interactive backends also provide the ability to rotate and zoom -the 3D scene. One can rotate the 3D scene by simply clicking-and-dragging -the scene. Zooming is done by right-clicking the scene and dragging the -mouse up and down. Note that one does not use the zoom button like one -would use for regular 2D plots. - -.. toctree:: - :maxdepth: 2 - - faq.rst diff --git a/doc/api/toolkits/mplot3d/view_angles.rst b/doc/api/toolkits/mplot3d/view_angles.rst new file mode 100644 index 000000000000..10d4fac39e8c --- /dev/null +++ b/doc/api/toolkits/mplot3d/view_angles.rst @@ -0,0 +1,40 @@ +.. _toolkit_mplot3d-view-angles: + +******************* +mplot3d View Angles +******************* + +How to define the view angle +============================ + +The position of the viewport "camera" in a 3D plot is defined by three angles: +*elevation*, *azimuth*, and *roll*. From the resulting position, it always +points towards the center of the plot box volume. The angle direction is a +common convention, and is shared with +`PyVista `_ and +`MATLAB `_ +(though MATLAB lacks a roll angle). Note that a positive roll angle rotates the +viewing plane clockwise, so the 3d axes will appear to rotate +counter-clockwise. + +.. image:: /_static/mplot3d_view_angles.png + :align: center + :scale: 50 + +Rotating the plot using the mouse will control only the azimuth and elevation, +but all three angles can be set programmatically:: + + import matplotlib.pyplot as plt + ax = plt.figure().add_subplot(projection='3d') + ax.view_init(elev=30, azim=45, roll=15) + + +Primary view planes +=================== + +To look directly at the primary view planes, the required elevation, azimuth, +and roll angles are shown in the diagram of an "unfolded" plot below. These are +further documented in the `.mplot3d.axes3d.Axes3D.view_init` API. + +.. plot:: gallery/mplot3d/view_planes_3d.py + :align: center diff --git a/doc/api/transformations.rst b/doc/api/transformations.rst index 58c29598704c..186db9aea728 100644 --- a/doc/api/transformations.rst +++ b/doc/api/transformations.rst @@ -2,7 +2,7 @@ ``matplotlib.transforms`` ************************* -.. inheritance-diagram:: matplotlib.transforms matplotlib.path +.. inheritance-diagram:: matplotlib.transforms :parts: 1 .. automodule:: matplotlib.transforms @@ -15,4 +15,3 @@ interval_contains, interval_contains_open :show-inheritance: :special-members: - diff --git a/doc/api/tri_api.rst b/doc/api/tri_api.rst index 9205e34ff93b..0b4e046eec08 100644 --- a/doc/api/tri_api.rst +++ b/doc/api/tri_api.rst @@ -2,7 +2,9 @@ ``matplotlib.tri`` ****************** -.. automodule:: matplotlib.tri +Unstructured triangular grid functions. + +.. py:module:: matplotlib.tri .. autoclass:: matplotlib.tri.Triangulation :members: @@ -17,7 +19,7 @@ :show-inheritance: .. autoclass:: matplotlib.tri.TriInterpolator - + .. autoclass:: matplotlib.tri.LinearTriInterpolator :members: __call__, gradient :show-inheritance: @@ -30,7 +32,7 @@ .. autoclass:: matplotlib.tri.UniformTriRefiner :show-inheritance: - :members: + :members: .. autoclass:: matplotlib.tri.TriAnalyzer - :members: + :members: diff --git a/doc/api/type1font.rst b/doc/api/type1font.rst index 2cb2a68eb5d5..00ef38f4d447 100644 --- a/doc/api/type1font.rst +++ b/doc/api/type1font.rst @@ -2,7 +2,12 @@ ``matplotlib.type1font`` ************************ -.. automodule:: matplotlib.type1font +.. attention:: + This module is considered internal. + + Its use is deprecated and it will be removed in a future version. + +.. automodule:: matplotlib._type1font :members: :undoc-members: :show-inheritance: diff --git a/doc/citing.rst b/doc/citing.rst deleted file mode 100644 index 7d5840925276..000000000000 --- a/doc/citing.rst +++ /dev/null @@ -1,141 +0,0 @@ -:orphan: - -Citing Matplotlib -================= - -If Matplotlib contributes to a project that leads to a scientific publication, -please acknowledge this fact by citing `J. D. Hunter, "Matplotlib: A 2D -Graphics Environment", Computing in Science & Engineering, vol. 9, no. 3, -pp. 90-95, 2007 `_. - -.. literalinclude:: MCSE.2007.55.bib - :language: bibtex - -.. container:: sphx-glr-download - - :download:`Download BibTeX bibliography file: MCSE.2007.55.bib ` - -DOIs ----- - -The following DOI represents *all* Matplotlib versions. Please select a more -specific DOI from the list below, referring to the version used for your publication. - - .. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.592536.svg - :target: https://doi.org/10.5281/zenodo.592536 - -By version -~~~~~~~~~~ -.. START OF AUTOGENERATED - - -v3.3.4 - .. image:: _static/zenodo_cache/4475376.svg - :target: https://doi.org/10.5281/zenodo.4475376 -v3.3.3 - .. image:: _static/zenodo_cache/4268928.svg - :target: https://doi.org/10.5281/zenodo.4268928 -v3.3.2 - .. image:: _static/zenodo_cache/4030140.svg - :target: https://doi.org/10.5281/zenodo.4030140 -v3.3.1 - .. image:: _static/zenodo_cache/3984190.svg - :target: https://doi.org/10.5281/zenodo.3984190 -v3.3.0 - .. image:: _static/zenodo_cache/3948793.svg - :target: https://doi.org/10.5281/zenodo.3948793 -v3.2.2 - .. image:: _static/zenodo_cache/3898017.svg - :target: https://doi.org/10.5281/zenodo.3898017 -v3.2.1 - .. image:: _static/zenodo_cache/3714460.svg - :target: https://doi.org/10.5281/zenodo.3714460 -v3.2.0 - .. image:: _static/zenodo_cache/3695547.svg - :target: https://doi.org/10.5281/zenodo.3695547 -v3.1.3 - .. image:: _static/zenodo_cache/3633844.svg - :target: https://doi.org/10.5281/zenodo.3633844 -v3.1.2 - .. image:: _static/zenodo_cache/3563226.svg - :target: https://doi.org/10.5281/zenodo.3563226 -v3.1.1 - .. image:: _static/zenodo_cache/3264781.svg - :target: https://doi.org/10.5281/zenodo.3264781 -v3.1.0 - .. image:: _static/zenodo_cache/2893252.svg - :target: https://doi.org/10.5281/zenodo.2893252 -v3.0.3 - .. image:: _static/zenodo_cache/2577644.svg - :target: https://doi.org/10.5281/zenodo.2577644 -v3.0.2 - .. image:: _static/zenodo_cache/1482099.svg - :target: https://doi.org/10.5281/zenodo.1482099 -v3.0.1 - .. image:: _static/zenodo_cache/1482098.svg - :target: https://doi.org/10.5281/zenodo.1482098 -v2.2.5 - .. image:: _static/zenodo_cache/3633833.svg - :target: https://doi.org/10.5281/zenodo.3633833 -v3.0.0 - .. image:: _static/zenodo_cache/1420605.svg - :target: https://doi.org/10.5281/zenodo.1420605 -v2.2.4 - .. image:: _static/zenodo_cache/2669103.svg - :target: https://doi.org/10.5281/zenodo.2669103 -v2.2.3 - .. image:: _static/zenodo_cache/1343133.svg - :target: https://doi.org/10.5281/zenodo.1343133 -v2.2.2 - .. image:: _static/zenodo_cache/1202077.svg - :target: https://doi.org/10.5281/zenodo.1202077 -v2.2.1 - .. image:: _static/zenodo_cache/1202050.svg - :target: https://doi.org/10.5281/zenodo.1202050 -v2.2.0 - .. image:: _static/zenodo_cache/1189358.svg - :target: https://doi.org/10.5281/zenodo.1189358 -v2.1.2 - .. image:: _static/zenodo_cache/1154287.svg - :target: https://doi.org/10.5281/zenodo.1154287 -v2.1.1 - .. image:: _static/zenodo_cache/1098480.svg - :target: https://doi.org/10.5281/zenodo.1098480 -v2.1.0 - .. image:: _static/zenodo_cache/1004650.svg - :target: https://doi.org/10.5281/zenodo.1004650 -v2.0.2 - .. image:: _static/zenodo_cache/573577.svg - :target: https://doi.org/10.5281/zenodo.573577 -v2.0.1 - .. image:: _static/zenodo_cache/570311.svg - :target: https://doi.org/10.5281/zenodo.570311 -v2.0.0 - .. image:: _static/zenodo_cache/248351.svg - :target: https://doi.org/10.5281/zenodo.248351 -v1.5.3 - .. image:: _static/zenodo_cache/61948.svg - :target: https://doi.org/10.5281/zenodo.61948 -v1.5.2 - .. image:: _static/zenodo_cache/56926.svg - :target: https://doi.org/10.5281/zenodo.56926 -v1.5.1 - .. image:: _static/zenodo_cache/44579.svg - :target: https://doi.org/10.5281/zenodo.44579 -v1.5.0 - .. image:: _static/zenodo_cache/32914.svg - :target: https://doi.org/10.5281/zenodo.32914 -v1.4.3 - .. image:: _static/zenodo_cache/15423.svg - :target: https://doi.org/10.5281/zenodo.15423 -v1.4.2 - .. image:: _static/zenodo_cache/12400.svg - :target: https://doi.org/10.5281/zenodo.12400 -v1.4.1 - .. image:: _static/zenodo_cache/12287.svg - :target: https://doi.org/10.5281/zenodo.12287 -v1.4.0 - .. image:: _static/zenodo_cache/11451.svg - :target: https://doi.org/10.5281/zenodo.11451 - -.. END OF AUTOGENERATED diff --git a/doc/conf.py b/doc/conf.py index a0f8891de6ac..5aa58a3aca5a 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 picklable (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. @@ -14,13 +16,27 @@ import shutil import subprocess import sys +from urllib.parse import urlsplit, urlunsplit import warnings import matplotlib -from matplotlib._api import MatplotlibDeprecationWarning -import sphinx from datetime import datetime +import time + +# debug that building expected version +print(f"Building Documentation for Matplotlib: {matplotlib.__version__}") + +# 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 # 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 @@ -36,10 +52,6 @@ # usage in the gallery. warnings.filterwarnings('error', append=True) -# Strip backslashes in function's signature -# To be removed when numpydoc > 0.9.x -strip_signature_backslash = True - # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ @@ -49,7 +61,6 @@ 'sphinx.ext.inheritance_diagram', 'sphinx.ext.intersphinx', 'sphinx.ext.ifconfig', - 'sphinx.ext.viewcode', 'IPython.sphinxext.ipython_console_highlighting', 'IPython.sphinxext.ipython_directive', 'numpydoc', # Needs to be loaded *after* autodoc. @@ -65,24 +76,21 @@ 'sphinxext.skip_deprecated', 'sphinxext.redirect_from', 'sphinx_copybutton', + 'sphinx_design', ] exclude_patterns = [ 'api/prev_api_changes/api_changes_*/*', - # Be sure to update users/whats_new.rst: - 'users/prev_whats_new/whats_new_3.4.0.rst', ] def _check_dependencies(): names = { + **{ext: ext.split(".")[0] for ext in extensions}, + # Explicitly list deps that are not extensions, or whose PyPI package + # name does not match the (toplevel) module name. "colorspacious": 'colorspacious', - "IPython.sphinxext.ipython_console_highlighting": 'ipython', - "matplotlib": 'matplotlib', - "numpydoc": 'numpydoc', - "PIL.Image": 'pillow', - "sphinx_copybutton": 'sphinx_copybutton', - "sphinx_gallery": 'sphinx_gallery', + "mpl_sphinx_theme": 'mpl_sphinx_theme', "sphinxcontrib.inkscapeconverter": 'sphinxcontrib-svg2pdfconverter', } missing = [] @@ -107,6 +115,7 @@ def _check_dependencies(): # gallery_order.py from the sphinxext folder provides the classes that # allow custom ordering of sections and subsections of the gallery import sphinxext.gallery_order as gallery_order + # The following import is only necessary to monkey patch the signature later on from sphinx_gallery import gen_rst @@ -117,7 +126,7 @@ def _check_dependencies(): # we should ignore warnings coming from importing deprecated modules for # autodoc purposes, as this will disappear automatically when they are removed -warnings.filterwarnings('ignore', category=MatplotlibDeprecationWarning, +warnings.filterwarnings('ignore', category=DeprecationWarning, module='importlib', # used by sphinx.autodoc.importer message=r'(\n|.)*module was deprecated.*') @@ -126,12 +135,10 @@ def _check_dependencies(): # make sure to ignore warnings that stem from simply inspecting deprecated # class-level attributes -warnings.filterwarnings('ignore', category=MatplotlibDeprecationWarning, +warnings.filterwarnings('ignore', category=DeprecationWarning, module='sphinx.util.inspect') -# missing-references names matches sphinx>=3 behavior, so we can't be nitpicky -# for older sphinxes. -nitpicky = sphinx.version_info >= (3,) +nitpicky = True # change this to True to update the allowed failures missing_references_write_json = False missing_references_warn_unused_ignores = False @@ -143,44 +150,84 @@ def _check_dependencies(): 'ipykernel': ('https://ipykernel.readthedocs.io/en/latest/', None), 'numpy': ('https://numpy.org/doc/stable/', None), 'pandas': ('https://pandas.pydata.org/pandas-docs/stable/', None), - 'pytest': ('https://pytest.org/en/latest/', None), + 'pytest': ('https://pytest.org/en/stable/', None), 'python': ('https://docs.python.org/3/', None), - 'scipy': ('https://docs.scipy.org/doc/scipy/reference/', None), + 'scipy': ('https://docs.scipy.org/doc/scipy/', None), + 'tornado': ('https://www.tornadoweb.org/en/stable/', None), + 'xarray': ('https://docs.xarray.dev/en/stable/', None), } # Sphinx gallery configuration + +def matplotlib_reduced_latex_scraper(block, block_vars, gallery_conf, + **kwargs): + """ + Reduce srcset when creating a PDF. + + Because sphinx-gallery runs *very* early, we cannot modify this even in the + earliest builder-inited signal. Thus we do it at scraping time. + """ + from sphinx_gallery.scrapers import matplotlib_scraper + + if gallery_conf['builder_name'] == 'latex': + gallery_conf['image_srcset'] = [] + return matplotlib_scraper(block, block_vars, gallery_conf, **kwargs) + + sphinx_gallery_conf = { - 'examples_dirs': ['../examples', '../tutorials'], - 'filename_pattern': '^((?!sgskip).)*$', - 'gallery_dirs': ['gallery', 'tutorials'], - 'doc_module': ('matplotlib', 'mpl_toolkits'), - 'reference_url': { - 'matplotlib': None, - 'numpy': 'https://docs.scipy.org/doc/numpy/', - 'scipy': 'https://docs.scipy.org/doc/scipy/reference/', - }, 'backreferences_dir': Path('api') / Path('_as_gen'), - 'subsection_order': gallery_order.sectionorder, - 'within_subsection_order': gallery_order.subsectionorder, - 'remove_config_comments': True, + # Compression is a significant effort that we skip for local and CI builds. + 'compress_images': ('thumbnails', 'images') if is_release_build else (), + 'doc_module': ('matplotlib', 'mpl_toolkits'), + 'examples_dirs': ['../examples', '../tutorials', '../plot_types'], + 'filename_pattern': '^((?!sgskip).)*$', + 'gallery_dirs': ['gallery', 'tutorials', 'plot_types'], + 'image_scrapers': (matplotlib_reduced_latex_scraper, ), + 'image_srcset': ["2x"], + 'junit': '../test-results/sphinx-gallery/junit.xml' if CIRCLECI else '', + 'matplotlib_animations': True, 'min_reported_time': 1, + 'reference_url': {'matplotlib': None}, + 'remove_config_comments': True, + 'reset_modules': ( + 'matplotlib', + # clear basic_units module to re-register with unit registry on import + lambda gallery_conf, fname: sys.modules.pop('basic_units', None) + ), + 'subsection_order': gallery_order.sectionorder, 'thumbnail_size': (320, 224), - 'compress_images': ('thumbnails', 'images'), - 'matplotlib_animations': True, + 'within_subsection_order': gallery_order.subsectionorder, + 'capture_repr': (), } -plot_gallery = 'True' +mathmpl_fontsize = 11.0 +mathmpl_srcset = ['2x'] + +# Monkey-patching gallery header to include search keywords +gen_rst.EXAMPLE_HEADER = """ +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "{0}" +.. LINE NUMBERS ARE GIVEN BELOW. -# Monkey-patching gallery signature to include search keywords -gen_rst.SPHX_GLR_SIG = """\n .. only:: html - .. rst-class:: sphx-glr-signature + .. meta:: + :keywords: codex + + .. note:: + :class: sphx-glr-download-link-note + + Click :ref:`here ` + to download the full example code{2} - Keywords: matplotlib code example, codex, python plot, pyplot - `Gallery generated by Sphinx-Gallery - `_\n""" +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_{1}: + +""" # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -191,29 +238,24 @@ def _check_dependencies(): # This is the default encoding, but it doesn't hurt to be explicit source_encoding = "utf-8" -# The master toctree document. -master_doc = 'contents' +# The toplevel toctree document (renamed to root_doc in Sphinx 4.0) +root_doc = master_doc = 'users/index' # General substitutions. try: SHA = subprocess.check_output( ['git', 'describe', '--dirty']).decode('utf-8').strip() -# Catch the case where git is not installed locally, and use the versioneer +# Catch the case where git is not installed locally, and use the setuptools_scm # version number instead except (subprocess.CalledProcessError, FileNotFoundError): SHA = matplotlib.__version__ -html_context = { - 'sha': SHA, - # This will disable any analytics in the HTML templates (currently Google - # Analytics.) - 'include_analytics': False, -} - project = 'Matplotlib' -copyright = ('2002 - 2012 John Hunter, Darren Dale, Eric Firing, ' - 'Michael Droettboom and the Matplotlib development ' - f'team; 2012 - {datetime.now().year} 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 @@ -227,7 +269,7 @@ def _check_dependencies(): # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. today_fmt = '%B %d, %Y' @@ -235,15 +277,15 @@ def _check_dependencies(): unused_docs = [] # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' @@ -253,28 +295,99 @@ def _check_dependencies(): # Plot directive configuration # ---------------------------- -plot_formats = [('png', 100), ('pdf', 100)] +# For speedup, decide which plot_formats to build based on build targets: +# html only -> png +# latex only -> pdf +# all other cases, including html + latex -> png, pdf +# For simplicity, we assume that the build targets appear in the command line. +# We're falling back on using all formats in case that assumption fails. +formats = {'html': ('png', 100), 'latex': ('pdf', 100)} +plot_formats = [formats[target] for target in ['html', 'latex'] + if target in sys.argv] or list(formats.values()) + # GitHub extension github_project_url = "https://github.com/matplotlib/matplotlib/" + # Options for HTML output # ----------------------- +def add_html_cache_busting(app, pagename, templatename, context, doctree): + """ + Add cache busting query on CSS and JavaScript assets. + + This adds the Matplotlib version as a query to the link reference in the + HTML, if the path is not absolute (i.e., it comes from the `_static` + directory) and doesn't already have a query. + """ + from sphinx.builders.html import Stylesheet, JavaScript + + css_tag = context['css_tag'] + js_tag = context['js_tag'] + + def css_tag_with_cache_busting(css): + if isinstance(css, Stylesheet) and css.filename is not None: + url = urlsplit(css.filename) + if not url.netloc and not url.query: + url = url._replace(query=SHA) + css = Stylesheet(urlunsplit(url), priority=css.priority, + **css.attributes) + return css_tag(css) + + def js_tag_with_cache_busting(js): + if isinstance(js, JavaScript) and js.filename is not None: + url = urlsplit(js.filename) + if not url.netloc and not url.query: + url = url._replace(query=SHA) + js = JavaScript(urlunsplit(url), priority=js.priority, + **js.attributes) + return js_tag(js) + + context['css_tag'] = css_tag_with_cache_busting + context['js_tag'] = js_tag_with_cache_busting + + # The style sheet to use for HTML and HTML Help pages. A file of that name # must exist either in Sphinx' static/ path, or in one of the custom paths # given in html_static_path. -#html_style = 'matplotlib.css' -html_style = f'mpl.css?{SHA}' +html_css_files = [ + "mpl.css", +] + +html_theme = "mpl_sphinx_theme" # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # The name of an image file (within the static path) to place at the top of # the sidebar. -#html_logo = 'logo.png' +html_logo = "_static/logo2.svg" +html_theme_options = { + "navbar_links": "internal", + # 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, + "show_prev_next": False, + "switcher": { + "json_url": "https://matplotlib.org/devdocs/_static/switcher.json", + "version_match": ( + # The start version to show. This must be in switcher.json. + # We either go to 'stable' or to 'devdocs' + 'stable' if matplotlib.__version_info__.releaselevel == 'final' + else 'devdocs') + }, + "logo": {"link": "index", + "image_light": "images/logo2.svg", + "image_dark": "images/logo_dark.svg"}, + "navbar_end": ["theme-switcher", "version-switcher", "mpl_icon_links"], + "page_sidebar_items": "page-toc.html", +} +include_analytics = is_release_build +if include_analytics: + html_theme_options["google_analytics_id"] = "UA-55954603-1" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -285,6 +398,9 @@ def _check_dependencies(): # default is ``".html"``. html_file_suffix = '.html' +# this makes this the canonical link for all the pages on the site... +html_baseurl = 'https://matplotlib.org/stable/' + # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%b %d, %Y' @@ -293,23 +409,32 @@ def _check_dependencies(): html_index = 'index.html' # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Custom sidebar templates, maps page names to templates. html_sidebars = { - 'index': [ + "index": [ # 'sidebar_announcement.html', - 'sidebar_versions.html', - 'donate_sidebar.html'], - '**': ['localtoc.html', 'pagesource.html'] + "sidebar_versions.html", + "cheatsheet_sidebar.html", + "donate_sidebar.html", + ], + # '**': ['localtoc.html', 'pagesource.html'] } -# If false, no module index is generated. -#html_use_modindex = True -html_domain_indices = ["py-modindex"] +# Copies only relevant code, not the '>>>' prompt +copybutton_prompt_text = r'>>> |\.\.\. ' +copybutton_prompt_is_regexp = True + +# If true, add an index to the HTML documents. +html_use_index = False + +# If true, generate domain-specific indices in addition to the general index. +# For e.g. the Python domain, this is the global module index. +html_domain_index = False # If true, the reST sources are included in the HTML build as _sources/. -#html_copy_source = True +# html_copy_source = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. @@ -330,11 +455,13 @@ 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', + (root_doc, 'Matplotlib.tex', 'Matplotlib', 'John Hunter\\and Darren Dale\\and Eric Firing\\and Michael Droettboom' '\\and and the matplotlib development team', 'manual'), ] @@ -364,7 +491,7 @@ def _check_dependencies(): # Sphinx 2.0 adopts GNU FreeFont by default, but it does not have all # the Unicode codepoints needed for the section about Mathtext # "Writing mathematical expressions" -fontpkg = r""" +latex_elements['fontpkg'] = r""" \IfFontExistsTF{XITS}{ \setmainfont{XITS} }{ @@ -404,12 +531,7 @@ def _check_dependencies(): Extension = .otf, ]} """ -latex_elements['fontpkg'] = fontpkg -# Sphinx <1.8.0 or >=2.0.0 does this by default, but the 1.8.x series -# did not for latex_engine = 'xelatex' (as it used Latin Modern font). -# We need this for code-blocks as FreeMono has wide glyphs. -latex_elements['fvset'] = r'\fvset{fontsize=\small}' # Fix fancyhdr complaining about \headheight being too small latex_elements['passoptionstopackages'] = r""" \PassOptionsToPackage{headheight=14pt}{geometry} @@ -417,6 +539,8 @@ def _check_dependencies(): # Additional stuff for the LaTeX preamble. latex_elements['preamble'] = r""" + % Show Parts and Chapters in Table of Contents + \setcounter{tocdepth}{0} % One line per author on title page \DeclareRobustCommand{\and}% {\end{tabular}\kern-\tabcolsep\\\begin{tabular}[t]{c}}% @@ -464,7 +588,7 @@ def _check_dependencies(): autoclass_content = 'both' texinfo_documents = [ - ("contents", 'matplotlib', 'Matplotlib Documentation', + (root_doc, 'matplotlib', 'Matplotlib Documentation', 'John Hunter@*Darren Dale@*Eric Firing@*Michael Droettboom@*' 'The matplotlib development team', 'Matplotlib', "Python plotting package", 'Programming', @@ -475,8 +599,6 @@ def _check_dependencies(): numpydoc_show_class_members = False -html4_writer = True - inheritance_node_attrs = dict(fontsize=16) graphviz_dot = shutil.which('dot') @@ -484,10 +606,82 @@ def _check_dependencies(): # https://github.com/sphinx-doc/sphinx/issues/3176 # graphviz_output_format = 'svg' +# ----------------------------------------------------------------------------- +# Source code links +# ----------------------------------------------------------------------------- +link_github = True +# You can add build old with link_github = False + +if link_github: + import inspect + from packaging.version import parse + + extensions.append('sphinx.ext.linkcode') + + def linkcode_resolve(domain, info): + """ + Determine the URL corresponding to Python object + """ + if domain != 'py': + return None + + modname = info['module'] + fullname = info['fullname'] + + submod = sys.modules.get(modname) + if submod is None: + return None + + obj = submod + for part in fullname.split('.'): + try: + obj = getattr(obj, part) + except AttributeError: + return None + + if inspect.isfunction(obj): + obj = inspect.unwrap(obj) + try: + fn = inspect.getsourcefile(obj) + except TypeError: + fn = None + if not fn or fn.endswith('__init__.py'): + try: + fn = inspect.getsourcefile(sys.modules[obj.__module__]) + except (TypeError, AttributeError, KeyError): + fn = None + if not fn: + return None + + try: + source, lineno = inspect.getsourcelines(obj) + except (OSError, TypeError): + lineno = None + + 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, '/') + + if not fn.startswith(('matplotlib/', 'mpl_toolkits/')): + return None + + version = parse(matplotlib.__version__) + tag = 'main' 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') + +# ----------------------------------------------------------------------------- +# Sphinx setup +# ----------------------------------------------------------------------------- def setup(app): - if any(st in version for st in ('post', 'alpha', 'beta')): + if any(st in version for st in ('post', 'dev', 'alpha', 'beta')): bld_type = 'dev' else: bld_type = 'rel' app.add_config_value('releaselevel', bld_type, 'env') + app.connect('html-page-context', add_html_cache_busting, priority=1000) diff --git a/doc/contents.rst b/doc/contents.rst deleted file mode 100644 index 407f81e53b9d..000000000000 --- a/doc/contents.rst +++ /dev/null @@ -1,28 +0,0 @@ - - -Overview -======== - -.. only:: html - - :Release: |version| - :Date: |today| - - Download `PDF `_ - - -.. toctree:: - :maxdepth: 2 - - users/index.rst - faq/index.rst - api/index.rst - resources/index.rst - thirdpartypackages/index.rst - devel/index.rst - -.. only:: html - - * :ref:`genindex` - * :ref:`modindex` - * :ref:`search` diff --git a/doc/devel/MEP/MEP11.rst b/doc/devel/MEP/MEP11.rst index 9ddd0109c06b..659b7e101480 100644 --- a/doc/devel/MEP/MEP11.rst +++ b/doc/devel/MEP/MEP11.rst @@ -117,7 +117,7 @@ Implementation For installing from source, and assuming the user has all of the C-level compilers and dependencies, this can be accomplished fairly easily using distribute_ and following the instructions `here -`_. The only anticipated +`_. The only anticipated change to the matplotlib library code will be to import pyparsing_ from the top-level namespace rather than from within matplotlib. Note that distribute_ will also allow us to remove the direct dependency diff --git a/doc/devel/MEP/MEP12.rst b/doc/devel/MEP/MEP12.rst index 87489393b2f5..009f013e0be9 100644 --- a/doc/devel/MEP/MEP12.rst +++ b/doc/devel/MEP/MEP12.rst @@ -106,7 +106,7 @@ sections described above. "Clean-up" should involve: * PEP8_ clean-ups (running `flake8 - `_, or a similar checker, is + `_, or a similar checker, is highly recommended) * Commented-out code should be removed. * Replace uses of `pylab` interface with `.pyplot` (+ `numpy`, @@ -142,8 +142,8 @@ page instead of the gallery examples. references to that example. For example, the API documentation for :file:`axes.py` and :file:`pyplot.py` may use these examples to generate plots. Use your favorite search tool (e.g., grep, ack, `grin -`_, `pss -`_) to search the matplotlib +`_, `pss +`_) to search the matplotlib package. See `2dc9a46 `_ and `aa6b410 diff --git a/doc/devel/MEP/MEP14.rst b/doc/devel/MEP/MEP14.rst index e3e7127abda9..574c733e10bf 100644 --- a/doc/devel/MEP/MEP14.rst +++ b/doc/devel/MEP/MEP14.rst @@ -78,11 +78,11 @@ number of other projects: - `Microsoft DirectWrite`_ - `Apple Core Text`_ -.. _pango: https://www.pango.org/ -.. _harfbuzz: https://www.freedesktop.org/wiki/Software/HarfBuzz/ +.. _pango: https://pango.gnome.org +.. _harfbuzz: https://github.com/harfbuzz/harfbuzz .. _QtTextLayout: https://doc.qt.io/archives/qt-4.8/qtextlayout.html .. _Microsoft DirectWrite: https://docs.microsoft.com/en-ca/windows/win32/directwrite/introducing-directwrite -.. _Apple Core Text: https://developer.apple.com/library/content/documentation/StringsTextFonts/Conceptual/CoreText_Programming/Overview/Overview.html +.. _Apple Core Text: https://developer.apple.com/library/archive/documentation/StringsTextFonts/Conceptual/CoreText_Programming/Overview/Overview.html Of the above options, it should be noted that harfbuzz_ is designed from the start as a cross platform option with minimal dependencies, diff --git a/doc/devel/MEP/MEP15.rst b/doc/devel/MEP/MEP15.rst index dc1802e33b8c..8e2f80707429 100644 --- a/doc/devel/MEP/MEP15.rst +++ b/doc/devel/MEP/MEP15.rst @@ -1,6 +1,6 @@ -========================================================================== - MEP15 - Fix axis autoscaling when limits are specified for one axis only -========================================================================== +========================================================================= + MEP15: Fix axis autoscaling when limits are specified for one axis only +========================================================================= .. contents:: :local: diff --git a/doc/devel/MEP/MEP19.rst b/doc/devel/MEP/MEP19.rst index 3fc276a238ee..fd93ba619aed 100644 --- a/doc/devel/MEP/MEP19.rst +++ b/doc/devel/MEP/MEP19.rst @@ -67,7 +67,7 @@ great!]: **Documentation** -Documentation of master is now built by travis and uploaded to http://matplotlib.org/devdocs/index.html +Documentation of main is now built by travis and uploaded to https://matplotlib.org/devdocs/index.html @NelleV, I believe, generates the docs automatically and posts them on the web to chart MEP10 progress. @@ -122,7 +122,7 @@ This section outlines the requirements that we would like to have. #. Make it easy to test a large but sparse matrix of different versions of matplotlib's dependencies. The matplotlib user survey provides some good data as to where to focus our efforts: - https://docs.google.com/spreadsheet/ccc?key=0AjrPjlTMRTwTdHpQS25pcTZIRWdqX0pNckNSU01sMHc + https://docs.google.com/spreadsheets/d/1jbK0J4cIkyBNncnS-gP7pINSliNy9lI-N4JHwxlNSXE/edit #. Nice to have: A decentralized design so that those with more obscure platforms can publish build results to a central dashboard. diff --git a/doc/devel/MEP/MEP22.rst b/doc/devel/MEP/MEP22.rst index d7e93bb5744d..8f8fe69b41a6 100644 --- a/doc/devel/MEP/MEP22.rst +++ b/doc/devel/MEP/MEP22.rst @@ -13,16 +13,18 @@ Status Branches and Pull requests ========================== -Previous work - * https://github.com/matplotlib/matplotlib/pull/1849 - * https://github.com/matplotlib/matplotlib/pull/2557 - * https://github.com/matplotlib/matplotlib/pull/2465 +Previous work: + +* https://github.com/matplotlib/matplotlib/pull/1849 +* https://github.com/matplotlib/matplotlib/pull/2557 +* https://github.com/matplotlib/matplotlib/pull/2465 Pull Requests: - * Removing the NavigationToolbar classes - https://github.com/matplotlib/matplotlib/pull/2740 **CLOSED** - * Keeping the NavigationToolbar classes https://github.com/matplotlib/matplotlib/pull/2759 **CLOSED** - * Navigation by events: https://github.com/matplotlib/matplotlib/pull/3652 + +* Removing the NavigationToolbar classes + https://github.com/matplotlib/matplotlib/pull/2740 **CLOSED** +* Keeping the NavigationToolbar classes https://github.com/matplotlib/matplotlib/pull/2759 **CLOSED** +* Navigation by events: https://github.com/matplotlib/matplotlib/pull/3652 Abstract ======== @@ -39,7 +41,7 @@ reconfiguration. This approach will make easier to create and share tools among users. In the far future, we can even foresee a kind of Marketplace -for ``Tool``\ s where the most popular can be added into the main +for ``Tool``\s where the most popular can be added into the main distribution. Detailed description @@ -55,18 +57,18 @@ https://github.com/matplotlib/matplotlib/issues/2699 The proposed solution is to take the actions out of the ``Toolbar`` and the shortcuts out of the ``Canvas``. The actions and shortcuts will be in the form -of ``Tool``\ s. +of ``Tool``\s. A new class ``Navigation`` will be the bridge between the events from the ``Canvas`` and ``Toolbar`` and redirect them to the appropriate ``Tool``. At the end the user interaction will be divided into three classes: - * NavigationBase: This class is instantiated for each FigureManager - and connect the all user interactions with the Tools - * ToolbarBase: This existing class is relegated only as a GUI access - to Tools. - * ToolBase: Is the basic definition of Tools. +* NavigationBase: This class is instantiated for each FigureManager + and connect the all user interactions with the Tools +* ToolbarBase: This existing class is relegated only as a GUI access + to Tools. +* ToolBase: Is the basic definition of Tools. Implementation @@ -80,37 +82,44 @@ present in the Toolbar as ``Quit``. The `.ToolBase` has the following class attributes for configuration at definition time - * keymap = None: Key(s) to be used to trigger the tool - * description = '': Small description of the tool - * image = None: Image that is used in the toolbar +* keymap = None: Key(s) to be used to trigger the tool +* description = '': Small description of the tool +* image = None: Image that is used in the toolbar The following instance attributes are set at instantiation: - * name - * navigation - -**Methods** - * trigger(self, event): This is the main method of the Tool, it is called when the Tool is triggered by: - * Toolbar button click - * keypress associated with the Tool Keymap - * Call to navigation.trigger_tool(name) - * set_figure(self, figure): Set the figure and navigation attributes - * ``destroy(self, *args)``: Destroy the ``Tool`` graphical interface (if - exists) - -**Available Tools** - * ToolQuit - * ToolEnableAllNavigation - * ToolEnableNavigation - * ToolToggleGrid - * ToolToggleFullScreen - * ToolToggleYScale - * ToolToggleXScale - * ToolHome - * ToolBack - * ToolForward - * SaveFigureBase - * ConfigureSubplotsBase +* name +* navigation + +Methods +~~~~~~~ + +* ``trigger(self, event)``: This is the main method of the Tool, it is called + when the Tool is triggered by: + + * Toolbar button click + * keypress associated with the Tool Keymap + * Call to navigation.trigger_tool(name) + +* ``set_figure(self, figure)``: Set the figure and navigation attributes +* ``destroy(self, *args)``: Destroy the ``Tool`` graphical interface (if + exists) + +Available Tools +~~~~~~~~~~~~~~~ + +* ToolQuit +* ToolEnableAllNavigation +* ToolEnableNavigation +* ToolToggleGrid +* ToolToggleFullScreen +* ToolToggleYScale +* ToolToggleXScale +* ToolHome +* ToolBack +* ToolForward +* SaveFigureBase +* ConfigureSubplotsBase ToolToggleBase(ToolBase) ------------------------ @@ -118,58 +127,63 @@ ToolToggleBase(ToolBase) The `.ToolToggleBase` has the following class attributes for configuration at definition time - * radio_group = None: Attribute to group 'radio' like tools (mutually - exclusive) - * cursor = None: Cursor to use when the tool is active +* radio_group = None: Attribute to group 'radio' like tools (mutually + exclusive) +* cursor = None: Cursor to use when the tool is active The **Toggleable** Tools, can capture keypress, mouse moves, and mouse button press -It defines the following methods - * enable(self, event): Called by `.ToolToggleBase.trigger` method - * disable(self, event): Called when the tool is untoggled - * toggled : **Property** True or False +Methods +~~~~~~~ -**Available Tools** - * ToolZoom - * ToolPan +* ``enable(self, event)``: Called by `.ToolToggleBase.trigger` method +* ``disable(self, event)``: Called when the tool is untoggled +* ``toggled``: **Property** True or False -NavigationBase --------------- +Available Tools +~~~~~~~~~~~~~~~ -Defines the following attributes - * canvas: - * keypresslock: Lock to know if the ``canvas`` ``key_press_event`` is - available and process it - * messagelock: Lock to know if the message is available to write - -Public methods for **User use**: - * nav_connect(self, s, func): Connect to to navigation for events - * nav_disconnect(self, cid): Disconnect from navigation event - * message_event(self, message, sender=None): Emit a - tool_message_event event - * active_toggle(self): **Property** The currently toggled tools or - None - * get_tool_keymap(self, name): Return a list of keys that are - associated with the tool - * set_tool_keymap(self, name, ``*keys``): Set the keys for the given tool - * remove_tool(self, name): Removes tool from the navigation control. - * add_tools(self, tools): Add multiple tools to ``Navigation`` - * add_tool(self, name, tool, group=None, position=None): Add a tool - to the ``Navigation`` - * tool_trigger_event(self, name, sender=None, canvasevent=None, - data=None): Trigger a tool and fire the event - - * tools(self) **Property**: Return a dict with available tools with - corresponding keymaps, descriptions and objects - * get_tool(self, name): Return the tool object +* ToolZoom +* ToolPan +NavigationBase +-------------- +Defines the following attributes: + +* canvas: +* keypresslock: Lock to know if the ``canvas`` ``key_press_event`` is + available and process it +* messagelock: Lock to know if the message is available to write + +Methods (intended for the end user) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* ``nav_connect(self, s, func)``: Connect to navigation for events +* ``nav_disconnect(self, cid)``: Disconnect from navigation event +* ``message_event(self, message, sender=None)``: Emit a + tool_message_event event +* ``active_toggle(self)``: **Property** The currently toggled tools or + None +* ``get_tool_keymap(self, name)``: Return a list of keys that are + associated with the tool +* ``set_tool_keymap(self, name, ``*keys``)``: Set the keys for the given tool +* ``remove_tool(self, name)``: Removes tool from the navigation control. +* ``add_tools(self, tools)``: Add multiple tools to ``Navigation`` +* ``add_tool(self, name, tool, group=None, position=None)``: Add a tool + to the ``Navigation`` +* ``tool_trigger_event(self, name, sender=None, canvasevent=None, + data=None)``: Trigger a tool and fire the event +* ``tools``: **Property** A dict with available tools with + corresponding keymaps, descriptions and objects +* ``get_tool(self, name)``: Return the tool object ToolbarBase ----------- -Methods for **Backend implementation** +Methods (for backend implementation) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * ``add_toolitem(self, name, group, position, image, description, toggle)``: Add a toolitem to the toolbar. This method is a callback from diff --git a/doc/devel/MEP/MEP24.rst b/doc/devel/MEP/MEP24.rst index 53f0609f3e9b..b0620ce3dc8f 100644 --- a/doc/devel/MEP/MEP24.rst +++ b/doc/devel/MEP/MEP24.rst @@ -1,5 +1,5 @@ ======================================= - MEP24: negative radius in polar plots + MEP24: Negative radius in polar plots ======================================= .. contents:: diff --git a/doc/devel/MEP/MEP26.rst b/doc/devel/MEP/MEP26.rst index 929393a683d2..9d3af8f8c703 100644 --- a/doc/devel/MEP/MEP26.rst +++ b/doc/devel/MEP/MEP26.rst @@ -34,7 +34,7 @@ Detailed description ==================== Currently, the look and appearance of existing artist objects (figure, -axes, Line2D etc...) can only be updated via ``set_`` and ``get_`` methods +axes, Line2D, etc.) can only be updated via ``set_`` and ``get_`` methods on the artist object, which is quite laborious, especially if no reference to the artist(s) has been stored. The new style sheets introduced in 1.4 allow styling before a plot is created, but do not @@ -51,7 +51,7 @@ of primitives. The new methodology would require development of a number of steps: - A new stylesheet syntax (likely based on CSS) to allow selection of - artists by type, class, id etc... + artists by type, class, id, etc. - A mechanism by which to parse a stylesheet into a tree - A mechanism by which to translate the parse-tree into something which can be used to update the properties of relevant diff --git a/doc/devel/MEP/MEP27.rst b/doc/devel/MEP/MEP27.rst index 13ed37cb73cb..81eca8f9c53d 100644 --- a/doc/devel/MEP/MEP27.rst +++ b/doc/devel/MEP/MEP27.rst @@ -1,5 +1,5 @@ ====================================== - MEP27: decouple pyplot from backends + MEP27: Decouple pyplot from backends ====================================== .. contents:: @@ -13,9 +13,11 @@ Status Branches and Pull requests ========================== Main PR (including GTK3): + + https://github.com/matplotlib/matplotlib/pull/4143 Backend specific branch diffs: + + https://github.com/OceanWolf/matplotlib/compare/backend-refactor...OceanWolf:backend-refactor-tkagg + https://github.com/OceanWolf/matplotlib/compare/backend-refactor...OceanWolf:backend-refactor-qt + https://github.com/OceanWolf/matplotlib/compare/backend-refactor...backend-refactor-wx @@ -49,15 +51,14 @@ Two main places for generic code appear in the classes derived from 1. ``FigureManagerBase`` has **three** jobs at the moment: - 1. The documentation describes it as a *``Helper class for pyplot - mode, wraps everything up into a neat bundle''* + 1. The documentation describes it as a *Helper class for pyplot + mode, wraps everything up into a neat bundle* 2. But it doesn't just wrap the canvas and toolbar, it also does all of the windowing tasks itself. The conflation of these two - tasks gets seen the best in the following line: ```python - self.set_window_title("Figure %d" % num) ``` This combines + tasks gets seen the best in the following line: + ``self.set_window_title("Figure %d" % num)`` This combines backend specific code ``self.set_window_title(title)`` with matplotlib generic code ``title = "Figure %d" % num``. - 3. Currently the backend specific subclass of ``FigureManager`` decides when to end the mainloop. This also seems very wrong as the figure should have no control over the other figures. @@ -95,7 +96,7 @@ The description of this MEP gives us most of the solution: 1. This allows us to break up the conversion of backends into separate PRs as we can keep the existing ``FigureManagerBase`` class and its dependencies intact. - 2. and this also anticipates MEP22 where the new + 2. And this also anticipates MEP22 where the new ``NavigationBase`` has morphed into a backend independent ``ToolManager``. diff --git a/doc/devel/MEP/MEP28.rst b/doc/devel/MEP/MEP28.rst index 631be1e2b548..07b83c17800e 100644 --- a/doc/devel/MEP/MEP28.rst +++ b/doc/devel/MEP/MEP28.rst @@ -46,7 +46,7 @@ Detailed description Currently, the ``Axes.boxplot`` method accepts parameters that allow the users to specify medians and confidence intervals for each box that -will be drawn in the plot. These were provided so that avdanced users +will be drawn in the plot. These were provided so that advanced users could provide statistics computed in a different fashion that the simple method provided by matplotlib. However, handling this input requires complex logic to make sure that the forms of the data structure match what diff --git a/doc/devel/MEP/MEP29.rst b/doc/devel/MEP/MEP29.rst index ae7eae9fe43e..d937889d55de 100644 --- a/doc/devel/MEP/MEP29.rst +++ b/doc/devel/MEP/MEP29.rst @@ -34,7 +34,7 @@ one has to look at the gallery where one such example is provided: This example takes a list of strings as well as a list of colors which makes it cumbersome to use. An alternative would be to use a restricted set of pango_-like markup and to interpret this markup. -.. _pango: https://developer.gnome.org/pygtk/stable/pango-markup-language.html +.. _pango: https://docs.gtk.org/Pango/pango_markup.html#pango-markup Some markup examples:: @@ -54,7 +54,7 @@ Improvements to use the html.parser from the standard library. * Computation of text fragment positions could benefit from the OffsetFrom - class. See for example item 5 in `Using Complex Coordinates with Annotations `_ + class. See for example item 5 in `Using Complex Coordinates with Annotations `_ Problems -------- diff --git a/doc/devel/MEP/template.rst b/doc/devel/MEP/template.rst index 81191fc44eeb..00bdbc87a95e 100644 --- a/doc/devel/MEP/template.rst +++ b/doc/devel/MEP/template.rst @@ -24,7 +24,7 @@ MEPs go through a number of phases in their lifetime: - **Progress**: Consensus was reached and implementation work has begun. -- **Completed**: The implementation has been merged into master. +- **Completed**: The implementation has been merged into main. - **Superseded**: This MEP has been abandoned in favor of another approach. diff --git a/doc/devel/README.txt b/doc/devel/README.txt index 3fc074035aff..d7636cd4c37c 100644 --- a/doc/devel/README.txt +++ b/doc/devel/README.txt @@ -2,8 +2,8 @@ All documentation in the gitwash directory are automatically generated by runnin script in the project's root directory using the following parameters: python gitwash_dumper.py doc/devel Matplotlib --repo-name=matplotlib --github-user=matplotlib \ - --project-url=http://matplotlib.org \ + --project-url=https://matplotlib.org \ --project-ml-url=https://mail.python.org/mailman/listinfo/matplotlib-devel The script is hosted at https://raw.githubusercontent.com/matthew-brett/gitwash/master/gitwash_dumper.py. -For more information please visit https://github.com/matthew-brett/gitwash \ No newline at end of file +For more information please visit https://github.com/matthew-brett/gitwash diff --git a/doc/devel/add_new_projection.rst b/doc/devel/add_new_projection.rst deleted file mode 100644 index 4eb2f80be490..000000000000 --- a/doc/devel/add_new_projection.rst +++ /dev/null @@ -1,129 +0,0 @@ -.. _adding-new-scales: - -========================================================= -Developer's guide for creating scales and transformations -========================================================= - -.. ::author Michael Droettboom - -Matplotlib supports the addition of custom procedures that transform -the data before it is displayed. - -There is an important distinction between two kinds of -transformations. Separable transformations, working on a single -dimension, are called "scales", and non-separable transformations, -that handle data in two or more dimensions at a time, are called -"projections". - -From the user's perspective, the scale of a plot can be set with -`.Axes.set_xscale` and `.Axes.set_yscale`. Projections can be chosen using the -*projection* keyword argument of functions that create Axes, such as -`.pyplot.subplot` or `.pyplot.axes`, e.g. :: - - plt.subplot(projection="custom") - -This document is intended for developers and advanced users who need -to create new scales and projections for Matplotlib. The necessary -code for scales and projections can be included anywhere: directly -within a plot script, in third-party code, or in the Matplotlib source -tree itself. - -.. _creating-new-scale: - -Creating a new scale -==================== - -Adding a new scale consists of defining a subclass of -:class:`matplotlib.scale.ScaleBase`, that includes the following -elements: - -- A transformation from data coordinates into display coordinates. - -- An inverse of that transformation. This is used, for example, to - convert mouse positions from screen space back into data space. - -- A function to limit the range of the axis to acceptable values - (``limit_range_for_scale()``). A log scale, for instance, would - prevent the range from including values less than or equal to zero. - -- Locators (major and minor) that determine where to place ticks in - the plot, and optionally, how to adjust the limits of the plot to - some "good" values. Unlike ``limit_range_for_scale()``, which is - always enforced, the range setting here is only used when - automatically setting the range of the plot. - -- Formatters (major and minor) that specify how the tick labels - should be drawn. - -Once the class is defined, it must be registered with Matplotlib so -that the user can select it. - -A full-fledged and heavily annotated example is in -:doc:`/gallery/scales/custom_scale`. There are also some classes -in :mod:`matplotlib.scale` that may be used as starting points. - - -.. _creating-new-projection: - -Creating a new projection -========================= - -Adding a new projection consists of defining a projection axes which -subclasses :class:`matplotlib.axes.Axes` and includes the following -elements: - -- A transformation from data coordinates into display coordinates. - -- An inverse of that transformation. This is used, for example, to - convert mouse positions from screen space back into data space. - -- Transformations for the gridlines, ticks and ticklabels. Custom - projections will often need to place these elements in special - locations, and Matplotlib has a facility to help with doing so. - -- Setting up default values (overriding :meth:`~matplotlib.axes.Axes.cla`), - since the defaults for a rectilinear axes may not be appropriate. - -- Defining the shape of the axes, for example, an elliptical axes, that will be - used to draw the background of the plot and for clipping any data elements. - -- Defining custom locators and formatters for the projection. For - example, in a geographic projection, it may be more convenient to - display the grid in degrees, even if the data is in radians. - -- Set up interactive panning and zooming. This is left as an - "advanced" feature left to the reader, but there is an example of - this for polar plots in :mod:`matplotlib.projections.polar`. - -- Any additional methods for additional convenience or features. - -Once the projection axes is defined, it can be used in one of two ways: - -- By defining the class attribute ``name``, the projection axes can be - registered with :func:`matplotlib.projections.register_projection` - and subsequently simply invoked by name:: - - plt.axes(projection='my_proj_name') - -- For more complex, parameterisable projections, a generic "projection" object - may be defined which includes the method ``_as_mpl_axes``. ``_as_mpl_axes`` - should take no arguments and return the projection's axes subclass and a - dictionary of additional arguments to pass to the subclass' ``__init__`` - method. Subsequently a parameterised projection can be initialised with:: - - plt.axes(projection=MyProjection(param1=param1_value)) - - where MyProjection is an object which implements a ``_as_mpl_axes`` method. - - -A full-fledged and heavily annotated example is in -:doc:`/gallery/misc/custom_projection`. The polar plot -functionality in :mod:`matplotlib.projections.polar` may also be of -interest. - -API documentation -================= - -* :mod:`matplotlib.scale` -* :mod:`matplotlib.projections` -* :mod:`matplotlib.projections.polar` diff --git a/doc/devel/coding_guide.rst b/doc/devel/coding_guide.rst index 053527477cf9..33062aa445f5 100644 --- a/doc/devel/coding_guide.rst +++ b/doc/devel/coding_guide.rst @@ -16,15 +16,15 @@ Pull request guidelines Pull requests (PRs) are the mechanism for contributing to Matplotlibs code and documentation. -Summary for PR authors -====================== +Summary for pull request authors +================================ .. note:: * We value contributions from people with all levels of experience. In particular if this is your first PR not everything has to be perfect. We'll guide you through the PR process. - * Nevertheless, try to follow the guidelines below as well as you can to + * Nevertheless, please try to follow the guidelines below as well as you can to help make the PR process quick and smooth. * Be patient with reviewers. We try our best to respond quickly, but we have limited bandwidth. If there is no feedback within a couple of days, @@ -34,26 +34,28 @@ When making a PR, pay attention to: .. rst-class:: checklist -* :ref:`Target the master branch `. +* :ref:`Target the main branch `. * Adhere to the :ref:`coding_guidelines`. * Update the :ref:`documentation ` if necessary. * Aim at making the PR as "ready-to-go" as you can. This helps to speed up the review process. * It is ok to open incomplete or work-in-progress PRs if you need help or feedback from the developers. You may mark these as - `draft pull requests `_ + `draft pull requests `_ 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 + + .. code-block:: bash - git commit --amend --no-edit - git push [your-remote-repo] [your-branch] --force-with-lease + 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. -Summary for PR reviewers -======================== +Summary for pull request reviewers +================================== .. note:: @@ -76,16 +78,16 @@ Organizational topics: .. rst-class:: checklist * Make sure all :ref:`automated tests ` pass. -* The PR should :ref:`target the master branch `. +* The PR should :ref:`target the main branch `. * Tag with descriptive :ref:`labels `. * Set the :ref:`milestone `. * Keep an eye on the :ref:`number of commits `. -* Approve if all of the above topics handled. +* Approve if all of the above topics are handled. * :ref:`Merge ` if a sufficient number of approvals is reached. .. _pr-guidelines-details: -Detailed Guidelines +Detailed guidelines =================== .. _pr-documentation: @@ -121,6 +123,8 @@ Labels * If you have the rights to set labels, tag the PR with descriptive labels. See the `list of labels `__. +* If the PR makes changes to the wheel building Action, add the + "Run cibuildwheel" label to enable testing wheels. .. _pr-milestones: @@ -130,20 +134,20 @@ Milestones * Set the milestone according to these rules: * *New features and API changes* are milestoned for the next minor release - ``v3.X.0``. + ``v3.N.0``. - * *Bugfixes and docstring changes* are milestoned for the next patch - release ``v3.X.Y`` + * *Bugfixes, tests for released code, and docstring changes* are milestoned + for the next patch release ``v3.N.M``. * *Documentation changes* (all .rst files and examples) are milestoned - ``v3.X-doc`` + ``v3.N-doc``. If multiple rules apply, choose the first matching from the above list. Setting a milestone does not imply or guarantee that a PR will be merged for that release, but if it were to be merged what release it would be in. - All of these PRs should target the master branch. The milestone tag triggers + All of these PRs should target the main branch. The milestone tag triggers an :ref:`automatic backport ` for milestones which have a corresponding branch. @@ -159,7 +163,7 @@ Merging core developers (those with commit rights) should review all pull requests. If you are the first to review a PR and approve of the changes use the GitHub `'approve review' - `__ + `__ tool to mark it as such. If you are a subsequent reviewer please approve the review and if you think no more review is needed, merge the PR. @@ -219,6 +223,18 @@ will run on all supported platforms and versions of Python. .. _tox: https://tox.readthedocs.io/ +* If you know your changes do not need to be tested (this is very rare!), all + CIs can be skipped for a given commit by including ``[ci skip]`` or + ``[skip ci]`` in the commit message. If you know only a subset of CIs need + to be run (e.g., if you are changing some block of plain reStructuredText and + want only CircleCI to run to render the result), individual CIs can be + skipped on individual commits as well by using the following substrings + in commit messages: + + - GitHub Actions: ``[skip actions]`` + - AppVeyor: ``[skip appveyor]`` (must be in the first line of the commit) + - Azure Pipelines: ``[skip azp]`` + - CircleCI: ``[skip circle]`` .. _pr-squashing: @@ -246,20 +262,20 @@ Number of commits and squashing .. _branches_and_backports: -Branches and Backports +Branches and backports ====================== Current branches ---------------- The current active branches are -*master* +*main* The current development version. Future minor releases (*v3.N.0*) will be - branched from this. Supports Python 3.7+. + branched from this. *v3.N.x* Maintenance branch for Matplotlib 3.N. Future patch releases will be - branched from this. Supports Python 3.6+. + branched from this. *v3.N.M-doc* Documentation for the current release. On a patch release, this will be @@ -271,7 +287,7 @@ The current active branches are Branch selection for pull requests ---------------------------------- -Generally, all pull requests should target the master branch. +Generally, all pull requests should target the main branch. Other branches are fed through :ref:`automatic ` or :ref:`manual `. Directly @@ -330,7 +346,7 @@ When doing backports please copy the form used by meeseekdev, conflicts make note of them and how you resolved them in the commit message. -We do a backport from master to v2.2.x assuming: +We do a backport from main to v2.2.x assuming: * ``matplotlib`` is a read-only remote branch of the matplotlib/matplotlib repo @@ -340,22 +356,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``: - 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 +.. 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 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``! +sure to push or PR against the ``v2.2.x`` upstream branch, not ``main``! diff --git a/doc/devel/color_changes.rst b/doc/devel/color_changes.rst index d36a873c7225..f7646ded7c14 100644 --- a/doc/devel/color_changes.rst +++ b/doc/devel/color_changes.rst @@ -1,10 +1,11 @@ .. _color_changes: ********************* -Default Color changes +Default color changes ********************* -As discussed at length elsewhere [insert links], ``jet`` is an +As discussed at length `elsewhere `__ , +``jet`` is an empirically bad colormap and should not be the default colormap. Due to the position that changing the appearance of the plot breaks backward compatibility, this change has been put off for far longer @@ -14,7 +15,7 @@ plots and to adopt a different colormap for filled plots (``imshow``, ``pcolor``, ``contourf``, etc) and for scatter like plots. -Default Heat Map Colormap +Default heat map colormap ------------------------- The choice of a new colormap is fertile ground to bike-shedding ("No, @@ -57,10 +58,10 @@ Nathaniel Smith) to evaluate proposed colormaps. Example script ++++++++++++++ -Proposed Colormaps +Proposed colormaps ++++++++++++++++++ -Default Scatter Colormap +Default scatter colormap ------------------------ For heat-map like applications it can be desirable to cover as much of @@ -99,10 +100,10 @@ Example script qd = np.random.rand(np.prod(X.shape)) Q.set_array(qd) -Proposed Colormaps +Proposed colormaps ++++++++++++++++++ -Color Cycle / Qualitative colormap +Color cycle / qualitative colormap ----------------------------------- When plotting lines it is frequently desirable to plot multiple lines @@ -131,5 +132,5 @@ Example script ax2.set_xlim(0, 2*np.pi) -Proposed Color cycle +Proposed color cycle ++++++++++++++++++++ diff --git a/doc/devel/contributing.rst b/doc/devel/contributing.rst index 236782ae95c8..362cf09c5f7e 100644 --- a/doc/devel/contributing.rst +++ b/doc/devel/contributing.rst @@ -7,38 +7,90 @@ Contributing This project is a community effort, and everyone is welcome to contribute. Everyone within the community is expected to abide by our -`code of conduct `_. +`code of conduct `_. The project is hosted on https://github.com/matplotlib/matplotlib -Contributor Incubator -===================== - -If you are interested in becoming a regular contributor to Matplotlib, but -don't know where to start or feel insecure about it, you can join our non-public -communication channel for new contributors. To do so, please go to `gitter -`_ and ask to be added to '#incubator'. -This is a private gitter room moderated by core Matplotlib developers where you can -get guidance and support for your first few PRs. This is a place you can ask questions -about anything: how to use git, github, how our PR review process works, technical questions -about the code, what makes for good documentation or a blog post, how to get involved involved -in community work, or get "pre-review" on your PR. - +Get Connected +============= + +Do I really have something to contribute to Matplotlib? +------------------------------------------------------- + +100% yes. There are so many ways to contribute to our community. + +When in doubt, we recommend going together! Get connected with our community of +active contributors, many of whom felt just like you when they started out and +are happy to welcome you and support you as you get to know how we work, and +where things are. Take a look at the next sections to learn more. + +Contributor incubator +--------------------- + +The incubator is our non-public communication channel for new contributors. It +is a private gitter room moderated by core Matplotlib developers where you can +get guidance and support for your first few PRs. It's a place you can ask +questions about anything: how to use git, github, how our PR review process +works, technical questions about the code, what makes for good documentation +or a blog post, how to get involved in community work, or get +"pre-review" on your PR. + +To join, please go to our public `gitter +`_ community channel, and ask to be +added to '#incubator'. One of our core developers will see your message and will +add you. + +New Contributors meeting +------------------------ + +Once a month, we host a meeting to discuss topics that interest new +contributors. Anyone can attend, present, or sit in and listen to the call. +Among our attendees are fellow new contributors, as well as maintainers, and +veteran contributors, who are keen to support onboarding of new folks and +share their experience. You can find our community calendar link at the +`Scientific Python website `_, and +you can browse previous meeting notes on `github +`_. +We recommend joining the meeting to clarify any doubts, or lingering +questions you might have, and to get to know a few of the people behind the +GitHub handles 😉. You can reach out to @noatamir on `gitter +`_ for any clarifications or +suggestions. We <3 feedback! .. _new_contributors: -Issues for New Contributors +Issues for new contributors --------------------------- While any contributions are welcome, we have marked some issues as -particularly suited for new contributors by the label -`good first issue `_ -These are well documented issues, that do not require a deep understanding of -the internals of Matplotlib. The issues may additionally be tagged with a -difficulty. ``Difficulty: Easy`` is suited for people with little Python experience. -``Difficulty: Medium`` and ``Difficulty: Hard`` are not trivial to solve and -require more thought and programming experience. +particularly suited for new contributors by the label `good first issue +`_. These +are well documented issues, that do not require a deep understanding of the +internals of Matplotlib. The issues may additionally be tagged with a +difficulty. ``Difficulty: Easy`` is suited for people with little Python +experience. ``Difficulty: Medium`` and ``Difficulty: Hard`` require more +programming experience. This could be for a variety of reasons, among them, +though not necessarily all at the same time: + +- The issue is in areas of the code base which have more interdependencies, + or legacy code. +- It has less clearly defined tasks, which require some independent + exploration, making suggestions, or follow-up discussions to clarify a good + path to resolve the issue. +- It involves Python features such as decorators and context managers, which + have subtleties due to our implementation decisions. + +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: @@ -73,11 +125,13 @@ 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. +.. _request-a-new-feature: + Requesting a new feature ======================== @@ -117,13 +171,13 @@ A brief overview is: git clone https://github.com//matplotlib.git 4. Enter the directory and install the local version of Matplotlib. - See ref`` for instructions + See :ref:`installing_for_devs` for instructions 5. Create a branch to hold your changes:: - git checkout -b my-feature origin/master + git checkout -b my-feature origin/main - and start making changes. Never work in the ``master`` branch! + and start making changes. Never work in the ``main`` branch! 6. Work on this copy, on your computer, using Git to do the version control. When you're done editing e.g., ``lib/matplotlib/collections.py``, do:: @@ -140,7 +194,7 @@ Finally, go to the web page of your fork of the Matplotlib repo, and click .. seealso:: - * `Git documentation `_ + * `Git documentation `_ * `Git-Contributing to a Project `_ * `Introduction to GitHub `_ * :ref:`development-workflow` for best practices for Matplotlib @@ -160,19 +214,31 @@ rules before submitting a pull request: appropriate. Use the `numpy docstring standard `_. -* Formatting should follow the recommendations of `PEP8 - `__. You should consider - installing/enabling automatic PEP8 checking in your editor. Part of the test - suite is checking PEP8 compliance, things go smoother if the code is mostly - PEP8 compliant to begin with. +* Formatting should follow the recommendations of PEP8_, as enforced by + flake8_. You can check flake8 compliance from the command line with :: + + python -m pip install flake8 + flake8 /path/to/module.py + + or your editor may provide integration with it. Note that Matplotlib + intentionally does not use the black_ auto-formatter (1__), in particular due + to its unability to understand the semantics of mathematical expressions + (2__, 3__). + + .. _PEP8: https://www.python.org/dev/peps/pep-0008/ + .. _flake8: https://flake8.pycqa.org/ + .. _black: https://black.readthedocs.io/ + .. __: https://github.com/matplotlib/matplotlib/issues/18796 + .. __: https://github.com/psf/black/issues/148 + .. __: https://github.com/psf/black/issues/1984 * Each high-level plotting function should have a simple example in the ``Example`` section of the docstring. This should be as simple as possible to demonstrate the method. More complex examples should go in the ``examples`` tree. -* Changes (both new features and bugfixes) should be tested. See :ref:`testing` - for more details. +* Changes (both new features and bugfixes) should have good test coverage. See + :ref:`testing` for more details. * Import the following modules using the standard scipy conventions:: @@ -201,19 +267,6 @@ rules before submitting a pull request: * See below for additional points about :ref:`keyword-argument-processing`, if applicable for your pull request. -In addition, you can check for common programming errors with the following -tools: - -* Code with a good unittest coverage (at least 70%, better 100%), check with:: - - python -m pip install coverage - python -m pytest --cov=matplotlib --showlocals -v - -* No pyflakes warnings, check with:: - - python -m pip install pyflakes - pyflakes path/to/module.py - .. note:: The current state of the Matplotlib code base is not compliant with all @@ -264,7 +317,7 @@ Other ways to contribute It also helps us if you spread the word: reference the project from your blog and articles or link to it from your website! If Matplotlib contributes to a project that leads to a scientific publication, please follow the -:doc:`/citing` guidelines. +:doc:`/users/project/citing` guidelines. .. _coding_guidelines: @@ -274,30 +327,66 @@ Coding guidelines API changes ----------- -Changes to the public API must follow a standard deprecation procedure to -prevent unexpected breaking of code that uses Matplotlib. - -- Deprecations must be announced via a new file in - a new file in :file:`doc/api/next_api_changes/deprecations/` with - naming convention ``99999-ABC.rst`` where ``99999`` is the pull request - number and ``ABC`` are the contributor's initials. -- Deprecations are targeted at the next point-release (i.e. 3.x.0). -- The deprecated API should, to the maximum extent possible, remain fully - functional during the deprecation period. In cases where this is not - possible, the deprecation must never make a given piece of code do something - different than it was before; at least an exception should be raised. -- If possible, usage of an deprecated API should emit a - `.MatplotlibDeprecationWarning`. There are a number of helper tools for this: - - - Use ``cbook.warn_deprecated()`` for general deprecation warnings. - - Use the decorator ``@cbook.deprecated`` to deprecate classes, functions, - methods, or properties. - - To warn on changes of the function signature, use the decorators - ``@cbook._delete_parameter``, ``@cbook._rename_parameter``, and - ``@cbook._make_keyword_only``. - -- Deprecated API may be removed two point-releases after they were deprecated. - +API consistency and stability are of great value. Therefore, API changes +(e.g. signature changes, behavior changes, removals) will only be conducted +if the added benefit is worth the user effort for adapting. + +Because we are a visualization library our primary output is the final +visualization the user sees. Thus it is our :ref:`long standing +` policy that the appearance of the figure is part of the API +and any changes, either semantic or esthetic, will be treated as a +backwards-incompatible API change. + +API changes in Matplotlib have to be performed following the deprecation process +below, except in very rare circumstances as deemed necessary by the development team. +This ensures that users are notified before the change will take effect and thus +prevents unexpected breaking of code. + +Rules +~~~~~ + +- Deprecations are targeted at the next point.release (e.g. 3.x) +- Deprecated API is generally removed two point-releases after introduction + of the deprecation. Longer deprecations can be imposed by core developers on + a case-by-case basis to give more time for the transition +- The old API must remain fully functional during the deprecation period +- If alternatives to the deprecated API exist, they should be available + during the deprecation period +- If in doubt, decisions about API changes are finally made by the + API consistency lead developer + +Introducing +~~~~~~~~~~~ + +1. Announce the deprecation in a new file + :file:`doc/api/next_api_changes/deprecations/99999-ABC.rst` where ``99999`` + is the pull request number and ``ABC`` are the contributor's initials. +2. If possible, issue a `~matplotlib.MatplotlibDeprecationWarning` when the + deprecated API is used. There are a number of helper tools for this: + + - Use ``_api.warn_deprecated()`` for general deprecation warnings + - Use the decorator ``@_api.deprecated`` to deprecate classes, functions, + methods, or properties + - To warn on changes of the function signature, use the decorators + ``@_api.delete_parameter``, ``@_api.rename_parameter``, and + ``@_api.make_keyword_only`` + + All these helpers take a first parameter *since*, which should be set to + the next point release, e.g. "3.x". + + You can use standard rst cross references in *alternative*. + +Expiring +~~~~~~~~ + +1. Announce the API changes in a new file + :file:`doc/api/next_api_changes/[kind]/99999-ABC.rst` where ``99999`` + is the pull request number and ``ABC`` are the contributor's initials, and + ``[kind]`` is one of the folders :file:`behavior`, :file:`development`, + :file:`removals`. See :file:`doc/api/next_api_changes/README.rst` for more + information. For the content, you can usually copy the deprecation notice + and adapt it slightly. +2. Change the code functionality and remove any related deprecation warnings. Adding new API -------------- @@ -323,7 +412,7 @@ New modules and files: installation * If you have added new files or directories, or reorganized existing ones, make sure the new files are included in the match patterns in - :file:`MANIFEST.in`, and/or in *package_data* in :file:`setup.py`. + in *package_data* in :file:`setupext.py`. C/C++ extensions ---------------- @@ -342,30 +431,33 @@ C/C++ extensions docstrings, and the Numpydoc format is well understood in the scientific Python community. +* C/C++ code in the :file:`extern/` directory is vendored, and should be kept + close to upstream whenever possible. It can be modified to fix bugs or + implement new features only if the required changes cannot be made elsewhere + in the codebase. In particular, avoid making style fixes to it. + .. _keyword-argument-processing: Keyword argument processing --------------------------- Matplotlib makes extensive use of ``**kwargs`` for pass-through customizations -from one function to another. A typical example is in `matplotlib.pyplot.text`. -The definition of the pylab text function is a simple pass-through to -`matplotlib.axes.Axes.text`:: +from one function to another. A typical example is +`~matplotlib.axes.Axes.text`. The definition of `matplotlib.pyplot.text` is a +simple pass-through to `matplotlib.axes.Axes.text`:: - # in pylab.py - def text(*args, **kwargs): - ret = gca().text(*args, **kwargs) - draw_if_interactive() - return ret + # in pyplot.py + def text(x, y, s, fontdict=None, **kwargs): + return gca().text(x, y, s, fontdict=fontdict, **kwargs) -`~matplotlib.axes.Axes.text` in simplified form looks like this, i.e., it just +`matplotlib.axes.Axes.text` (simplified for illustration) just passes all ``args`` and ``kwargs`` on to ``matplotlib.text.Text.__init__``:: # in axes/_axes.py - def text(self, x, y, s, fontdict=None, withdash=False, **kwargs): + def text(self, x, y, s, fontdict=None, **kwargs): t = Text(x=x, y=y, text=s, **kwargs) -and ``matplotlib.text.Text.__init__`` (again with liberties for illustration) +and ``matplotlib.text.Text.__init__`` (again, simplified) just passes them on to the `matplotlib.artist.Artist.update` method:: # in text.py @@ -436,11 +528,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? ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -485,14 +579,14 @@ 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 -will display:: +.. code-block:: none UserWarning: Attempting to set identical bottom==top warnings.warn('Attempting to set identical bottom==top') @@ -505,10 +599,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/dependencies.rst b/doc/devel/dependencies.rst index c28283517005..1bef37f86690 100644 --- a/doc/devel/dependencies.rst +++ b/doc/devel/dependencies.rst @@ -4,84 +4,92 @@ Dependencies ============ +Runtime dependencies +==================== + + Mandatory dependencies -====================== +---------------------- When installing through a package manager like ``pip`` or ``conda``, the mandatory dependencies are automatically installed. This list is mainly for reference. -* `Python `_ (>= 3.7) -* `NumPy `_ (>= 1.16) -* `setuptools `_ +* `Python `_ (>= 3.8) +* `contourpy `_ (>= 1.0.1) * `cycler `_ (>= 0.10.0) -* `dateutil `_ (>= 2.7) +* `dateutil `_ (>= 2.7) +* `fontTools `_ (>= 4.22.0) * `kiwisolver `_ (>= 1.0.1) +* `NumPy `_ (>= 1.19) +* `packaging `_ (>= 20.0) * `Pillow `_ (>= 6.2) -* `pyparsing `_ (>=2.2.1) +* `pyparsing `_ (>= 2.2.1) +* `setuptools `_ .. _optional_dependencies: Optional dependencies -===================== +--------------------- The following packages and tools are not required but extend the capabilities of Matplotlib. Backends --------- +~~~~~~~~ Matplotlib figures can be rendered to various user interfaces. See :ref:`what-is-a-backend` for more details on the optional Matplotlib backends and the capabilities they provide. -* Tk_ (>= 8.3, != 8.6.0 or 8.6.1) [#]_: for the Tk-based backends. -* PyQt4_ (>= 4.6) or PySide_ (>= 1.0.3) [#]_: for the Qt4-based backends. -* PyQt5_ or PySide2_: for the Qt5-based backends. -* PyGObject_: for the GTK3-based backends [#]_. -* wxPython_ (>= 4) [#]_: for the wx-based backends. -* pycairo_ (>= 1.11.0) or cairocffi_ (>= 0.8): for the GTK3 and/or cairo-based - backends. -* Tornado_: for the WebAgg backend. +* Tk_ (>= 8.4, != 8.6.0 or 8.6.1): for the Tk-based backends. Tk is part of + most standard Python installations, but it's not part of Python itself and + thus may not be present in rare cases. +* PyQt6_ (>= 6.1), PySide6_, PyQt5_, or PySide2_: for the Qt-based backends. +* PyGObject_ and pycairo_ (>= 1.14.0): for the GTK-based backends. If using pip + (but not conda or system package manager) PyGObject must be built from + source; see `pygobject documentation + `_. +* pycairo_ (>= 1.14.0) or cairocffi_ (>= 0.8): for cairo-based backends. +* wxPython_ (>= 4): for the wx-based backends. If using pip (but not conda or + system package manager) on Linux wxPython wheels must be manually downloaded + from https://wxpython.org/pages/downloads/. +* Tornado_ (>= 5): for the WebAgg backend. +* ipykernel_: for the nbagg backend. +* macOS (>= 10.12): for the macosx backend. .. _Tk: https://docs.python.org/3/library/tk.html -.. _PyQt4: https://pypi.org/project/PyQt4 -.. _PySide: https://pypi.org/project/PySide -.. _PyQt5: https://pypi.org/project/PyQt5 -.. _PySide2: https://pypi.org/project/PySide2 +.. _PyQt5: https://pypi.org/project/PyQt5/ +.. _PySide2: https://pypi.org/project/PySide2/ +.. _PyQt6: https://pypi.org/project/PyQt6/ +.. _PySide6: https://pypi.org/project/PySide6/ .. _PyGObject: https://pygobject.readthedocs.io/en/latest/ .. _wxPython: https://www.wxpython.org/ .. _pycairo: https://pycairo.readthedocs.io/en/latest/ .. _cairocffi: https://cairocffi.readthedocs.io/en/latest/ -.. _Tornado: https://pypi.org/project/tornado - -.. [#] Tk is part of most standard Python installations, but it's not part of - Python itself and thus may not be present in rare cases. -.. [#] PySide cannot be pip-installed on Linux (but can be conda-installed). -.. [#] If using pip (and not conda), PyGObject must be built from source; see - https://pygobject.readthedocs.io/en/latest/devguide/dev_environ.html. -.. [#] If using pip (and not conda) on Linux, wxPython wheels must be manually - downloaded from https://wxpython.org/pages/downloads/. +.. _Tornado: https://pypi.org/project/tornado/ +.. _ipykernel: https://pypi.org/project/ipykernel/ Animations ----------- +~~~~~~~~~~ * `ffmpeg `_: for saving movies. * `ImageMagick `_: for saving animated gifs. Font handling and rendering ---------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~ * `LaTeX `_ (with `cm-super - `__ ) and `GhostScript (>=9.0) - `_ : for rendering text with LaTeX. + `__ and `underscore + `__) and `GhostScript (>= 9.0) + `_: for rendering text with LaTeX. * `fontconfig `_ (>= 2.7): for detection of system fonts on Linux. C libraries -=========== +----------- Matplotlib brings its own copies of the following libraries: @@ -102,7 +110,7 @@ rasterize characters differently) and of Qhull. As an exception, Matplotlib defaults to the system version of FreeType on AIX. To force Matplotlib to use a copy of FreeType or Qhull already installed in -your system, create a :file:`setup.cfg` file with the following contents: +your system, create a :file:`mplsetup.cfg` file with the following contents: .. code-block:: cfg @@ -161,3 +169,159 @@ If you go this route but need to reset and rebuild to change your settings, remember to clear your artifacts before re-building:: git clean -xfd + + +Minimum pip / manylinux support (linux) +--------------------------------------- + +Matplotlib publishes `manylinux wheels `_ +which have a minimum version of pip which will recognize the wheels + +- Python 3.8: ``manylinx2010`` / pip >= 19.0 +- Python 3.9+: ``manylinx2014`` / pip >= 19.3 + +In all cases the required version of pip is embedded in the CPython source. + + + +.. _development-dependencies: + +Dependencies for building Matplotlib +==================================== + +.. _setup-dependencies: + +Setup dependencies +------------------ + +- `certifi `_ (>= 2020.06.20). Used while + downloading the freetype and QHull source during build. This is not a + runtime dependency. +- `setuptools_scm `_ (>= 7). Used to + update the reported ``mpl.__version__`` based on the current git commit. + Also a runtime dependency for editable installs. +- `NumPy `_ (>= 1.19). Also a runtime dependency. + + +.. _compile-dependencies: + +C++ compiler +------------ + +Matplotlib requires a C++ compiler that supports C++11. + +- `gcc 4.8.1 `_ or higher +- `clang 3.3 `_ or higher +- `Visual Studio 2015 + `_ + (aka VS 14.0) or higher + + +.. _test-dependencies: + +Dependencies for testing Matplotlib +=================================== +This section lists the additional software required for +:ref:`running the tests `. + +Required: + +- pytest_ (>= 3.6) + +Optional: + +In addition to all of the optional dependencies on the main library, for +testing the following will be used if they are installed. + +- Ghostscript_ (>= 9.0, to render PDF files) +- Inkscape_ (to render SVG files) +- nbformat_ and nbconvert_ used to test the notebook backend +- pandas_ used to test compatibility with Pandas +- pikepdf_ used in some tests for the pgf and pdf backends +- psutil_ used in testing the interactive backends +- pytest-cov_ (>= 2.3.1) to collect coverage information +- pytest-flake8_ to test coding standards using flake8_ +- pytest-timeout_ to limit runtime in case of stuck tests +- pytest-xdist_ to run tests in parallel +- pytest-xvfb_ to run tests without windows popping up (Linux) +- pytz_ used to test pytz int +- sphinx_ used to test our sphinx extensions +- WenQuanYi Zen Hei and `Noto Sans CJK `_ + fonts for testing font fallback and non-western fonts +- xarray_ used to test compatibility with xarray + +If any of these dependencies are not discovered the tests that rely on them +will be skipped by pytest. + +.. note:: + + When installing Inkscape on Windows, make sure that you select “Add + Inkscape to system PATHâ€, either for all users or current user, or the + tests will not find it. + +.. _Ghostscript: https://ghostscript.com/ +.. _Inkscape: https://inkscape.org +.. _flake8: https://pypi.org/project/flake8/ +.. _nbconvert: https://pypi.org/project/nbconvert/ +.. _nbformat: https://pypi.org/project/nbformat/ +.. _pandas: https://pypi.org/project/pandas/ +.. _pikepdf: https://pypi.org/project/pikepdf/ +.. _psutil: https://pypi.org/project/psuitl/ +.. _pytz: https://fonts.google.com/noto/use#faq +.. _pytest-cov: https://pytest-cov.readthedocs.io/en/latest/ +.. _pytest-flake8: https://pypi.org/project/pytest-flake8/ +.. _pytest-timeout: https://pypi.org/project/pytest-timeout/ +.. _pytest-xdist: https://pypi.org/project/pytest-xdist/ +.. _pytest-xvfb: https://pypi.org/project/pytest-xvfb/ +.. _pytest: http://doc.pytest.org/en/latest/ +.. _sphinx: https://pypi.org/project/Sphinx/ +.. _xarray: https://pypi.org/project/xarray/ + + +.. _doc-dependencies: + +Dependencies for building Matplotlib's documentation +==================================================== + +Python packages +--------------- +The additional Python packages required to build the +:ref:`documentation ` are listed in +:file:`doc-requirements.txt` and can be installed using :: + + pip install -r requirements/doc/doc-requirements.txt + +The content of :file:`doc-requirements.txt` is also shown below: + + .. include:: ../../requirements/doc/doc-requirements.txt + :literal: + +Additional external dependencies +-------------------------------- +Required: + +* a minimal working LaTeX distribution +* `Graphviz `_ +* the following LaTeX packages (if your OS bundles TeXLive, the + "complete" version of the installer, e.g. "texlive-full" or "texlive-all", + will often automatically include these packages): + + * `cm-super `_ + * `dvipng `_ + * `underscore `_ + +Optional, but recommended: + +* `Inkscape `_ +* `optipng `_ +* the font "Humor Sans" (aka the "XKCD" font), or the free alternative + `Comic Neue `_ +* the font "Times New Roman" + +.. note:: + + The documentation will not build without LaTeX and Graphviz. These are not + Python packages and must be installed separately. The documentation can be + built without Inkscape and optipng, but the build process will raise various + warnings. If the build process warns that you are missing fonts, make sure + your LaTeX distribution bundles cm-super or install it separately. diff --git a/doc/devel/development_setup.rst b/doc/devel/development_setup.rst index d926f401c757..d69ca18f7c37 100644 --- a/doc/devel/development_setup.rst +++ b/doc/devel/development_setup.rst @@ -4,49 +4,90 @@ Setting up Matplotlib for development ===================================== +To set up Matplotlib for development follow these steps: + +.. contents:: + :local: + +Retrieve the latest version of the code +======================================= + +Matplotlib is hosted at https://github.com/matplotlib/matplotlib.git. + +You can retrieve the latest sources with the command (see +:ref:`set-up-fork` for more details) + +.. tab-set:: + + .. tab-item:: https + + .. code-block:: bash + + git clone https://github.com/matplotlib/matplotlib.git + + .. tab-item:: ssh + + .. code-block:: bash + + git clone git@github.com:matplotlib/matplotlib.git + + This requires you to setup an `SSH key`_ in advance, but saves you from + typing your password at every connection. + + .. _SSH key: https://docs.github.com/en/authentication/connecting-to-github-with-ssh + +This will place the sources in a directory :file:`matplotlib` below your +current working directory. Change into this directory:: + + cd matplotlib + + .. _dev-environment: -Creating a dedicated environment -================================ +Create a dedicated environment +============================== You should set up a dedicated environment to decouple your Matplotlib development from other Python and Matplotlib installations on your system. -Here we use python's virtual environment `venv`_, but you may also use others -such as conda. + +The simplest way to do this is to use either Python's virtual environment +`venv`_ or `conda`_. .. _venv: https://docs.python.org/3/library/venv.html +.. _conda: https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html -A new environment can be set up with :: +.. tab-set:: - python -m venv + .. tab-item:: venv environment -and activated with one of the following:: + Create a new `venv`_ environment with :: - source /bin/activate # Linux/macOS - \Scripts\activate.bat # Windows cmd.exe - \Scripts\Activate.ps1 # Windows PowerShell + python -m venv -Whenever you plan to work on Matplotlib, remember to activate the development -environment in your shell. + and activate it with one of the following :: -Retrieving the latest version of the code -========================================= + source /bin/activate # Linux/macOS + \Scripts\activate.bat # Windows cmd.exe + \Scripts\Activate.ps1 # Windows PowerShell -Matplotlib is hosted at https://github.com/matplotlib/matplotlib.git. + .. tab-item:: conda environment -You can retrieve the latest sources with the command (see -:ref:`set-up-fork` for more details):: + Create a new `conda`_ environment with :: - git clone https://github.com/matplotlib/matplotlib.git + conda env create -f environment.yml -This will place the sources in a directory :file:`matplotlib` below your -current working directory. + You can use ``mamba`` instead of ``conda`` in the above command if + you have `mamba`_ installed. + + .. _mamba: https://mamba.readthedocs.io/en/latest/ + + Activate the environment using :: -If you have the proper privileges, you can use ``git@`` instead of -``https://``, which works through the ssh protocol and might be easier to use -if you are using 2-factor authentication. + conda activate mpl-dev -Installing Matplotlib in editable mode -====================================== +Remember to activate the environment whenever you start working on Matplotlib. + +Install Matplotlib in editable mode +=================================== Install Matplotlib in editable mode from the :file:`matplotlib` directory using the command :: @@ -60,76 +101,24 @@ true for ``*.py`` files. If you change the C-extension source (which might also happen if you change branches) you will have to re-run ``python -m pip install -ve .`` -.. _test-dependencies: +Install additional development dependencies +=========================================== +See :ref:`development-dependencies`. -Additional dependencies for testing +Install pre-commit hooks (optional) =================================== -This section lists the additional software required for -:ref:`running the tests `. - -Required: - -- pytest_ (>=3.6) -- Ghostscript_ (>= 9.0, to render PDF files) -- Inkscape_ (to render SVG files) - -Optional: - -- pytest-cov_ (>=2.3.1) to collect coverage information -- pytest-flake8_ to test coding standards using flake8_ -- pytest-timeout_ to limit runtime in case of stuck tests -- pytest-xdist_ to run tests in parallel - -.. _pytest: http://doc.pytest.org/en/latest/ -.. _Ghostscript: https://www.ghostscript.com/ -.. _Inkscape: https://inkscape.org -.. _pytest-cov: https://pytest-cov.readthedocs.io/en/latest/ -.. _pytest-flake8: https://pypi.org/project/pytest-flake8/ -.. _pytest-xdist: https://pypi.org/project/pytest-xdist/ -.. _pytest-timeout: https://pypi.org/project/pytest-timeout/ -.. _flake8: https://pypi.org/project/flake8/ - - -.. _doc-dependencies: - -Additional dependencies for building documentation -================================================== - -Python packages ---------------- -The additional Python packages required to build the -:ref:`documentation ` are listed in -:file:`doc-requirements.txt` and can be installed using :: - - pip install -r requirements/doc/doc-requirements.txt - -The content of :file:`doc-requirements.txt` is also shown below: - - .. include:: ../../requirements/doc/doc-requirements.txt - :literal: - -Additional external dependencies --------------------------------- -Required: +`pre-commit `_ hooks automatically check flake8 and +other style issues when you run ``git commit``. The hooks are defined in the +top level ``.pre-commit-config.yaml`` file. To install the hooks :: -* a minimal working LaTeX distribution -* `Graphviz `_ -* the LaTeX packages *cm-super* and *dvipng*. If your OS bundles ``TexLive``, - then often the "complete" version of the installer will automatically include - these packages (e.g. "texlive-full" or "texlive-all"). + python -m pip install pre-commit + pre-commit install -Optional, but recommended: +The hooks can also be run manually. All the hooks can be run, in order as +listed in ``.pre-commit-config.yaml``, against the full codebase with :: -* `Inkscape `_ -* `optipng `_ -* the font "Humor Sans" (aka the "XKCD" font), or the free alternative - `Comic Neue `_. -* the font "Times New Roman" + pre-commit run --all-files -.. note:: +To run a particular hook manually, run ``pre-commit run`` with the hook id :: - The documentation will not build without LaTeX and Graphviz. These are not - Python packages and must be installed separately. The documentation can be - built without Inkscape and optipng, but the build process will raise various - warnings. If the build process warns that you are missing fonts, make sure - your LaTeX distribution bundles cm-super or install it separately. + pre-commit run --all-files diff --git a/doc/devel/documenting_mpl.rst b/doc/devel/documenting_mpl.rst index 01985f700a4e..a9305d31a078 100644 --- a/doc/devel/documenting_mpl.rst +++ b/doc/devel/documenting_mpl.rst @@ -4,47 +4,41 @@ Writing documentation ===================== -.. contents:: Contents - :depth: 3 - :local: - :backlinks: top - :class: multicol-toc - - Getting started =============== General file structure ---------------------- -All documentation is built from the :file:`doc/`, :file:`tutorials/`, and -:file:`examples/` directories. The :file:`doc/` directory contains -configuration files for Sphinx and reStructuredText (ReST_; ``.rst``) files -that are rendered to documentation pages. - - -The main entry point is :file:`doc/index.rst`, which pulls in the -:file:`index.rst` file for the users guide (:file:`doc/users`), developers -guide (:file:`doc/devel`), api reference (:file:`doc/api`), and FAQs -(:file:`doc/faq`). The documentation suite is built as a single document in -order to make the most effective use of cross referencing. +All documentation is built from the :file:`doc/`. The :file:`doc/` +directory contains configuration files for Sphinx and reStructuredText +(ReST_; ``.rst``) files that are rendered to documentation pages. -Sphinx_ also creates ``.rst`` files that are staged in :file:`doc/api` from +Documentation is created in three ways. First, API documentation +(:file:`doc/api`) is created by Sphinx_ from the docstrings of the classes in the Matplotlib library. Except for -:file:`doc/api/api_changes/`, these ``.rst`` files are created when the -documentation is built. - -Similarly, the contents of :file:`doc/gallery` and :file:`doc/tutorials` are -generated by the `Sphinx Gallery`_ from the sources in :file:`examples/` and -:file:`tutorials/`. These sources consist of python scripts that have ReST_ -documentation built into their comments. +:file:`doc/api/api_changes/`, ``.rst`` files in :file:`doc/api` are created +when the documentation is built. See :ref:`writing-docstrings` below. + +Second, the contents of :file:`doc/plot_types`, :file:`doc/gallery` and +:file:`doc/tutorials` are generated by the `Sphinx Gallery`_ from python +files in :file:`plot_types/`, :file:`examples/` and :file:`tutorials/`. +These sources consist of python scripts that have ReST_ documentation built +into their comments. See :ref:`writing-examples-and-tutorials` below. + +Third, Matplotlib has narrative docs written in ReST_ in subdirectories of +:file:`doc/users/`. If you would like to add new documentation that is suited +to an ``.rst`` file rather than a gallery or tutorial example, choose an +appropriate subdirectory to put it in, and add the file to the table of +contents of :file:`index.rst` of the subdirectory. See +:ref:`writing-rest-pages` below. .. note:: - Don't directly edit the ``.rst`` files in :file:`doc/gallery`, - :file:`doc/tutorials`, and :file:`doc/api` (excepting - :file:`doc/api/api_changes/`). Sphinx_ regenerates files in these - directories when building documentation. + Don't directly edit the ``.rst`` files in :file:`doc/plot_types`, + :file:`doc/gallery`, :file:`doc/tutorials`, and :file:`doc/api` + (excepting :file:`doc/api/api_changes/`). Sphinx_ regenerates + files in these directories when building documentation. Setting up the doc build ------------------------ @@ -88,38 +82,48 @@ it, use make SPHINXOPTS= html -On Windows the arguments must be at the end of the statement: - -.. code-block:: bat - - make html SPHINXOPTS= - You can use the ``O`` variable to set additional options: * ``make O=-j4 html`` runs a parallel build with 4 processes. * ``make O=-Dplot_formats=png:100 html`` saves figures in low resolution. * ``make O=-Dplot_gallery=0 html`` skips the gallery build. -Multiple options can be combined using e.g. ``make O='-j4 -Dplot_gallery=0' +Multiple options can be combined, e.g. ``make O='-j4 -Dplot_gallery=0' html``. -On Windows, either use the format shown above or set options as environment variables, e.g.: +On Windows, set the options as environment variables, e.g.: .. code-block:: bat - set O=-W --keep-going -j4 - make html + set SPHINXOPTS= & set O=-j4 -Dplot_gallery=0 & make html + +Showing locally built docs +-------------------------- + +The built docs are available in the folder :file:`build/html`. A shortcut +for opening them in your default browser is: + +.. code-block:: sh + + make show .. _writing-rest-pages: Writing ReST pages ================== -Most documentation is either in the docstring of individual +Most documentation is either in the docstrings of individual classes and methods, in explicit ``.rst`` files, or in examples and tutorials. -All of these use the ReST_ syntax. Users should look at the ReST_ documentation -for a full description. But some specific hints and conventions Matplotlib -uses are useful for creating documentation. +All of these use the ReST_ syntax and are processed by Sphinx_. + +The `Sphinx reStructuredText Primer +`_ is +a good introduction into using ReST. More complete information is available in +the `reStructuredText reference documentation +`_. + +This section contains additional information and conventions how ReST is used +in the Matplotlib documentation. Formatting and style conventions -------------------------------- @@ -127,13 +131,28 @@ Formatting and style conventions It is useful to strive for consistency in the Matplotlib documentation. Here are some formatting and style conventions that are used. -Section name formatting -~~~~~~~~~~~~~~~~~~~~~~~ +Section formatting +~~~~~~~~~~~~~~~~~~ For everything but top-level chapters, use ``Upper lower`` for section titles, e.g., ``Possible hangups`` rather than ``Possible Hangups`` +We aim to follow the recommendations from the +`Python documentation `_ +and the `Sphinx reStructuredText documentation `_ +for section markup characters, i.e.: + +- ``#`` with overline, for parts. This is reserved for the main title in + ``index.rst``. All other pages should start with "chapter" or lower. +- ``*`` with overline, for chapters +- ``=``, for sections +- ``-``, for subsections +- ``^``, for subsubsections +- ``"``, for paragraphs + +This may not yet be applied consistently in existing docs. + Function arguments ~~~~~~~~~~~~~~~~~~ @@ -171,22 +190,22 @@ Documents can be linked with the ``:doc:`` directive: .. code-block:: rst - See the :doc:`/faq/installing_faq` + See the :doc:`/users/installing/index` - See the tutorial :doc:`/tutorials/introductory/sample_plots` + See the tutorial :doc:`/tutorials/introductory/quick_start` See the example :doc:`/gallery/lines_bars_and_markers/simple_plot` will render as: - See the :doc:`/faq/installing_faq` + See the :doc:`/users/installing/index` - See the tutorial :doc:`/tutorials/introductory/sample_plots` + See the tutorial :doc:`/tutorials/introductory/quick_start` See the example :doc:`/gallery/lines_bars_and_markers/simple_plot` Sections can also be given reference names. For instance from the -:doc:`/faq/installing_faq` link: +:doc:`/users/installing/index` link: .. code-block:: rst @@ -243,7 +262,7 @@ generates a link like this: `matplotlib.collections.LineCollection`. have to use qualifiers like ``:class:``, ``:func:``, ``:meth:`` and the likes. Often, you don't want to show the full package and module name. As long as the -target is unanbigous you can simply leave them out: +target is unambiguous you can simply leave them out: .. code-block:: rst @@ -280,8 +299,8 @@ you can check the full list of referenceable objects with the following commands:: python -m sphinx.ext.intersphinx 'https://docs.python.org/3/objects.inv' - python -m sphinx.ext.intersphinx 'https://docs.scipy.org/doc/numpy/objects.inv' - python -m sphinx.ext.intersphinx 'https://docs.scipy.org/doc/scipy/reference/objects.inv' + python -m sphinx.ext.intersphinx 'https://numpy.org/doc/stable/objects.inv' + python -m sphinx.ext.intersphinx 'https://docs.scipy.org/doc/scipy/objects.inv' python -m sphinx.ext.intersphinx 'https://pandas.pydata.org/pandas-docs/stable/objects.inv' .. _rst-figures-and-includes: @@ -290,24 +309,19 @@ Including figures and files --------------------------- Image files can directly included in pages with the ``image::`` directive. -e.g., :file:`thirdpartypackages/index.rst` displays the images for the third-party -packages as static images:: - - .. image:: /_static/toolbar.png +e.g., :file:`tutorials/intermediate/constrainedlayout_guide.py` displays +a couple of static images:: -as rendered on the page: :ref:`thirdparty-index`. + # .. image:: /_static/constrained_layout_1b.png + # :align: center -Files can be included verbatim. For instance the ``matplotlibrc`` file -is important for customizing Matplotlib, and is included verbatim in the -tutorial in :doc:`/tutorials/introductory/customizing`:: - .. literalinclude:: ../../_static/matplotlibrc +Files can be included verbatim. For instance the ``LICENSE`` file is included +at :ref:`license-agreement` using :: -This is rendered at the bottom of :doc:`/tutorials/introductory/customizing`. -Note that this is in a tutorial; see :ref:`writing-examples-and-tutorials` -below. + .. literalinclude:: ../../LICENSE/LICENSE -The examples directory is also copied to :file:`doc/gallery` by sphinx-gallery, +The examples directory is copied to :file:`doc/gallery` by sphinx-gallery, so plots from the examples directory can be included using .. code-block:: rst @@ -347,7 +361,7 @@ An example docstring looks like: .. code-block:: python - def hlines(self, y, xmin, xmax, colors='k', linestyles='solid', + def hlines(self, y, xmin, xmax, colors=None, linestyles='solid', label='', **kwargs): """ Plot horizontal lines at each *y* from *xmin* to *xmax*. @@ -361,24 +375,26 @@ An example docstring looks like: Respective beginning and end of each line. If scalars are provided, all lines will have the same length. - colors : array-like of colors, default: 'k' + colors : list of colors, default: :rc:`lines.color` - linestyles : {'solid', 'dashed', 'dashdot', 'dotted'}, default: 'solid' + linestyles : {'solid', 'dashed', 'dashdot', 'dotted'}, optional label : str, default: '' Returns ------- - lines : `~matplotlib.collections.LineCollection` + `~matplotlib.collections.LineCollection` Other Parameters ---------------- - **kwargs : `~matplotlib.collections.LineCollection` properties + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER + **kwargs : `~matplotlib.collections.LineCollection` properties. - See also + See Also -------- vlines : vertical lines - axhline: horizontal line across the axes + axhline : horizontal line across the Axes """ See the `~.Axes.hlines` documentation for how this renders. @@ -545,10 +561,10 @@ effect. Sphinx automatically links code elements in the definition blocks of ``See also`` sections. No need to use backticks there:: - See also + See Also -------- vlines : vertical lines - axhline: horizontal line across the axes + axhline : horizontal line across the Axes Wrapping parameter lists ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -585,7 +601,7 @@ Setters and getters ------------------- Artist properties are implemented using setter and getter methods (because -Matplotlib predates the introductions of the `property` decorator in Python). +Matplotlib predates the Python `property` decorator). By convention, these setters and getters are named ``set_PROPERTYNAME`` and ``get_PROPERTYNAME``; the list of properties thusly defined on an artist and their values can be listed by the `~.pyplot.setp` and `~.pyplot.getp` functions. @@ -652,7 +668,7 @@ Keyword arguments Since Matplotlib uses a lot of pass-through ``kwargs``, e.g., in every function that creates a line (`~.pyplot.plot`, `~.pyplot.semilogx`, `~.pyplot.semilogy`, -etc...), it can be difficult for the new user to know which ``kwargs`` are +etc.), it can be difficult for the new user to know which ``kwargs`` are supported. Matplotlib uses a docstring interpolation scheme to support documentation of every function that takes a ``**kwargs``. The requirements are: @@ -663,26 +679,14 @@ are: 2. as automated as possible so that as properties change, the docs are updated automatically. -The function `matplotlib.artist.kwdoc` and the decorator -``matplotlib.docstring.dedent_interpd`` facilitate this. They combine Python -string interpolation in the docstring with the Matplotlib artist introspection -facility that underlies ``setp`` and ``getp``. The ``kwdoc`` function gives -the list of properties as a docstring. In order to use this in another -docstring, first update the ``matplotlib.docstring.interpd`` object, as seen in -this example from `matplotlib.lines`: - -.. code-block:: python - - # in lines.py - docstring.interpd.update(Line2D_kwdoc=artist.kwdoc(Line2D)) - -Then in any function accepting `~.Line2D` pass-through ``kwargs``, e.g., -`matplotlib.axes.Axes.plot`: +The ``@_docstring.interpd`` decorator implements this. Any function accepting +`.Line2D` pass-through ``kwargs``, e.g., `matplotlib.axes.Axes.plot`, can list +a summary of the `.Line2D` properties, as follows: .. code-block:: python # in axes.py - @docstring.dedent_interpd + @_docstring.interpd def plot(self, *args, **kwargs): """ Some stuff omitted @@ -707,17 +711,19 @@ Then in any function accepting `~.Line2D` pass-through ``kwargs``, e.g., Here is a list of available `.Line2D` properties: - %(Line2D_kwdoc)s - + %(Line2D:kwdoc)s """ -Note there is a problem for `~matplotlib.artist.Artist` ``__init__`` methods, -e.g., `matplotlib.patches.Patch.__init__`, which supports ``Patch`` ``kwargs``, -since the artist inspector cannot work until the class is fully defined and -we can't modify the ``Patch.__init__.__doc__`` docstring outside the class -definition. There are some some manual hacks in this case, violating the -"single entry point" requirement above -- see the ``docstring.interpd.update`` -calls in `matplotlib.patches`. +The ``%(Line2D:kwdoc)`` syntax makes ``interpd`` lookup an `.Artist` subclass +named ``Line2D``, and call `.artist.kwdoc` on that class. `.artist.kwdoc` +introspects the subclass and summarizes its properties as a substring, which +gets interpolated into the docstring. + +Note that this scheme does not work for decorating an Artist's ``__init__``, as +the subclass and its properties are not defined yet at that point. Instead, +``@_docstring.interpd`` can be used to decorate the class itself -- at that +point, `.kwdoc` can list the properties and interpolate them into +``__init__.__doc__``. Inheriting docstrings @@ -748,7 +754,7 @@ Adding figures -------------- As above (see :ref:`rst-figures-and-includes`), figures in the examples gallery -can be referenced with a ``:plot:`` directive pointing to the python script +can be referenced with a ``.. plot::`` directive pointing to the python script that created the figure. For instance the `~.Axes.legend` docstring references the file :file:`examples/text_labels_and_annotations/legend.py`: @@ -836,7 +842,7 @@ render as comments in :doc:`/gallery/lines_bars_and_markers/simple_plot`. Tutorials are made with the exact same mechanism, except they are longer, and typically have more than one comment block (i.e. -:doc:`/tutorials/introductory/usage`). The first comment block +:doc:`/tutorials/introductory/quick_start`). The first comment block can be the same as the example above. Subsequent blocks of ReST text are delimited by a line of ``###`` characters: @@ -864,6 +870,35 @@ are delimited by a line of ``###`` characters: In this way text, code, and figures are output in a "notebook" style. +References for sphinx-gallery +----------------------------- + +The showcased Matplotlib functions should be listed in an admonition at the +bottom as follows + +.. code-block:: python + + ############################################################################### + # + # .. admonition:: References + # + # The use of the following functions, methods, classes and modules is shown + # in this example: + # + # - `matplotlib.axes.Axes.fill` / `matplotlib.pyplot.fill` + # - `matplotlib.axes.Axes.axis` / `matplotlib.pyplot.axis` + +This allows sphinx-gallery to place an entry to the example in the +mini-gallery of the mentioned functions. Whether or not a function is mentioned +here should be decided depending on if a mini-gallery link prominently helps +to illustrate that function; e.g. mention ``matplotlib.pyplot.subplots`` only +in examples that are about laying out subplots, not in every example that uses +it. + +Functions that exist in ``pyplot`` as well as in Axes or Figure should mention +both references no matter which one is used in the example code. The ``pyplot`` +reference should always be the second to mention; see the example above. + Order of examples in the gallery -------------------------------- @@ -918,63 +953,20 @@ will yield an html file ``/build/html/old_topic/old_info2.html`` that has a (relative) refresh to ``../topic/new_info.html``. Use the full path for this directive, relative to the doc root at -``http://matplotlib.org/stable/``. So ``/old_topic/old_info2`` would be +``https://matplotlib.org/stable/``. So ``/old_topic/old_info2`` would be found by users at ``http://matplotlib.org/stable/old_topic/old_info2``. For clarity, do not use relative links. -Adding animations ------------------ - -Animations are scraped automatically by Sphinx-gallery. If this is not -desired, -there is also a Matplotlib Google/Gmail account with username ``mplgithub`` -which was used to setup the github account but can be used for other -purposes, like hosting Google docs or Youtube videos. You can embed a -Matplotlib animation in the docs by first saving the animation as a -movie using :meth:`matplotlib.animation.Animation.save`, and then -uploading to `Matplotlib's Youtube -channel `_ and inserting the -embedding string youtube provides like: - -.. code-block:: rst - - .. raw:: html - - - -An example save command to generate a movie looks like this - -.. code-block:: python - - ani = animation.FuncAnimation(fig, animate, np.arange(1, len(y)), - interval=25, blit=True, init_func=init) - - ani.save('double_pendulum.mp4', fps=15) - -Contact Michael Droettboom for the login password to upload youtube videos of -google docs to the mplgithub account. - .. _inheritance-diagrams: Generating inheritance diagrams ------------------------------- -Class inheritance diagrams can be generated with the -``inheritance-diagram`` directive. To use it, provide the -directive with a number of class or module names (separated by -whitespace). If a module name is provided, all classes in that module -will be used. All of the ancestors of these classes will be included -in the inheritance diagram. +Class inheritance diagrams can be generated with the Sphinx +`inheritance-diagram`_ directive. -A single option is available: *parts* controls how many of parts in -the path to the class are shown. For example, if *parts* == 1, the -class ``matplotlib.patches.Patch`` is shown as ``Patch``. If *parts* -== 2, it is shown as ``patches.Patch``. If *parts* == 0, the full -path is shown. +.. _inheritance-diagram: https://www.sphinx-doc.org/en/master/usage/extensions/inheritance.html Example: @@ -986,46 +978,18 @@ Example: .. inheritance-diagram:: matplotlib.patches matplotlib.lines matplotlib.text :parts: 2 -.. _emacs-helpers: - -Emacs helpers -------------- - -There is an emacs mode `rst.el -`_ which -automates many important ReST tasks like building and updating -table-of-contents, and promoting or demoting section headings. Here -is the basic ``.emacs`` configuration: - -.. code-block:: lisp - - (require 'rst) - (setq auto-mode-alist - (append '(("\\.txt$" . rst-mode) - ("\\.rst$" . rst-mode) - ("\\.rest$" . rst-mode)) auto-mode-alist)) - -Some helpful functions:: - - C-c TAB - rst-toc-insert - - Insert table of contents at point - - C-c C-u - rst-toc-update - - Update the table of contents at point - - C-c C-l rst-shift-region-left - - Shift region to the left - C-c C-r rst-shift-region-right +Navbar and style +---------------- - Shift region to the right +Matplotlib has a few subprojects that share the same navbar and style, so these +are centralized as a sphinx theme at +`mpl_sphinx_theme `_. Changes to the +style or topbar should be made there to propagate across all subprojects. .. TODO: Add section about uploading docs -.. _ReST: http://docutils.sourceforge.net/rst.html +.. _ReST: https://docutils.sourceforge.io/rst.html .. _Sphinx: http://www.sphinx-doc.org .. _documentation: https://www.sphinx-doc.org/en/master/contents.html .. _index: http://www.sphinx-doc.org/markup/para.html#index-generating-markup diff --git a/doc/devel/gitwash/development_workflow.rst b/doc/devel/gitwash/development_workflow.rst index 7d6b6f0e70c3..d2917230d3ad 100644 --- a/doc/devel/gitwash/development_workflow.rst +++ b/doc/devel/gitwash/development_workflow.rst @@ -13,10 +13,10 @@ git by following :ref:`configure-git`. Now you are ready for some real work. Workflow summary ================ -In what follows we'll refer to the upstream Matplotlib ``master`` branch, as +In what follows we'll refer to the upstream Matplotlib ``main`` branch, as "trunk". -* Don't use your ``master`` branch for anything. Consider deleting it. +* Don't use your ``main`` branch for anything. Consider deleting it. * When you are starting a new set of changes, fetch any changes from trunk, and start a new *feature branch* from that. * Make a new branch for each separable set of changes |emdash| "one task, one @@ -24,7 +24,7 @@ In what follows we'll refer to the upstream Matplotlib ``master`` branch, as * Name your branch for the purpose of the changes - e.g. ``bugfix-for-issue-14`` or ``refactor-database-code``. * If you can possibly avoid it, avoid merging trunk or any other branches into - your feature branch while you are working. + your feature branch while you are working. * If you do find yourself merging from trunk, consider :ref:`rebase-on-trunk` * Ask on the `Matplotlib mailing list`_ if you get stuck. * Ask for code review! @@ -35,11 +35,11 @@ what you've done, and why you did it. See `linux git workflow`_ and `ipython git workflow`_ for some explanation. -Consider deleting your master branch -==================================== +Consider deleting your main branch +================================== -It may sound strange, but deleting your own ``master`` branch can help reduce -confusion about which branch you are on. See `deleting master on github`_ for +It may sound strange, but deleting your own ``main`` branch can help reduce +confusion about which branch you are on. See `deleting main on github`_ for details. .. _update-mirror-trunk: @@ -55,8 +55,8 @@ From time to time you should fetch the upstream (trunk) changes from github:: This will pull down any commits you don't have, and set the remote branches to point to the right commit. For example, 'trunk' is the branch referred to by -(remote/branchname) ``upstream/master`` - and if there have been commits since -you last checked, ``upstream/master`` will change after you do the fetch. +(remote/branchname) ``upstream/main`` - and if there have been commits since +you last checked, ``upstream/main`` will change after you do the fetch. .. _make-feature-branch: @@ -72,14 +72,14 @@ someone reviewing your branch to see what you are doing. Choose an informative name for the branch to remind yourself and the rest of us what the changes in the branch are for. For example ``add-ability-to-fly``, or -``buxfix-for-issue-42``. +``bugfix-for-issue-42``. :: # Update the mirror of trunk git fetch upstream # Make new feature branch starting at current trunk - git branch my-new-feature upstream/master + git branch my-new-feature upstream/main git checkout my-new-feature Generally, you will want to keep your feature branches on your public github_ @@ -180,7 +180,7 @@ Delete a branch on github :: - git checkout master + git checkout main # delete branch locally git branch -D my-unwanted-branch # delete branch on github @@ -210,14 +210,14 @@ Now all those people can do:: git clone https://github.com/your-user-name/matplotlib.git -Remember that links starting with ``https`` or ``git@`` are read-write, and that -``git@`` uses the ssh protocol; links starting with ``git://`` are read-only. +Remember that links starting with ``https`` or ``git@`` are read-write, and that +``git@`` uses the ssh protocol. Your collaborators can then commit directly into that repo with the usual:: git commit -am 'ENH - much better code' - git push origin master # pushes directly into your repo + git push origin main # pushes directly into your repo Explore your repository ----------------------- @@ -284,12 +284,12 @@ To do a rebase on trunk:: # make a backup in case you mess up git branch tmp cool-feature # rebase cool-feature onto trunk - git rebase --onto upstream/master upstream/master cool-feature + git rebase --onto upstream/main upstream/main cool-feature In this situation, where you are already on branch ``cool-feature``, the last command can be written more succinctly as:: - git rebase upstream/master + git rebase upstream/main When all looks good you can delete your backup branch:: diff --git a/doc/devel/gitwash/dot2_dot3.rst b/doc/devel/gitwash/dot2_dot3.rst index 7759e2e60d68..30852b5ad387 100644 --- a/doc/devel/gitwash/dot2_dot3.rst +++ b/doc/devel/gitwash/dot2_dot3.rst @@ -7,22 +7,22 @@ Thanks to Yarik Halchenko for this explanation. Imagine a series of commits A, B, C, D... Imagine that there are two -branches, *topic* and *master*. You branched *topic* off *master* when -*master* was at commit 'E'. The graph of the commits looks like this:: +branches, *topic* and *main*. You branched *topic* off *main* when +*main* was at commit 'E'. The graph of the commits looks like this:: A---B---C topic / - D---E---F---G master + D---E---F---G main Then:: - git diff master..topic + git diff main..topic will output the difference from G to C (i.e. with effects of F and G), while:: - git diff master...topic + git diff main...topic would output just differences in the topic branch (i.e. only A, B, and C). diff --git a/doc/devel/gitwash/following_latest.rst b/doc/devel/gitwash/following_latest.rst index 03518ea52f44..5c90f5b84446 100644 --- a/doc/devel/gitwash/following_latest.rst +++ b/doc/devel/gitwash/following_latest.rst @@ -20,7 +20,7 @@ Get the local copy of the code From the command line:: - git clone git://github.com/matplotlib/matplotlib.git + git clone https://github.com/matplotlib/matplotlib.git You now have a copy of the code tree in the new ``matplotlib`` directory. diff --git a/doc/devel/gitwash/git_links.inc b/doc/devel/gitwash/git_links.inc index c26173367c9b..67ddc4dcb5a6 100644 --- a/doc/devel/gitwash/git_links.inc +++ b/doc/devel/gitwash/git_links.inc @@ -46,7 +46,7 @@ .. _linux git workflow: https://www.mail-archive.com/dri-devel@lists.sourceforge.net/msg39091.html .. _git parable: http://tom.preston-werner.com/2009/05/19/the-git-parable.html .. _git foundation: https://matthew-brett.github.io/pydagogue/foundation.html -.. _deleting master on github: https://matthew-brett.github.io/pydagogue/gh_delete_master.html +.. _deleting main on github: https://matthew-brett.github.io/pydagogue/gh_delete_master.html .. _rebase without tears: https://matthew-brett.github.io/pydagogue/rebase_without_tears.html .. _resolving a merge: https://schacon.github.io/git/user-manual.html#resolving-a-merge .. _ipython git workflow: https://mail.python.org/pipermail/ipython-dev/2010-October/005632.html diff --git a/doc/devel/gitwash/maintainer_workflow.rst b/doc/devel/gitwash/maintainer_workflow.rst index 302f75241399..9a572e311168 100644 --- a/doc/devel/gitwash/maintainer_workflow.rst +++ b/doc/devel/gitwash/maintainer_workflow.rst @@ -26,12 +26,12 @@ Integrating changes ******************* Let's say you have some changes that need to go into trunk -(``upstream-rw/master``). +(``upstream-rw/main``). The changes are in some branch that you are currently on. For example, you are looking at someone's changes like this:: - git remote add someone git://github.com/someone/matplotlib.git + git remote add someone https://github.com/someone/matplotlib.git git fetch someone git branch cool-feature --track someone/cool-feature git checkout cool-feature @@ -47,7 +47,7 @@ If there are only a few commits, consider rebasing to upstream:: # Fetch upstream changes git fetch upstream-rw # rebase - git rebase upstream-rw/master + git rebase upstream-rw/main Remember that, if you do a rebase, and push that, you'll have to close any github pull requests manually, because github will not be able to detect the @@ -59,7 +59,7 @@ A long series of commits If there are a longer series of related commits, consider a merge instead:: git fetch upstream-rw - git merge --no-ff upstream-rw/master + git merge --no-ff upstream-rw/main The merge will be detected by github, and should close any related pull requests automatically. @@ -76,11 +76,11 @@ Now, in either case, you should check that the history is sensible and you have the right commits:: git log --oneline --graph - git log -p upstream-rw/master.. + git log -p upstream-rw/main.. The first line above just shows the history in a compact way, with a text representation of the history graph. The second line shows the log of commits -excluding those that can be reached from trunk (``upstream-rw/master``), and +excluding those that can be reached from trunk (``upstream-rw/main``), and including those that can be reached from current HEAD (implied with the ``..`` at the end). So, it shows the commits unique to this branch compared to trunk. The ``-p`` option shows the diff for these commits in patch form. @@ -90,9 +90,9 @@ Push to trunk :: - git push upstream-rw my-new-feature:master + git push upstream-rw my-new-feature:main -This pushes the ``my-new-feature`` branch in this repository to the ``master`` +This pushes the ``my-new-feature`` branch in this repository to the ``main`` branch in the ``upstream-rw`` repository. .. include:: links.inc diff --git a/doc/devel/gitwash/patching.rst b/doc/devel/gitwash/patching.rst index e7f852758477..aeea8c5394ac 100644 --- a/doc/devel/gitwash/patching.rst +++ b/doc/devel/gitwash/patching.rst @@ -31,7 +31,7 @@ Overview git config --global user.email you@yourdomain.example.com git config --global user.name "Your Name Comes Here" # get the repository if you don't have it - git clone git://github.com/matplotlib/matplotlib.git + git clone https://github.com/matplotlib/matplotlib.git # make a branch for your patching cd matplotlib git branch the-fix-im-thinking-of @@ -44,7 +44,7 @@ Overview # hack hack, hack git commit -am 'BF - added fix for Funny bug' # make the patch files - git format-patch -M -C master + git format-patch -M -C main Then, send the generated patch files to the `Matplotlib mailing list`_ |emdash| where we will thank you warmly. @@ -61,7 +61,7 @@ In detail #. If you don't already have one, clone a copy of the `Matplotlib`_ repository:: - git clone git://github.com/matplotlib/matplotlib.git + git clone https://github.com/matplotlib/matplotlib.git cd matplotlib #. Make a 'feature branch'. This will be where you work on @@ -93,9 +93,9 @@ In detail git status #. Finally, make your commits into patches. You want all the - commits since you branched from the ``master`` branch:: + commits since you branched from the ``main`` branch:: - git format-patch -M -C master + git format-patch -M -C main You will now have several files named for the commits: @@ -107,9 +107,9 @@ In detail Send these files to the `Matplotlib mailing list`_. When you are done, to switch back to the main copy of the -code, just return to the ``master`` branch:: +code, just return to the ``main`` branch:: - git checkout master + git checkout main Moving from patching to development =================================== @@ -122,9 +122,9 @@ have. Fork the `Matplotlib`_ repository on github |emdash| :ref:`forking`. Then:: - # checkout and refresh master branch from main repo - git checkout master - git pull origin master + # checkout and refresh main branch from main repo + git checkout main + git pull origin main # rename pointer to main repository to 'upstream' git remote rename origin upstream # point your repo to default read / write to your fork on github diff --git a/doc/devel/gitwash/set_up_fork.rst b/doc/devel/gitwash/set_up_fork.rst index 6b7e0271c45b..cfbd00dca63f 100644 --- a/doc/devel/gitwash/set_up_fork.rst +++ b/doc/devel/gitwash/set_up_fork.rst @@ -15,7 +15,7 @@ Overview git clone https://github.com/your-user-name/matplotlib.git cd matplotlib - git remote add upstream git://github.com/matplotlib/matplotlib.git + git remote add upstream https://github.com/matplotlib/matplotlib.git In detail ========= @@ -31,11 +31,11 @@ Clone your fork .. code-block:: none - * master - remotes/origin/master + * main + remotes/origin/main - This tells you that you are currently on the ``master`` branch, and - that you also have a ``remote`` connection to ``origin/master``. + This tells you that you are currently on the ``main`` branch, and + that you also have a ``remote`` connection to ``origin/main``. What remote repository is ``remote/origin``? Try ``git remote -v`` to see the URLs for the remote. They will point to your github fork. @@ -50,23 +50,18 @@ Linking your repository to the upstream repo :: cd matplotlib - git remote add upstream git://github.com/matplotlib/matplotlib.git + git remote add upstream https://github.com/matplotlib/matplotlib.git ``upstream`` here is just the arbitrary name we're using to refer to the main `Matplotlib`_ repository at `Matplotlib github`_. -Note that we've used ``git://`` for the URL rather than ``https://`` or ``git@``. The -``git://`` URL is read only. This means that we can't accidentally -(or deliberately) write to the upstream repo, and we are only going to -use it to merge into our own code. - Just for your own satisfaction, show yourself that you now have a new 'remote', with ``git remote -v show``, giving you something like: .. code-block:: none - upstream git://github.com/matplotlib/matplotlib.git (fetch) - upstream git://github.com/matplotlib/matplotlib.git (push) + upstream https://github.com/matplotlib/matplotlib.git (fetch) + upstream https://github.com/matplotlib/matplotlib.git (push) origin https://github.com/your-user-name/matplotlib.git (fetch) origin https://github.com/your-user-name/matplotlib.git (push) diff --git a/doc/devel/index.rst b/doc/devel/index.rst index c2c140173227..462205ee2271 100644 --- a/doc/devel/index.rst +++ b/doc/devel/index.rst @@ -1,8 +1,8 @@ .. _developers-guide-index: -################################ -The Matplotlib Developers' Guide -################################ +############ +Contributing +############ Thank you for your interest in helping to improve Matplotlib! There are various ways to contribute to Matplotlib. All of them are super valuable but don't necessarily @@ -19,14 +19,37 @@ process or how to fix something feel free to ask on `gitter `_ for short questions and on `discourse `_ for longer questions. -.. raw:: html +.. rst-class:: sd-d-inline-block - + .. button-ref:: submitting-a-bug-report + :class: sd-fs-6 + :color: primary + + Report a bug + +.. rst-class:: sd-d-inline-block + + .. button-ref:: request-a-new-feature + :class: sd-fs-6 + :color: primary + + Request a feature + +.. rst-class:: sd-d-inline-block + + .. button-ref:: contributing-code + :class: sd-fs-6 + :color: primary + + Contribute code + +.. rst-class:: sd-d-inline-block + + .. button-ref:: contributing_documentation + :class: sd-fs-6 + :color: primary + + Write documentation .. toctree:: :maxdepth: 2 @@ -36,7 +59,7 @@ process or how to fix something feel free to ask on `gitter development_setup.rst testing.rst documenting_mpl.rst - add_new_projection.rst + style_guide.rst gitwash/index.rst coding_guide.rst release_guide.rst diff --git a/doc/devel/min_dep_policy.rst b/doc/devel/min_dep_policy.rst index 45cee59e34a3..6f0ec95c7969 100644 --- a/doc/devel/min_dep_policy.rst +++ b/doc/devel/min_dep_policy.rst @@ -1,7 +1,7 @@ .. _min_deps_policy: ====================================== -Minimum Version of Dependencies Policy +Minimum version of dependencies policy ====================================== For the purpose of this document, 'minor version' is in the sense of @@ -32,7 +32,7 @@ on every major and minor release, but never on a patch release. See also the :ref:`list-of-dependency-min-versions`. -Python Dependencies +Python dependencies =================== For Python dependencies we should support at least: @@ -41,14 +41,14 @@ with compiled extensions minor versions initially released in the 24 months prior to our planned release date or the oldest that support our minimum Python + NumPy -without complied extensions +without compiled extensions minor versions initially released in the 12 months prior to our planned release date or the oldest that supports our minimum Python. We will only bump these dependencies as we need new features or the old versions no longer support our minimum NumPy or Python. -Test and Documentation Dependencies +Test and documentation dependencies =================================== As these packages are only needed for testing or building the docs and @@ -61,7 +61,7 @@ We will support at least minor versions of the development dependencies released in the 12 months prior to our planned release. We will only bump these as needed or versions no longer support our -minimum Python and numpy. +minimum Python and NumPy. System and C-dependencies ========================= @@ -83,7 +83,9 @@ specification of the dependencies. ========== ======== ====== Matplotlib Python NumPy ========== ======== ====== -3.4 3.7 1.16.0 +`3.6`_ 3.8 1.19.0 +`3.5`_ 3.7 1.17.0 +`3.4`_ 3.7 1.16.0 `3.3`_ 3.6 1.15.0 `3.2`_ 3.6 1.11.0 `3.1`_ 3.6 1.11.0 @@ -99,6 +101,9 @@ Matplotlib Python NumPy 1.0 2.4 1.1 ========== ======== ====== +.. _`3.6`: https://matplotlib.org/3.6.0/devel/dependencies.html +.. _`3.5`: https://matplotlib.org/3.5.0/devel/dependencies.html +.. _`3.4`: https://matplotlib.org/3.4.0/devel/dependencies.html .. _`3.3`: https://matplotlib.org/3.3.0/users/installing.html#dependencies .. _`3.2`: https://matplotlib.org/3.2.0/users/installing.html#dependencies .. _`3.1`: https://matplotlib.org/3.1.0/users/installing.html#dependencies diff --git a/doc/devel/release_guide.rst b/doc/devel/release_guide.rst index 451d482bc819..3f49631d00d8 100644 --- a/doc/devel/release_guide.rst +++ b/doc/devel/release_guide.rst @@ -3,7 +3,7 @@ .. _release-guide: ============= -Release Guide +Release guide ============= @@ -45,8 +45,8 @@ is currently broken:: .. _release_ghstats: -GitHub Stats -============ +GitHub statistics +================= We automatically extract GitHub issue, PRs, and authors from GitHub via the @@ -85,7 +85,7 @@ most common issue is ``*`` which is interpreted as unclosed markup). .. _release_chkdocs: -Update and Validate the Docs +Update and validate the docs ============================ Merge ``*-doc`` branch @@ -101,27 +101,62 @@ When making major or minor releases, update the supported versions in the Security Policy in :file:`SECURITY.md`. Commonly, this may be one or two previous minor releases, but is dependent on release managers. -Update "What's New" and "API changes" -------------------------------------- +Update release notes +-------------------- + +What's new +~~~~~~~~~~ + +*Only needed for major and minor releases. Bugfix releases should not have new +features.* + +Merge the contents of all the files in :file:`doc/users/next_whats_new/` +into a single file :file:`doc/users/prev_whats_new/whats_new_X.Y.0.rst` +and delete the individual files. + +API changes +~~~~~~~~~~~ + +*Primarily needed for major and minor releases. We may sometimes have API +changes in bugfix releases.* + +Merge the contents of all the files in :file:`doc/api/next_api_changes/` +into a single file :file:`doc/api/prev_api_changes/api_changes_X.Y.Z.rst` +and delete the individual files. + +Release notes TOC +~~~~~~~~~~~~~~~~~ -Before tagging major and minor releases, the "what's new" and "API changes" -listings should be updated. This is not needed for micro releases. +Update :file:`doc/users/release_notes.rst`: -For the "what's new", +- For major and minor releases add a new section - 1. copy the current content to a file in :file:`doc/users/prev_whats_new` - 2. merge all of the files in :file:`doc/users/next_whats_new/` into - :file:`doc/users/whats_new.rst` and delete the individual files - 3. comment out the next what's new glob at the top + .. code:: rst -Similarly for the "API changes", + X.Y + === + .. toctree:: + :maxdepth: 1 - 1. copy the current api changes to a file is :file:`doc/api/prev_api_changes` - 2. merge all of the files in the most recent :file:`doc/api/next_api_changes` - into :file:`doc/api/api_changes.rst` - 3. comment out the most recent API changes at the top. + prev_whats_new/whats_new_X.Y.0.rst + ../api/prev_api_changes/api_changes_X.Y.0.rst + prev_whats_new/github_stats_X.Y.0.rst +- For bugfix releases add the GitHub stats and (if present) the API changes to + the existing X.Y section -In both cases step 3 will have to be un-done right after the release. + .. code:: rst + + ../api/prev_api_changes/api_changes_X.Y.Z.rst + prev_whats_new/github_stats_X.Y.Z.rst + +Update version switcher +~~~~~~~~~~~~~~~~~~~~~~~ + +Update ``doc/_static/switcher.json``. If a minor release, ``X.Y.Z``, create +a new entry ``version: X.Y.(Z-1)``, and change the name of stable +``name: stable/X.Y.Z``. If a major release, ``X.Y.0``, change the name +of ``name: devel/X.(Y+1)`` and ``name: stable/X.Y.0`` as well as adding +a new version for the previous stable. Verify that docs build ---------------------- @@ -145,6 +180,19 @@ Python3 yet. You will need to create a Python2 environment with Address any issues which may arise. The internal links are checked on Circle CI, this should only flag failed external links. + +Update supported versions in SECURITY.md +---------------------------------------- + +For minor version release update the table in :file:`SECURITY.md` to specify +that the 2 most recent minor releases in the current major version series are +supported. + +For a major version release update the table in :file:`SECURITY.md` to specify +that the last minor version in the previous major version series is still +supported. Dropping support for the last version of a major version series +will be handled on an ad-hoc basis. + .. _release_tag: Create release commit and tag @@ -172,18 +220,16 @@ with the tag [#]_:: Finally, push the tag to GitHub:: - git push DANGER master v2.0.0 + git push DANGER main v2.0.0 Congratulations, the scariest part is done! -.. [#] The tarball that is provided by GitHub is produced using `git - archive `__. We use - `versioneer `__ - which uses a format string in +.. [#] The tarball that is provided by GitHub is produced using `git archive`_. + We use setuptools_scm_ which uses a format string in :file:`lib/matplotlib/_version.py` to have ``git`` insert a list of references to exported commit (see :file:`.gitattributes` for the configuration). This string is - then used by ``versioneer`` to produce the correct version, + then used by ``setuptools_scm`` to produce the correct version, based on the git tag, when users install from the tarball. However, if there is a branch pointed at the tagged commit, then the branch name will also be included in the tarball. @@ -195,6 +241,8 @@ Congratulations, the scariest part is done! git archive v2.0.0 -o matplotlib-2.0.0.tar.gz --prefix=matplotlib-2.0.0/ +.. _git archive: https://git-scm.com/docs/git-archive +.. _setuptools_scm: https://github.com/pypa/setuptools_scm If this is a final release, also create a 'doc' branch (this is not done for pre-releases):: @@ -216,7 +264,7 @@ On this branch un-comment the globs from :ref:`release_chkdocs`. And then :: .. _release_DOI: -Release Management / DOI +Release management / DOI ======================== Via the `GitHub UI @@ -308,7 +356,7 @@ Congratulations, you have now done the second scariest part! .. _release_docs: -Build and Deploy Documentation +Build and deploy documentation ============================== To build the documentation you must have the tagged version installed, but @@ -318,21 +366,21 @@ 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. The built documentation exists in the `matplotlib.github.com `__ repository. -Pushing changes to master automatically updates the website. +Pushing changes to main automatically updates the website. The documentation is organized by version. At the root of the tree is always the documentation for the latest stable release. Under that, there are directories containing the documentation for older versions. The documentation -for current master is built on Circle CI and pushed to the `devdocs +for current main is built on Circle CI and pushed to the `devdocs `__ repository. These are available at -`matplotlib.org/devdocs `__. +`matplotlib.org/devdocs `__. Assuming you have this repository checked out in the same directory as matplotlib :: @@ -355,7 +403,7 @@ the newly released version. Now commit and push everything to GitHub :: git add * git commit -a -m 'Updating docs for v2.0.0' - git push DANGER master + git push DANGER main Congratulations you have now done the third scariest part! diff --git a/doc/devel/style_guide.rst b/doc/devel/style_guide.rst new file mode 100644 index 000000000000..9dab7a6d99d2 --- /dev/null +++ b/doc/devel/style_guide.rst @@ -0,0 +1,411 @@ + +========================= +Documentation style guide +========================= + +This guide contains best practices for the language and formatting of Matplotlib +documentation. + +.. seealso:: + + For more information about contributing, see the :ref:`documenting-matplotlib` + section. + +Expository language +=================== + +For explanatory writing, the following guidelines are for clear and concise +language use. + +Terminology +----------- + +There are several key terms in Matplotlib that are standards for +reliability and consistency in documentation. They are not interchangeable. + +.. table:: + :widths: 15, 15, 35, 35 + + +------------------+--------------------------+--------------+--------------+ + | Term | Description | Correct | Incorrect | + +==================+==========================+==============+==============+ + | |Figure| | Matplotlib working space | - *For | - "The figure| + | | for programming. | Matplotlib | is the | + | | | objects*: | working | + | | | Figure, | space for | + | | | "The Figure| visuals." | + | | | is the | - "Methods in| + | | | working | the figure | + | | | space for | provide the| + | | | the visual.| visuals." | + | | | - *Referring | - "The | + | | | to class*: | |Figure| | + | | | |Figure|, | Four | + | | | "Methods | leglock is | + | | | within the | a wrestling| + | | | |Figure| | move." | + | | | provide the| | + | | | visuals." | | + | | | - *General | | + | | | language*: | | + | | | figure, | | + | | | "Michelle | | + | | | Kwan is a | | + | | | famous | | + | | | figure | | + | | | skater." | | + +------------------+--------------------------+--------------+--------------+ + | |Axes| | Subplots within Figure. | - *For | - "The axes | + | | Contains plot elements | Matplotlib | methods | + | | and is responsible for | objects*: | transform | + | | plotting and configuring | Axes, "An | the data." | + | | additional details. | Axes is a | - "Each | + | | | subplot | |Axes| is | + | | | within the | specific to| + | | | Figure." | a Figure." | + | | | - *Referring | - "The | + | | | to class*: | musicians | + | | | |Axes|, | on stage | + | | | "Each | call their | + | | | |Axes| is | guitars | + | | | specific to| Axes." | + | | | one | - "The point | + | | | Figure." | where the | + | | | - *General | Axes meet | + | | | language*: | is the | + | | | axes, "Both| origin of | + | | | loggers and| the | + | | | lumberjacks| coordinate | + | | | use axes to| system." | + | | | chop wood."| | + | | | OR "There | | + | | | are no | | + | | | standard | | + | | | names for | | + | | | the | | + | | | coordinates| | + | | | in the | | + | | | three | | + | | | axes." | | + | | | (Plural of | | + | | | axis) | | + +------------------+--------------------------+--------------+--------------+ + | |Artist| | Broad variety of | - *For | - "Configure | + | | Matplotlib objects that | Matplotlib | the legend | + | | display visuals. | objects*: | artist with| + | | | Artist, | its | + | | | "Artists | respective | + | | | display | method." | + | | | visuals and| - "There is | + | | | are the | an | + | | | visible | |Artist| | + | | | elements | for that | + | | | when | visual in | + | | | rendering a| the graph."| + | | | Figure." | - "Some | + | | | - *Referring | Artists | + | | | to class*: | became | + | | | |Artist| , | famous only| + | | | "Each | by | + | | | |Artist| | accident." | + | | | has | | + | | | respective | | + | | | methods and| | + | | | functions."| | + | | | - *General | | + | | | language*: | | + | | | artist, | | + | | | "The | | + | | | artist in | | + | | | the museum | | + | | | is from | | + | | | France." | | + +------------------+--------------------------+--------------+--------------+ + | |Axis| | Human-readable single | - *For | - "Plot the | + | | dimensional object | Matplotlib | graph onto | + | | of reference marks | objects*: | the axis." | + | | containing ticks, tick | Axis, "The | - "Each Axis | + | | labels, spines, and | Axis for | is usually | + | | edges. | the bar | named after| + | | | chart is a | the | + | | | separate | coordinate | + | | | Artist." | which is | + | | | (plural, | measured | + | | | Axis | along it." | + | | | objects) | - "In some | + | | | - *Referring | computer | + | | | to class*: | graphics | + | | | |Axis|, | contexts, | + | | | "The | the | + | | | |Axis| | ordinate | + | | | contains | |Axis| may | + | | | respective | be oriented| + | | | XAxis and | downwards."| + | | | YAxis | | + | | | objects." | | + | | | - *General | | + | | | language*: | | + | | | axis, | | + | | | "Rotation | | + | | | around a | | + | | | fixed axis | | + | | | is a | | + | | | special | | + | | | case of | | + | | | rotational | | + | | | motion." | | + +------------------+--------------------------+--------------+--------------+ + | Explicit, | Explicit approach of | - Explicit | - object | + | Object Oriented | programming in | - explicit | oriented | + | Programming (OOP)| Matplotlib. | - OOP | - OO-style | + +------------------+--------------------------+--------------+--------------+ + | Implicit, | Implicit approach of | - Implicit | - MATLAB like| + | ``pyplot`` | programming in Matplotlib| - implicit | - Pyplot | + | | with ``pyplot`` module. | - ``pyplot`` | - pyplot | + | | | | interface | + +------------------+--------------------------+--------------+--------------+ + +.. |Figure| replace:: :class:`~matplotlib.figure.Figure` +.. |Axes| replace:: :class:`~matplotlib.axes.Axes` +.. |Artist| replace:: :class:`~matplotlib.artist.Artist` +.. |Axis| replace:: :class:`~matplotlib.axis.Axis` + + +Grammar +------- + +Subject +^^^^^^^ +Use second-person imperative sentences for directed instructions specifying an +action. Second-person pronouns are for individual-specific contexts and +possessive reference. + +.. table:: + :width: 100% + :widths: 50, 50 + + +------------------------------------+------------------------------------+ + | Correct | Incorrect | + +====================================+====================================+ + | Install Matplotlib from the source | You can install Matplotlib from the| + | directory using the Python ``pip`` | source directory. You can find | + | installer program. Depending on | additional support if you are | + | your operating system, you may need| having trouble with your | + | additional support. | installation. | + +------------------------------------+------------------------------------+ + +Tense +^^^^^ +Use present simple tense for explanations. Avoid future tense and other modal +or auxiliary verbs when possible. + +.. table:: + :width: 100% + :widths: 50, 50 + + +------------------------------------+------------------------------------+ + | Correct | Incorrect | + +====================================+====================================+ + | The fundamental ideas behind | Matplotlib will take data and | + | Matplotlib for visualization | transform it through functions and | + | involve taking data and | methods. They can generate many | + | transforming it through functions | kinds of visuals. These will be the| + | and methods. | fundamentals for using Matplotlib. | + +------------------------------------+------------------------------------+ + +Voice +^^^^^ +Write in active sentences. Passive voice is best for situations or conditions +related to warning prompts. + +.. table:: + :width: 100% + :widths: 50, 50 + + +------------------------------------+------------------------------------+ + | Correct | Incorrect | + +====================================+====================================+ + | The function ``plot`` generates the| The graph is generated by the | + | graph. | ``plot`` function. | + +------------------------------------+------------------------------------+ + | An error message is returned by the| You will see an error message from | + | function if there are no arguments.| the function if there are no | + | | arguments. | + +------------------------------------+------------------------------------+ + +Sentence structure +^^^^^^^^^^^^^^^^^^ +Write with short sentences using Subject-Verb-Object order regularly. Limit +coordinating conjunctions in sentences. Avoid pronoun references and +subordinating conjunctive phrases. + +.. table:: + :width: 100% + :widths: 50, 50 + + +------------------------------------+------------------------------------+ + | Correct | Incorrect | + +====================================+====================================+ + | The ``pyplot`` module in Matplotlib| The ``pyplot`` module in Matplotlib| + | is a collection of functions. These| is a collection of functions which | + | functions create, manage, and | create, manage, and manipulate the | + | manipulate the current Figure and | current Figure and plotting area. | + | plotting area. | | + +------------------------------------+------------------------------------+ + | The ``plot`` function plots data | The ``plot`` function plots data | + | to the respective Axes. The Axes | within its respective Axes for its | + | corresponds to the respective | respective Figure. | + | Figure. | | + +------------------------------------+------------------------------------+ + | The implicit approach is a | Users that wish to have convenient | + | convenient shortcut for | shortcuts for generating plots use | + | generating simple plots. | the implicit approach. | + +------------------------------------+------------------------------------+ + + +Formatting +========== + +The following guidelines specify how to incorporate code and use appropriate +formatting for Matplotlib documentation. + +Code +---- + +Matplotlib is a Python library and follows the same standards for +documentation. + +Comments +^^^^^^^^ +Examples of Python code have comments before or on the same line. + +.. table:: + :width: 100% + :widths: 50, 50 + + +---------------------------------------+---------------------------------+ + | Correct | Incorrect | + +=======================================+=================================+ + | :: | :: | + | | | + | # Data | years = [2006, 2007, 2008] | + | years = [2006, 2007, 2008] | # Data | + +---------------------------------------+ | + | :: | | + | | | + | years = [2006, 2007, 2008] # Data | | + +---------------------------------------+---------------------------------+ + +Outputs +^^^^^^^ +When generating visuals with Matplotlib using ``.py`` files in examples, +display the visual with `matplotlib.pyplot.show` to display the visual. +Keep the documentation clear of Python output lines. + +.. table:: + :width: 100% + :widths: 50, 50 + + +------------------------------------+------------------------------------+ + | Correct | Incorrect | + +====================================+====================================+ + | :: | :: | + | | | + | plt.plot([1, 2, 3], [1, 2, 3]) | plt.plot([1, 2, 3], [1, 2, 3]) | + | plt.show() | | + +------------------------------------+------------------------------------+ + | :: | :: | + | | | + | fig, ax = plt.subplots() | fig, ax = plt.subplots() | + | ax.plot([1, 2, 3], [1, 2, 3]) | ax.plot([1, 2, 3], [1, 2, 3]) | + | fig.show() | | + +------------------------------------+------------------------------------+ + +reStructuredText +---------------- + +Matplotlib uses reStructuredText Markup for documentation. Sphinx helps to +transform these documents into appropriate formats for accessibility and +visibility. + +- `reStructuredText Specifications `_ +- `Quick Reference Document `_ + + +Lists +^^^^^ +Bulleted lists are for items that do not require sequencing. Numbered lists are +exclusively for performing actions in a determined order. + +.. table:: + :width: 100% + :widths: 50, 50 + + +------------------------------------+------------------------------------+ + | Correct | Incorrect | + +====================================+====================================+ + | The example uses three graphs. | The example uses three graphs. | + +------------------------------------+------------------------------------+ + | - Bar | 1. Bar | + | - Line | 2. Line | + | - Pie | 3. Pie | + +------------------------------------+------------------------------------+ + | These four steps help to get | The following steps are important | + | started using Matplotlib. | to get started using Matplotlib. | + +------------------------------------+------------------------------------+ + | 1. Import the Matplotlib library. | - Import the Matplotlib library. | + | 2. Import the necessary modules. | - Import the necessary modules. | + | 3. Set and assign data to work on.| - Set and assign data to work on. | + | 4. Transform data with methods and| - Transform data with methods and | + | functions. | functions. | + +------------------------------------+------------------------------------+ + +Tables +^^^^^^ +Use ASCII tables with reStructuredText standards in organizing content. +Markdown tables and the csv-table directive are not accepted. + +.. table:: + :width: 100% + :widths: 50, 50 + + +--------------------------------+----------------------------------------+ + | Correct | Incorrect | + +================================+========================================+ + | +----------+----------+ | :: | + | | Correct | Incorrect| | | + | +==========+==========+ | | Correct | Incorrect | | + | | OK | Not OK | | | ------- | --------- | | + | +----------+----------+ | | OK | Not OK | | + | | | + +--------------------------------+----------------------------------------+ + | :: | :: | + | | | + | +----------+----------+ | .. csv-table:: | + | | Correct | Incorrect| | :header: "correct", "incorrect" | + | +==========+==========+ | :widths: 10, 10 | + | | OK | Not OK | | | + | +----------+----------+ | "OK ", "Not OK" | + | | | + +--------------------------------+ | + | :: | | + | | | + | =========== =========== | | + | Correct Incorrect | | + | =========== =========== | | + | OK Not OK | | + | =========== =========== | | + | | | + +--------------------------------+----------------------------------------+ + + +Additional resources +==================== +This style guide is not a comprehensive standard. For a more thorough +reference of how to contribute to documentation, see the links below. These +resources contain common best practices for writing documentation. + +* `Python Developer's Guide `_ +* `Google Developer Style Guide `_ +* `IBM Style Guide `_ +* `Red Hat Style Guide `_ diff --git a/doc/devel/testing.rst b/doc/devel/testing.rst index 49c27002b058..945569310b48 100644 --- a/doc/devel/testing.rst +++ b/doc/devel/testing.rst @@ -37,7 +37,7 @@ Running the tests In the root directory of your development repository run:: - pytest + python -m pytest pytest can be configured via a lot of `command-line parameters`_. Some @@ -57,14 +57,6 @@ not need to be installed, but Matplotlib should be):: pytest lib/matplotlib/tests/test_simplification.py::test_clipping -An alternative implementation that does not look at command line arguments -and works from within Python is to run the tests from the Matplotlib library -function :func:`matplotlib.test`:: - - import matplotlib - matplotlib.test() - - .. _command-line parameters: http://doc.pytest.org/en/latest/usage.html @@ -95,10 +87,12 @@ Random data in tests Random data is a very convenient way to generate data for examples, however the randomness is problematic for testing (as the tests must be deterministic!). To work around this set the seed in each test. -For numpy use:: +For numpy's default random number generator use:: import numpy as np - np.random.seed(19680801) + rng = np.random.default_rng(19680801) + +and then use ``rng`` when generating the random numbers. The seed is John Hunter's birthday. @@ -165,7 +159,9 @@ workflows GitHub Actions should be automatically enabled for your personal Matplotlib fork once the YAML workflow files are in it. It generally isn't necessary to look at these workflows, since any pull request submitted against the main -Matplotlib repository will be tested. +Matplotlib repository will be tested. The Tests workflow is skipped in forked +repositories but you can trigger a run manually from the `GitHub web interface +`_. You can see the GitHub Actions results at https://github.com/your_GitHub_user_name/matplotlib/actions -- here's `an @@ -177,7 +173,7 @@ Using tox `Tox `_ is a tool for running tests against multiple Python environments, including multiple versions of Python -(e.g., 3.6, 3.7) and even different Python implementations altogether +(e.g., 3.7, 3.8) and even different Python implementations altogether (e.g., CPython, PyPy, Jython, etc.), as long as all these versions are available on your system's $PATH (consider using your system package manager, e.g. apt-get, yum, or Homebrew, to install them). @@ -194,7 +190,7 @@ You can also run tox on a subset of environments: .. code-block:: bash - $ tox -e py37,py38 + $ tox -e py38,py39 Tox processes everything serially so it can take a long time to test several environments. To speed it up, you might try using a new, @@ -252,16 +248,15 @@ The correct target folder can be found using:: python -c "import matplotlib.tests; print(matplotlib.tests.__file__.rsplit('/', 1)[0])" An analogous copying of :file:`lib/mpl_toolkits/tests/baseline_images` -is necessary for testing the :ref:`toolkits`. +is necessary for testing ``mpl_toolkits``. Run the tests ^^^^^^^^^^^^^ To run the all the tests on your installed version of Matplotlib:: - pytest --pyargs matplotlib.tests + python -m pytest --pyargs matplotlib.tests The test discovery scope can be narrowed to single test modules or even single functions:: - pytest --pyargs matplotlib.tests.test_simplification.py::test_clipping - + python -m pytest --pyargs matplotlib.tests.test_simplification.py::test_clipping diff --git a/doc/devel/triage.rst b/doc/devel/triage.rst old mode 100644 new mode 100755 index b57947b049b7..7b09da2488f7 --- a/doc/devel/triage.rst +++ b/doc/devel/triage.rst @@ -16,7 +16,7 @@ internals of Matplotlib, is extremely valuable to the project, and we welcome anyone to participate in issue triage! However, people who are not part of the Matplotlib organization do not have `permissions to change milestones, add labels, or close issue -`_. +`_. If you do not have enough GitHub permissions do something (e.g. add a label, close an issue), please leave a comment tagging ``@matplotlib/triageteam`` with your recommendations! @@ -62,7 +62,7 @@ The following actions are typically useful: explores how to lead online discussions in the context of open source. -Triage Team +Triage team ----------- @@ -208,7 +208,7 @@ The following workflow [1]_ is a good way to approach issue triaging: .. [1] Adapted from the pandas project `maintainers guide - `_ and + `_ and `the scikit-learn project `_ . @@ -223,5 +223,5 @@ participate to the review process following our :ref:`review guidelines Acknowledgments --------------- -This page is lightly adapted from `the sckit-learn project +This page is lightly adapted from `the scikit-learn project `_ . diff --git a/doc/faq/howto_faq.rst b/doc/faq/howto_faq.rst deleted file mode 100644 index 62ca027d399c..000000000000 --- a/doc/faq/howto_faq.rst +++ /dev/null @@ -1,528 +0,0 @@ -.. _howto-faq: - -****** -How-to -****** - -.. contents:: - :backlinks: none - - -.. _howto-plotting: - -How-to: Plotting -================ - -.. _howto-datetime64: - -Plot `numpy.datetime64` values ------------------------------- - -As of Matplotlib 2.2, `numpy.datetime64` objects are handled the same way -as `datetime.datetime` objects. - -If you prefer the pandas converters and locators, you can register them. This -is done automatically when calling a pandas plot function and may be -unnecessary when using pandas instead of Matplotlib directly. :: - - from pandas.plotting import register_matplotlib_converters - register_matplotlib_converters() - - -.. _howto-figure-empty: - -Check whether a figure is empty -------------------------------- -Empty can actually mean different things. Does the figure contain any artists? -Does a figure with an empty `~.axes.Axes` still count as empty? Is the figure -empty if it was rendered pure white (there may be artists present, but they -could be outside the drawing area or transparent)? - -For the purpose here, we define empty as: "The figure does not contain any -artists except it's background patch." The exception for the background is -necessary, because by default every figure contains a `.Rectangle` as it's -background patch. This definition could be checked via:: - - def is_empty(figure): - """ - Return whether the figure contains no Artists (other than the default - background patch). - """ - contained_artists = figure.get_children() - return len(contained_artists) <= 1 - -We've decided not to include this as a figure method because this is only one -way of defining empty, and checking the above is only rarely necessary. -Usually the user or program handling the figure know if they have added -something to the figure. - -Checking whether a figure would render empty cannot be reliably checked except -by actually rendering the figure and investigating the rendered result. - -.. _howto-findobj: - -Find all objects in a figure of a certain type ----------------------------------------------- - -Every Matplotlib artist (see :doc:`/tutorials/intermediate/artists`) has a method -called :meth:`~matplotlib.artist.Artist.findobj` that can be used to -recursively search the artist for any artists it may contain that meet -some criteria (e.g., match all :class:`~matplotlib.lines.Line2D` -instances or match some arbitrary filter function). For example, the -following snippet finds every object in the figure which has a -``set_color`` property and makes the object blue:: - - def myfunc(x): - return hasattr(x, 'set_color') - - for o in fig.findobj(myfunc): - o.set_color('blue') - -You can also filter on class instances:: - - import matplotlib.text as text - for o in fig.findobj(text.Text): - o.set_fontstyle('italic') - - -.. _howto-supress_offset: - -How to prevent ticklabels from having an offset ------------------------------------------------ -The default formatter will use an offset to reduce -the length of the ticklabels. To turn this feature -off on a per-axis basis:: - - ax.get_xaxis().get_major_formatter().set_useOffset(False) - -set :rc:`axes.formatter.useoffset`, or use a different -formatter. See :mod:`~matplotlib.ticker` for details. - -.. _howto-transparent: - -Save transparent figures ------------------------- - -The :meth:`~matplotlib.pyplot.savefig` command has a keyword argument -*transparent* which, if 'True', will make the figure and axes -backgrounds transparent when saving, but will not affect the displayed -image on the screen. - -If you need finer grained control, e.g., you do not want full transparency -or you want to affect the screen displayed version as well, you can set -the alpha properties directly. The figure has a -:class:`~matplotlib.patches.Rectangle` instance called *patch* -and the axes has a Rectangle instance called *patch*. You can set -any property on them directly (*facecolor*, *edgecolor*, *linewidth*, -*linestyle*, *alpha*). e.g.:: - - fig = plt.figure() - fig.patch.set_alpha(0.5) - ax = fig.add_subplot(111) - ax.patch.set_alpha(0.5) - -If you need *all* the figure elements to be transparent, there is -currently no global alpha setting, but you can set the alpha channel -on individual elements, e.g.:: - - ax.plot(x, y, alpha=0.5) - ax.set_xlabel('volts', alpha=0.5) - - -.. _howto-multipage: - -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 - - -.. _howto-subplots-adjust: - -Move the edge of an axes to make room for tick labels ------------------------------------------------------ - -For subplots, you can control the default spacing on the left, right, -bottom, and top as well as the horizontal and vertical spacing between -multiple rows and columns using the -:meth:`matplotlib.figure.Figure.subplots_adjust` method (in pyplot it -is :func:`~matplotlib.pyplot.subplots_adjust`). For example, to move -the bottom of the subplots up to make room for some rotated x tick -labels:: - - fig = plt.figure() - fig.subplots_adjust(bottom=0.2) - ax = fig.add_subplot(111) - -You can control the defaults for these parameters in your -:file:`matplotlibrc` file; see :doc:`/tutorials/introductory/customizing`. For -example, to make the above setting permanent, you would set:: - - figure.subplot.bottom : 0.2 # the bottom of the subplots of the figure - -The other parameters you can configure are, with their defaults - -*left* = 0.125 - the left side of the subplots of the figure -*right* = 0.9 - the right side of the subplots of the figure -*bottom* = 0.1 - the bottom of the subplots of the figure -*top* = 0.9 - the top of the subplots of the figure -*wspace* = 0.2 - the amount of width reserved for space between subplots, - expressed as a fraction of the average axis width -*hspace* = 0.2 - the amount of height reserved for space between subplots, - expressed as a fraction of the average axis height - -If you want additional control, you can create an -:class:`~matplotlib.axes.Axes` using the -:func:`~matplotlib.pyplot.axes` command (or equivalently the figure -:meth:`~matplotlib.figure.Figure.add_axes` method), which allows you to -specify the location explicitly:: - - ax = fig.add_axes([left, bottom, width, height]) - -where all values are in fractional (0 to 1) coordinates. See -:doc:`/gallery/subplots_axes_and_figures/axes_demo` for an example of -placing axes manually. - -.. _howto-auto-adjust: - -Automatically make room for tick labels ---------------------------------------- - -.. note:: - This is now easier to handle than ever before. - Calling :func:`~matplotlib.pyplot.tight_layout` or alternatively using - ``constrained_layout=True`` argument in :func:`~matplotlib.pyplot.subplots` - can fix many common layout issues. See the - :doc:`/tutorials/intermediate/tight_layout_guide` and - :doc:`/tutorials/intermediate/constrainedlayout_guide` for more details. - - The information below is kept here in case it is useful for other - purposes. - -In most use cases, it is enough to simply change the subplots adjust -parameters as described in :ref:`howto-subplots-adjust`. But in some -cases, you don't know ahead of time what your tick labels will be, or -how large they will be (data and labels outside your control may be -being fed into your graphing application), and you may need to -automatically adjust your subplot parameters based on the size of the -tick labels. Any :class:`~matplotlib.text.Text` instance can report -its extent in window coordinates (a negative x coordinate is outside -the window), but there is a rub. - -The :class:`~matplotlib.backend_bases.RendererBase` instance, which is -used to calculate the text size, is not known until the figure is -drawn (:meth:`~matplotlib.figure.Figure.draw`). After the window is -drawn and the text instance knows its renderer, you can call -:meth:`~matplotlib.text.Text.get_window_extent`. One way to solve -this chicken and egg problem is to wait until the figure is draw by -connecting -(:meth:`~matplotlib.backend_bases.FigureCanvasBase.mpl_connect`) to the -"on_draw" signal (:class:`~matplotlib.backend_bases.DrawEvent`) and -get the window extent there, and then do something with it, e.g., move -the left of the canvas over; see :ref:`event-handling-tutorial`. - -Here is an example that gets a bounding box in relative figure coordinates -(0..1) of each of the labels and uses it to move the left of the subplots -over so that the tick labels fit in the figure: - -.. figure:: ../gallery/pyplots/images/sphx_glr_auto_subplots_adjust_001.png - :target: ../gallery/pyplots/auto_subplots_adjust.html - :align: center - :scale: 50 - - Auto Subplots Adjust - -.. _howto-ticks: - -Configure the tick widths -------------------------- - -Wherever possible, it is recommended to use the :meth:`~.axes.Axes.tick_params` -or :meth:`~.axis.Axis.set_tick_params` methods to modify tick properties:: - - import matplotlib.pyplot as plt - - fig, ax = plt.subplots() - ax.plot(range(10)) - - ax.tick_params(width=10) - - plt.show() - -For more control of tick properties that are not provided by the above methods, -it is important to know that in Matplotlib, the ticks are *markers*. All -:class:`~matplotlib.lines.Line2D` objects support a line (solid, dashed, etc) -and a marker (circle, square, tick). The tick width is controlled by the -``"markeredgewidth"`` property, so the above effect can also be achieved by:: - - import matplotlib.pyplot as plt - - fig, ax = plt.subplots() - ax.plot(range(10)) - - for line in ax.get_xticklines() + ax.get_yticklines(): - line.set_markeredgewidth(10) - - plt.show() - -The other properties that control the tick marker, and all markers, -are ``markerfacecolor``, ``markeredgecolor``, ``markeredgewidth``, -``markersize``. For more information on configuring ticks, see -:ref:`axis-container` and :ref:`tick-container`. - - -.. _howto-align-label: - -Align my ylabels across multiple subplots ------------------------------------------ - -If you have multiple subplots over one another, and the y data have -different scales, you can often get ylabels that do not align -vertically across the multiple subplots, which can be unattractive. -By default, Matplotlib positions the x location of the ylabel so that -it does not overlap any of the y ticks. You can override this default -behavior by specifying the coordinates of the label. The example -below shows the default behavior in the left subplots, and the manual -setting in the right subplots. - -.. figure:: ../gallery/pyplots/images/sphx_glr_align_ylabels_001.png - :target: ../gallery/pyplots/align_ylabels.html - :align: center - :scale: 50 - - Align Ylabels - -.. _date-index-plots: - -Skip dates where there is no data ---------------------------------- - -When plotting time series, e.g., financial time series, one often wants to -leave out days on which there is no data, e.g., weekends. By passing in -dates on the x-xaxis, you get large horizontal gaps on periods when there -is not data. The solution is to pass in some proxy x-data, e.g., evenly -sampled indices, and then use a custom formatter to format these as dates. -:doc:`/gallery/text_labels_and_annotations/date_index_formatter` demonstrates -how to use an 'index formatter' to achieve the desired plot. - -.. _howto-set-zorder: - -Control the depth of plot elements ----------------------------------- - - -Within an axes, the order that the various lines, markers, text, -collections, etc appear is determined by the -:meth:`~matplotlib.artist.Artist.set_zorder` property. The default -order is patches, lines, text, with collections of lines and -collections of patches appearing at the same level as regular lines -and patches, respectively:: - - line, = ax.plot(x, y, zorder=10) - -.. only:: html - - See :doc:`/gallery/misc/zorder_demo` for a complete example. - -You can also use the Axes property -:meth:`~matplotlib.axes.Axes.set_axisbelow` to control whether the grid -lines are placed above or below your other plot elements. - -.. _howto-axis-equal: - -Make the aspect ratio for plots equal -------------------------------------- - -The Axes property :meth:`~matplotlib.axes.Axes.set_aspect` controls the -aspect ratio of the axes. You can set it to be 'auto', 'equal', or -some ratio which controls the ratio:: - - ax = fig.add_subplot(111, aspect='equal') - -.. only:: html - - See :doc:`/gallery/subplots_axes_and_figures/axis_equal_demo` for a - complete example. - -.. _howto-twoscale: - -Multiple y-axis scales ----------------------- - -A frequent request is to have two scales for the left and right -y-axis, which is possible using :func:`~matplotlib.pyplot.twinx` (more -than two scales are not currently supported, though it is on the wish -list). This works pretty well, though there are some quirks when you -are trying to interactively pan and zoom, because both scales do not get -the signals. - -The approach uses :func:`~matplotlib.pyplot.twinx` (and its sister -:func:`~matplotlib.pyplot.twiny`) to use *2 different axes*, -turning the axes rectangular frame off on the 2nd axes to keep it from -obscuring the first, and manually setting the tick locs and labels as -desired. You can use separate ``matplotlib.ticker`` formatters and -locators as desired because the two axes are independent. - -.. plot:: - - import numpy as np - import matplotlib.pyplot as plt - - fig = plt.figure() - ax1 = fig.add_subplot(111) - t = np.arange(0.01, 10.0, 0.01) - s1 = np.exp(t) - ax1.plot(t, s1, 'b-') - ax1.set_xlabel('time (s)') - ax1.set_ylabel('exp') - - ax2 = ax1.twinx() - s2 = np.sin(2*np.pi*t) - ax2.plot(t, s2, 'r.') - ax2.set_ylabel('sin') - plt.show() - - -.. only:: html - - See :doc:`/gallery/subplots_axes_and_figures/two_scales` for a - complete example. - -.. _howto-batch: - -Generate images without having a window appear ----------------------------------------------- - -Simply do not call `~matplotlib.pyplot.show`, and directly save the figure to -the desired format:: - - import matplotlib.pyplot as plt - plt.plot([1, 2, 3]) - plt.savefig('myfig.png') - -.. seealso:: - - :doc:`/gallery/user_interfaces/web_application_server_sgskip` for - information about running matplotlib inside of a web application. - -.. _howto-show: - -Use :func:`~matplotlib.pyplot.show` ------------------------------------ - -When you want to view your plots on your display, -the user interface backend will need to start the GUI mainloop. -This is what :func:`~matplotlib.pyplot.show` does. It tells -Matplotlib to raise all of the figure windows created so far and start -the mainloop. Because this mainloop is blocking by default (i.e., script -execution is paused), you should only call this once per script, at the end. -Script execution is resumed after the last window is closed. Therefore, if -you are using Matplotlib to generate only images and do not want a user -interface window, you do not need to call ``show`` (see :ref:`howto-batch` -and :ref:`what-is-a-backend`). - -.. note:: - Because closing a figure window unregisters it from pyplot, you must call - `~matplotlib.pyplot.savefig` *before* calling ``show`` if you wish to save - the figure as well as view it. - -Whether ``show`` blocks further execution of the script or the python -interpreter depends on whether Matplotlib is set to use interactive mode. -In non-interactive mode (the default setting), execution is paused -until the last figure window is closed. In interactive mode, the execution -is not paused, which allows you to create additional figures (but the script -won't finish until the last figure window is closed). - -Because it is expensive to draw, you typically will not want Matplotlib -to redraw a figure many times in a script such as the following:: - - plot([1, 2, 3]) # draw here? - xlabel('time') # and here? - ylabel('volts') # and here? - title('a simple plot') # and here? - show() - -However, it is *possible* to force Matplotlib to draw after every command, -which might be what you want when working interactively at the -python console (see :ref:`mpl-shell`), but in a script you want to -defer all drawing until the call to ``show``. This is especially -important for complex figures that take some time to draw. -:func:`~matplotlib.pyplot.show` is designed to tell Matplotlib that -you're all done issuing commands and you want to draw the figure now. - -.. note:: - - :func:`~matplotlib.pyplot.show` should typically only be called at - most once per script and it should be the last line of your - script. At that point, the GUI takes control of the interpreter. - If you want to force a figure draw, use - :func:`~matplotlib.pyplot.draw` instead. - -.. versionadded:: v1.0.0 - Matplotlib 1.0.0 and 1.0.1 added support for calling ``show`` multiple times - per script, and harmonized the behavior of interactive mode, across most - backends. - -.. _howto-boxplot_violinplot: - -Interpreting box plots and violin plots ---------------------------------------- - -Tukey's :doc:`box plots ` (Robert McGill, -John W. Tukey and Wayne A. Larsen: "The American Statistician" Vol. 32, No. 1, -Feb., 1978, pp. 12-16) are statistical plots that provide useful information -about the data distribution such as skewness. However, bar plots with error -bars are still the common standard in most scientific literature, and thus, the -interpretation of box plots can be challenging for the unfamiliar reader. The -figure below illustrates the different visual features of a box plot. - -.. figure:: ../_static/boxplot_explanation.png - -:doc:`Violin plots ` are closely related to box -plots but add useful information such as the distribution of the sample data -(density trace). Violin plots were added in Matplotlib 1.4. - -.. _how-to-threads: - -Working with threads --------------------- - -Matplotlib is not thread-safe: in fact, there are known race conditions -that affect certain artists. Hence, if you work with threads, it is your -responsibility to set up the proper locks to serialize access to Matplotlib -artists. - -You may be able to work on separate figures from separate threads. However, -you must in that case use a *non-interactive backend* (typically Agg), because -most GUI backends *require* being run from the main thread as well. diff --git a/doc/faq/installing_faq.rst b/doc/faq/installing_faq.rst deleted file mode 100644 index e1b97a505840..000000000000 --- a/doc/faq/installing_faq.rst +++ /dev/null @@ -1,158 +0,0 @@ -.. _installing-faq: - -************* - Installation -************* - -.. contents:: - :backlinks: none - -Report a compilation problem -============================ - -See :ref:`reporting-problems`. - -Matplotlib compiled fine, but nothing shows up when I use it -============================================================ - -The first thing to try is a :ref:`clean install ` and see if -that helps. If not, the best way to test your install is by running a script, -rather than working interactively from a python shell or an integrated -development environment such as :program:`IDLE` which add additional -complexities. Open up a UNIX shell or a DOS command prompt and run, for -example:: - - python -c "from pylab import *; set_loglevel('debug'); plot(); show()" - -This will give you additional information about which backends Matplotlib is -loading, version information, and more. At this point you might want to make -sure you understand Matplotlib's :doc:`configuration ` -process, governed by the :file:`matplotlibrc` configuration file which contains -instructions within and the concept of the Matplotlib backend. - -If you are still having trouble, see :ref:`reporting-problems`. - -.. _clean-install: - -How to completely remove Matplotlib -=================================== - -Occasionally, problems with Matplotlib can be solved with a clean -installation of the package. In order to fully remove an installed Matplotlib: - -1. Delete the caches from your :ref:`Matplotlib configuration directory - `. - -2. Delete any Matplotlib directories or eggs from your :ref:`installation - directory `. - -Linux Notes -=========== - -To install Matplotlib at the system-level, we recommend that you use your -distribution's package manager. This will guarantee that Matplotlib's -dependencies will be installed as well. - -If, for some reason, you cannot use the package manager, you may use the wheels -available on PyPI:: - - python -m pip install matplotlib - -or :ref:`build Matplotlib from source `. - -OSX Notes -========= - -.. _which-python-for-osx: - -Which python for OSX? ---------------------- - -Apple ships OSX with its own Python, in ``/usr/bin/python``, and its own copy -of Matplotlib. Unfortunately, the way Apple currently installs its own copies -of NumPy, Scipy and Matplotlib means that these packages are difficult to -upgrade (see `system python packages`_). For that reason we strongly suggest -that you install a fresh version of Python and use that as the basis for -installing libraries such as NumPy and Matplotlib. One convenient way to -install Matplotlib with other useful Python software is to use the Anaconda_ -Python scientific software collection, which includes Python itself and a -wide range of libraries; if you need a library that is not available from the -collection, you can install it yourself using standard methods such as *pip*. -See the Ananconda web page for installation support. - -.. _system python packages: - https://github.com/MacPython/wiki/wiki/Which-Python#system-python-and-extra-python-packages -.. _Anaconda: https://www.anaconda.com/ - -Other options for a fresh Python install are the standard installer from -`python.org `_, or installing -Python using a general OSX package management system such as `homebrew -`_ or `macports `_. Power users on -OSX will likely want one of homebrew or macports on their system to install -open source software packages, but it is perfectly possible to use these -systems with another source for your Python binary, such as Anaconda -or Python.org Python. - -.. _install_osx_binaries: - -Installing OSX binary wheels ----------------------------- - -If you are using Python from https://www.python.org, Homebrew, or Macports, -then you can use the standard pip installer to install Matplotlib binaries in -the form of wheels. - -pip is installed by default with python.org and Homebrew Python, but needs to -be manually installed on Macports with :: - - sudo port install py38-pip - -Once pip is installed, you can install Matplotlib and all its dependencies with -from the Terminal.app command line:: - - python3 -mpip install matplotlib - -(``sudo python3.6 ...`` on Macports). - -You might also want to install IPython or the Jupyter notebook (``python3 -mpip -install ipython notebook``). - -Checking your installation --------------------------- - -The new version of Matplotlib should now be on your Python "path". Check this -at the Terminal.app command line:: - - python3 -c 'import matplotlib; print(matplotlib.__version__, matplotlib.__file__)' - -You should see something like :: - - 3.0.0 /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/matplotlib/__init__.py - -where ``3.0.0`` is the Matplotlib version you just installed, and the path -following depends on whether you are using Python.org Python, Homebrew or -Macports. If you see another version, or you get an error like :: - - Traceback (most recent call last): - File "", line 1, in - ImportError: No module named matplotlib - -then check that the Python binary is the one you expected by running :: - - which python3 - -If you get a result like ``/usr/bin/python...``, then you are getting the -Python installed with OSX, which is probably not what you want. Try closing -and restarting Terminal.app before running the check again. If that doesn't fix -the problem, depending on which Python you wanted to use, consider reinstalling -Python.org Python, or check your homebrew or macports setup. Remember that -the disk image installer only works for Python.org Python, and will not get -picked up by other Pythons. If all these fail, please :ref:`let us know -`. - -.. _install-from-git: - -Install from source -=================== - -See :ref:`install_from_source`. \ No newline at end of file diff --git a/doc/index.rst b/doc/index.rst index 2d08c108a161..d83f09de6b08 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -1,220 +1,111 @@ :orphan: -.. title:: Matplotlib: Python plotting +.. title:: Matplotlib documentation -Matplotlib: Visualization with Python -------------------------------------- +.. module:: matplotlib + + +################################## +Matplotlib |release| documentation +################################## Matplotlib is a comprehensive library for creating static, animated, and interactive visualizations in Python. -.. raw:: html - - - - -Matplotlib makes easy things easy and hard things possible. - -.. container:: bullet-box-container - - .. container:: bullet-box - - Create - - - Develop `publication quality plots`_ with just a few lines of code - - Use `interactive figures`_ that can zoom, pan, update... - - .. _publication quality plots: https://matplotlib.org/gallery/index.html - .. _interactive figures: https://matplotlib.org/gallery/index.html#event-handling - - .. container:: bullet-box - - Customize - - - `Take full control`_ of line styles, font properties, axes properties... - - `Export and embed`_ to a number of file formats and interactive environments - - .. _Take full control: https://matplotlib.org/tutorials/index.html#tutorials - .. _Export and embed: https://matplotlib.org/api/index_backend_api.html - - .. container:: bullet-box - - Extend - - - Explore tailored functionality provided by - :doc:`third party packages ` - - Learn more about Matplotlib through the many - :doc:`external learning resources ` - -Documentation -~~~~~~~~~~~~~ - -To get started, read the :doc:`User's Guide `. - -Trying to learn how to do a particular kind of plot? Check out the -:doc:`examples gallery ` or the :doc:`list of plotting commands -`. - -Join our community! -~~~~~~~~~~~~~~~~~~~ - -Matplotlib is a welcoming, inclusive project, and everyone within the community -is expected to abide by our `code of conduct <../CODE_OF_CONDUCT.md>`_. - - -.. raw:: html - -

Get help

-
-
- Discourse -

Join our community at discourse.matplotlib.org - to get help, discuss contributing & development, and share your work.

-
-
- Questions -

If you have questions, be sure to check the FAQ, - the API docs. The full text - search is a good way to discover the docs including the many examples.

-
-
- Stackoverflow -

Check out the Matplotlib tag on stackoverflow.

-
-
- Gitter -

Short questions may be posted on the gitter channel.

-
-
-
-

News

-
-
- News -

To keep up to date with what's going on in Matplotlib, see the - what's new page or browse the - source code. Anything that could - require changes to your existing code is logged in the - API changes file.

-
-
- Social media - -
-
-
-

Development

-
-
- Github -

Matplotlib is hosted on GitHub.

- -

It is a good idea to ping us on Discourse as well.

-
-
- Mailing lists -

Mailing lists

- -
-
- - -Toolkits -======== - -Matplotlib ships with several add-on :doc:`toolkits `, -including 3D plotting with `.mplot3d`, axes helpers in `.axes_grid1` and axis -helpers in `.axisartist`. - -Third party packages -==================== - -A large number of :doc:`third party packages ` -extend and build on Matplotlib functionality, including several higher-level -plotting interfaces (seaborn_, HoloViews_, ggplot_, ...), and a projection -and mapping toolkit (Cartopy_). - -.. _seaborn: https://seaborn.pydata.org -.. _HoloViews: https://holoviews.org -.. _ggplot: http://ggplot.yhathq.com -.. _Cartopy: https://scitools.org.uk/cartopy/docs/latest - -Citing Matplotlib -================= - -Matplotlib is the brainchild of John Hunter (1968-2012), who, along with its -many contributors, have put an immeasurable amount of time and effort into -producing a piece of software utilized by thousands of scientists worldwide. - -If Matplotlib contributes to a project that leads to a scientific publication, -please acknowledge this work by citing the project. A :doc:`ready-made citation -entry ` is available. - -Open source -=========== - -.. raw:: html - - - A Fiscally Sponsored Project of NUMFocus - - - -Matplotlib is a Sponsored Project of NumFOCUS, a 501(c)(3) nonprofit -charity in the United States. NumFOCUS provides Matplotlib with -fiscal, legal, and administrative support to help ensure the health -and sustainability of the project. Visit `numfocus.org `_ for more -information. - -Donations to Matplotlib are managed by NumFOCUS. For donors in the -United States, your gift is tax-deductible to the extent provided by -law. As with any donation, you should consult with your tax adviser -about your particular tax situation. - -Please consider `donating to the Matplotlib project `_ through -the NumFOCUS organization or to the `John Hunter Technology Fellowship -`_. - -.. _donating: https://numfocus.org/donate-to-matplotlib -.. _jdh-fellowship: https://numfocus.org/programs/john-hunter-technology-fellowship/ -.. _nf: https://numfocus.org - -The :doc:`Matplotlib license ` is based on the `Python Software -Foundation (PSF) license `_. - -.. _psf-license: https://www.python.org/psf/license - -There is an active developer community and a long list of people who have made -significant :doc:`contributions `. +************ +Installation +************ + +.. grid:: 1 1 2 2 + + .. grid-item:: + + Install using `pip `__: + + .. code-block:: bash + + pip install matplotlib + + .. grid-item:: + + Install using `conda `__: + + .. code-block:: bash + + conda install matplotlib + +Further details are available in the :doc:`Installation Guide `. + + +****************** +Learning resources +****************** + +.. grid:: 1 1 2 2 + + .. grid-item-card:: + :padding: 2 + + Tutorials + ^^^ + + - :doc:`Quick-start guide ` + - :doc:`Plot types ` + - `Introductory tutorials <../tutorials/index.html#introductory>`_ + - :doc:`External learning resources ` + + .. grid-item-card:: + :padding: 2 + + How-tos + ^^^ + + - :doc:`Example gallery ` + - :doc:`Matplotlib FAQ ` + + .. grid-item-card:: + :padding: 2 + + Understand how Matplotlib works + ^^^ + + - The :ref:`users-guide-explain` in the :doc:`Users guide + ` + - Many of the :ref:`Intermediate ` and + :ref:`Advanced ` tutorials have explanatory + material + + .. grid-item-card:: + :padding: 2 + + Reference + ^^^ + + - :doc:`API Reference ` + - :doc:`Axes API ` for most plotting methods + - :doc:`Figure API ` for figure-level methods + - Top-level interfaces to create: + + - Figures (`.pyplot.figure`) + - Subplots (`.pyplot.subplots`, `.pyplot.subplot_mosaic`) + + +******************** +Third-party packages +******************** + +There are many `Third-party packages +`_ built on top of and extending +Matplotlib. + + +************ +Contributing +************ + +Matplotlib is a community project maintained for and by its users. There are many ways +you can help! + +- Help other users `on discourse `__ +- report a bug or request a feature `on GitHub `__ +- or improve the :ref:`documentation and code ` diff --git a/doc/make.bat b/doc/make.bat index 042c9ef3543b..578f111789c8 100644 --- a/doc/make.bat +++ b/doc/make.bat @@ -10,8 +10,9 @@ if "%SPHINXBUILD%" == "" ( set SOURCEDIR=. set BUILDDIR=build set SPHINXPROJ=matplotlib -set SPHINXOPTS=-W -set O= +if defined SPHINXOPTS goto skipopts +set SPHINXOPTS=-W --keep-going +:skipopts %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( diff --git a/doc/missing-references.json b/doc/missing-references.json index 054a201acca3..52ffa65805b1 100644 --- a/doc/missing-references.json +++ b/doc/missing-references.json @@ -5,7 +5,6 @@ "lib/matplotlib/dates.py:docstring of matplotlib.dates.AutoDateLocator.tick_values:5", "lib/matplotlib/dates.py:docstring of matplotlib.dates.MicrosecondLocator.tick_values:5", "lib/matplotlib/dates.py:docstring of matplotlib.dates.RRuleLocator.tick_values:5", - "lib/matplotlib/dates.py:docstring of matplotlib.dates.YearLocator.tick_values:5", "lib/matplotlib/ticker.py:docstring of matplotlib.ticker.AutoMinorLocator.tick_values:5", "lib/matplotlib/ticker.py:docstring of matplotlib.ticker.IndexLocator.tick_values:5", "lib/matplotlib/ticker.py:docstring of matplotlib.ticker.LinearLocator.tick_values:5", @@ -14,16 +13,11 @@ "lib/matplotlib/ticker.py:docstring of matplotlib.ticker.LogitLocator.tick_values:5", "lib/matplotlib/ticker.py:docstring of matplotlib.ticker.MaxNLocator.tick_values:5", "lib/matplotlib/ticker.py:docstring of matplotlib.ticker.MultipleLocator.tick_values:5", - "lib/matplotlib/ticker.py:docstring of matplotlib.ticker.OldAutoLocator.tick_values:5", "lib/matplotlib/ticker.py:docstring of matplotlib.ticker.SymmetricalLogLocator.tick_values:5" ], - "button": [ - "doc/users/prev_whats_new/whats_new_3.1.0.rst:335" - ], "cbar_axes": [ - "lib/mpl_toolkits/axes_grid1/axes_grid.py:docstring of mpl_toolkits.axes_grid1.axes_grid.ImageGrid.__init__:49", - "lib/mpl_toolkits/axes_grid1/axes_grid.py:docstring of mpl_toolkits.axes_grid1.axes_grid.ImageGrid:49", - "lib/mpl_toolkits/axisartist/axes_grid.py:docstring of mpl_toolkits.axisartist.axes_grid.ImageGrid:49" + "lib/mpl_toolkits/axes_grid1/axes_grid.py:docstring of mpl_toolkits.axes_grid1.axes_grid.ImageGrid:45", + "lib/mpl_toolkits/axisartist/axes_grid.py:docstring of mpl_toolkits.axisartist.axes_grid.ImageGrid:45" ], "eventson": [ "lib/matplotlib/widgets.py:docstring of matplotlib.widgets.CheckButtons.set_active:4", @@ -36,6 +30,22 @@ "lib/matplotlib/transforms.py:docstring of matplotlib.transforms.Bbox.bounds:2" ], "input_dims": [ + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.AitoffAxes.AitoffTransform.transform_non_affine:14", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.AitoffAxes.AitoffTransform.transform_non_affine:20", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.AitoffAxes.InvertedAitoffTransform.transform_non_affine:14", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.AitoffAxes.InvertedAitoffTransform.transform_non_affine:20", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.HammerAxes.HammerTransform.transform_non_affine:14", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.HammerAxes.HammerTransform.transform_non_affine:20", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.HammerAxes.InvertedHammerTransform.transform_non_affine:14", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.HammerAxes.InvertedHammerTransform.transform_non_affine:20", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.LambertAxes.InvertedLambertTransform.transform_non_affine:14", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.LambertAxes.InvertedLambertTransform.transform_non_affine:20", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.LambertAxes.LambertTransform.transform_non_affine:14", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.LambertAxes.LambertTransform.transform_non_affine:20", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.MollweideAxes.InvertedMollweideTransform.transform_non_affine:14", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.MollweideAxes.InvertedMollweideTransform.transform_non_affine:20", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.MollweideAxes.MollweideTransform.transform_non_affine:14", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.MollweideAxes.MollweideTransform.transform_non_affine:20", "lib/matplotlib/transforms.py:docstring of matplotlib.transforms.AffineBase.transform:14", "lib/matplotlib/transforms.py:docstring of matplotlib.transforms.AffineBase.transform:8", "lib/matplotlib/transforms.py:docstring of matplotlib.transforms.AffineBase.transform_affine:15", @@ -54,7 +64,7 @@ "lib/matplotlib/transforms.py:docstring of matplotlib.transforms.IdentityTransform.transform_non_affine:20" ], "lines": [ - "lib/matplotlib/colorbar.py:docstring of matplotlib.colorbar.ColorbarBase.add_lines:4" + "lib/matplotlib/colorbar.py:docstring of matplotlib.colorbar.Colorbar.add_lines:4" ], "matplotlib.axes.Axes.dataLim": [ "doc/api/prev_api_changes/api_changes_0.99.x.rst:23" @@ -63,43 +73,36 @@ "doc/api/prev_api_changes/api_changes_0.98.x.rst:89" ], "matplotlib.axes.Axes.lines": [ - "doc/tutorials/intermediate/artists.rst:425", - "doc/tutorials/intermediate/artists.rst:91" + "doc/tutorials/intermediate/artists.rst:100", + "doc/tutorials/intermediate/artists.rst:442" ], "matplotlib.axes.Axes.patch": [ "doc/api/prev_api_changes/api_changes_0.98.x.rst:89", - "doc/tutorials/intermediate/artists.rst:174", - "doc/tutorials/intermediate/artists.rst:409" + "doc/tutorials/intermediate/artists.rst:187", + "doc/tutorials/intermediate/artists.rst:426" ], "matplotlib.axes.Axes.patches": [ - "doc/tutorials/intermediate/artists.rst:448" + "doc/tutorials/intermediate/artists.rst:465" ], "matplotlib.axes.Axes.transAxes": [ - "lib/mpl_toolkits/axes_grid1/anchored_artists.py:docstring of mpl_toolkits.axes_grid1.anchored_artists.AnchoredDirectionArrows.__init__:8", "lib/mpl_toolkits/axes_grid1/anchored_artists.py:docstring of mpl_toolkits.axes_grid1.anchored_artists.AnchoredDirectionArrows:8" ], "matplotlib.axes.Axes.transData": [ - "lib/mpl_toolkits/axes_grid1/anchored_artists.py:docstring of mpl_toolkits.axes_grid1.anchored_artists.AnchoredAuxTransformBox.__init__:11", "lib/mpl_toolkits/axes_grid1/anchored_artists.py:docstring of mpl_toolkits.axes_grid1.anchored_artists.AnchoredAuxTransformBox:11", - "lib/mpl_toolkits/axes_grid1/anchored_artists.py:docstring of mpl_toolkits.axes_grid1.anchored_artists.AnchoredEllipse.__init__:8", "lib/mpl_toolkits/axes_grid1/anchored_artists.py:docstring of mpl_toolkits.axes_grid1.anchored_artists.AnchoredEllipse:8", - "lib/mpl_toolkits/axes_grid1/anchored_artists.py:docstring of mpl_toolkits.axes_grid1.anchored_artists.AnchoredSizeBar.__init__:8", "lib/mpl_toolkits/axes_grid1/anchored_artists.py:docstring of mpl_toolkits.axes_grid1.anchored_artists.AnchoredSizeBar:8" ], "matplotlib.axes.Axes.viewLim": [ "doc/api/prev_api_changes/api_changes_0.99.x.rst:23" ], "matplotlib.axes.Axes.xaxis": [ - "doc/tutorials/intermediate/artists.rst:593" + "doc/tutorials/intermediate/artists.rst:610" ], "matplotlib.axes.Axes.yaxis": [ - "doc/tutorials/intermediate/artists.rst:593" + "doc/tutorials/intermediate/artists.rst:610" ], "matplotlib.axis.Axis.label": [ - "doc/tutorials/intermediate/artists.rst:640" - ], - "matplotlib.cm.ScalarMappable.callbacksSM": [ - "doc/api/prev_api_changes/api_changes_0.98.0.rst:10" + "doc/tutorials/intermediate/artists.rst:657" ], "matplotlib.colorbar.ColorbarBase.ax": [ "doc/api/prev_api_changes/api_changes_1.3.x.rst:100" @@ -109,11 +112,11 @@ ], "matplotlib.figure.Figure.patch": [ "doc/api/prev_api_changes/api_changes_0.98.x.rst:89", - "doc/tutorials/intermediate/artists.rst:174", - "doc/tutorials/intermediate/artists.rst:307" + "doc/tutorials/intermediate/artists.rst:187", + "doc/tutorials/intermediate/artists.rst:320" ], "matplotlib.figure.Figure.transFigure": [ - "doc/tutorials/intermediate/artists.rst:356" + "doc/tutorials/intermediate/artists.rst:369" ], "max": [ "lib/matplotlib/transforms.py:docstring of matplotlib.transforms.Bbox.p1:4" @@ -122,12 +125,20 @@ "lib/matplotlib/transforms.py:docstring of matplotlib.transforms.Bbox.p0:4" ], "mpl_toolkits.mplot3d.axis3d._axinfo": [ - "doc/api/toolkits/mplot3d.rst:40" + "doc/api/toolkits/mplot3d.rst:66" ], "name": [ "lib/matplotlib/scale.py:docstring of matplotlib.scale.ScaleBase:8" ], "output_dims": [ + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.AitoffAxes.AitoffTransform.transform_non_affine:20", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.AitoffAxes.InvertedAitoffTransform.transform_non_affine:20", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.HammerAxes.HammerTransform.transform_non_affine:20", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.HammerAxes.InvertedHammerTransform.transform_non_affine:20", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.LambertAxes.InvertedLambertTransform.transform_non_affine:20", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.LambertAxes.LambertTransform.transform_non_affine:20", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.MollweideAxes.InvertedMollweideTransform.transform_non_affine:20", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.MollweideAxes.MollweideTransform.transform_non_affine:20", "lib/matplotlib/transforms.py:docstring of matplotlib.transforms.AffineBase.transform:14", "lib/matplotlib/transforms.py:docstring of matplotlib.transforms.AffineBase.transform_affine:21", "lib/matplotlib/transforms.py:docstring of matplotlib.transforms.AffineBase.transform_non_affine:20", @@ -167,10 +178,10 @@ "doc/api/prev_api_changes/api_changes_2.2.0.rst:215" ], "Patch3DCollection": [ - "doc/api/toolkits/mplot3d.rst:83::1" + "doc/api/toolkits/mplot3d.rst:109::1" ], "Path3DCollection": [ - "doc/api/toolkits/mplot3d.rst:83::1" + "doc/api/toolkits/mplot3d.rst:109::1" ], "backend_qt5.FigureCanvasQT": [ "doc/api/prev_api_changes/api_changes_2.2.0.rst:199" @@ -230,40 +241,26 @@ "doc/api/mathtext_api.rst:12" ], "matplotlib.axes.Subplot": [ - "doc/tutorials/intermediate/artists.rst:35", - "doc/tutorials/intermediate/artists.rst:58" - ], - "matplotlib.axes._axes.Axes": [ - "doc/api/artist_api.rst:189", - "doc/api/axes_api.rst:609", - "lib/matplotlib/projections/polar.py:docstring of matplotlib.projections.polar.PolarAxes:1", - "lib/mpl_toolkits/axes_grid1/mpl_axes.py:docstring of mpl_toolkits.axes_grid1.mpl_axes.Axes:1", - "lib/mpl_toolkits/axisartist/axislines.py:docstring of mpl_toolkits.axisartist.axislines.Axes:1", - "lib/mpl_toolkits/mplot3d/axes3d.py:docstring of mpl_toolkits.mplot3d.axes3d.Axes3D:1" + "doc/tutorials/intermediate/artists.rst:44", + "doc/tutorials/intermediate/artists.rst:67" ], "matplotlib.axes._base._AxesBase": [ - "doc/api/artist_api.rst:189", + "doc/api/artist_api.rst:190", "doc/api/axes_api.rst:609", "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes:1" ], "matplotlib.backend_bases.FigureCanvas": [ - "doc/tutorials/intermediate/artists.rst:20", - "doc/tutorials/intermediate/artists.rst:22", - "doc/tutorials/intermediate/artists.rst:27" + "doc/tutorials/intermediate/artists.rst:29", + "doc/tutorials/intermediate/artists.rst:31", + "doc/tutorials/intermediate/artists.rst:36" ], "matplotlib.backend_bases.Renderer": [ - "doc/tutorials/intermediate/artists.rst:22", - "doc/tutorials/intermediate/artists.rst:27" + "doc/tutorials/intermediate/artists.rst:31", + "doc/tutorials/intermediate/artists.rst:36" ], "matplotlib.backend_bases._Backend": [ "lib/matplotlib/backend_bases.py:docstring of matplotlib.backend_bases.ShowBase:1" ], - "matplotlib.backend_tools._ToolEnableAllNavigation": [ - "lib/matplotlib/backend_tools.py:docstring of matplotlib.backend_tools.ToolEnableAllNavigation:1" - ], - "matplotlib.backend_tools._ToolEnableNavigation": [ - "lib/matplotlib/backend_tools.py:docstring of matplotlib.backend_tools.ToolEnableNavigation:1" - ], "matplotlib.backends._backend_pdf_ps.RendererPDFPSBase": [ "lib/matplotlib/backends/backend_pdf.py:docstring of matplotlib.backends.backend_pdf.RendererPdf:1", "lib/matplotlib/backends/backend_ps.py:docstring of matplotlib.backends.backend_ps.RendererPS:1" @@ -273,7 +270,8 @@ "lib/matplotlib/backends/backend_tkcairo.py:docstring of matplotlib.backends.backend_tkcairo.FigureCanvasTkCairo:1" ], "matplotlib.backends.backend_webagg_core.FigureCanvasWebAggCore": [ - "lib/matplotlib/backends/backend_nbagg.py:docstring of matplotlib.backends.backend_nbagg.FigureCanvasNbAgg:1" + "lib/matplotlib/backends/backend_nbagg.py:docstring of matplotlib.backends.backend_nbagg.FigureCanvasNbAgg:1", + "lib/matplotlib/backends/backend_webagg.py:docstring of matplotlib.backends.backend_webagg.FigureCanvasWebAgg:1" ], "matplotlib.backends.backend_webagg_core.FigureManagerWebAgg": [ "lib/matplotlib/backends/backend_nbagg.py:docstring of matplotlib.backends.backend_nbagg.FigureManagerNbAgg:1" @@ -281,11 +279,8 @@ "matplotlib.backends.backend_webagg_core.NavigationToolbar2WebAgg": [ "lib/matplotlib/backends/backend_nbagg.py:docstring of matplotlib.backends.backend_nbagg.NavigationIPy:1" ], - "matplotlib.cm.Wistia": [ - "doc/users/prev_whats_new/whats_new_1.4.rst:21" - ], "matplotlib.collections._CollectionWithSizes": [ - "doc/api/artist_api.rst:189", + "doc/api/artist_api.rst:190", "doc/api/collections_api.rst:13", "lib/matplotlib/collections.py:docstring of matplotlib.collections.CircleCollection:1", "lib/matplotlib/collections.py:docstring of matplotlib.collections.PathCollection:1", @@ -293,10 +288,10 @@ "lib/matplotlib/collections.py:docstring of matplotlib.collections.RegularPolyCollection:1" ], "matplotlib.dates.rrulewrapper": [ - "doc/api/dates_api.rst:11" + "doc/api/dates_api.rst:12" ], "matplotlib.image._ImageBase": [ - "doc/api/artist_api.rst:189", + "doc/api/artist_api.rst:190", "lib/matplotlib/image.py:docstring of matplotlib.image.AxesImage:1", "lib/matplotlib/image.py:docstring of matplotlib.image.BboxImage:1", "lib/matplotlib/image.py:docstring of matplotlib.image.FigureImage:1" @@ -354,17 +349,17 @@ "lib/matplotlib/patches.py:docstring of matplotlib.patches.ArrowStyle.Simple:1", "lib/matplotlib/patches.py:docstring of matplotlib.patches.ArrowStyle.Wedge:1" ], - "matplotlib.patches.ArrowStyle._Bracket": [ + "matplotlib.patches.ArrowStyle._Curve": [ "lib/matplotlib/patches.py:docstring of matplotlib.patches.ArrowStyle.BarAB:1", "lib/matplotlib/patches.py:docstring of matplotlib.patches.ArrowStyle.BracketA:1", "lib/matplotlib/patches.py:docstring of matplotlib.patches.ArrowStyle.BracketAB:1", - "lib/matplotlib/patches.py:docstring of matplotlib.patches.ArrowStyle.BracketB:1" - ], - "matplotlib.patches.ArrowStyle._Curve": [ + "lib/matplotlib/patches.py:docstring of matplotlib.patches.ArrowStyle.BracketB:1", + "lib/matplotlib/patches.py:docstring of matplotlib.patches.ArrowStyle.BracketCurve:1", "lib/matplotlib/patches.py:docstring of matplotlib.patches.ArrowStyle.Curve:1", "lib/matplotlib/patches.py:docstring of matplotlib.patches.ArrowStyle.CurveA:1", "lib/matplotlib/patches.py:docstring of matplotlib.patches.ArrowStyle.CurveAB:1", "lib/matplotlib/patches.py:docstring of matplotlib.patches.ArrowStyle.CurveB:1", + "lib/matplotlib/patches.py:docstring of matplotlib.patches.ArrowStyle.CurveBracket:1", "lib/matplotlib/patches.py:docstring of matplotlib.patches.ArrowStyle.CurveFilledA:1", "lib/matplotlib/patches.py:docstring of matplotlib.patches.ArrowStyle.CurveFilledAB:1", "lib/matplotlib/patches.py:docstring of matplotlib.patches.ArrowStyle.CurveFilledB:1" @@ -391,23 +386,18 @@ "lib/matplotlib/patches.py:docstring of matplotlib.patches.ConnectionStyle:1", "lib/mpl_toolkits/axisartist/axisline_style.py:docstring of mpl_toolkits.axisartist.axisline_style.AxislineStyle:1" ], - "matplotlib.projections.geo.AitoffAxes": [ - "doc/api/artist_api.rst:189" - ], - "matplotlib.projections.geo.GeoAxes": [ - "doc/api/artist_api.rst:189" - ], - "matplotlib.projections.geo.HammerAxes": [ - "doc/api/artist_api.rst:189" - ], - "matplotlib.projections.geo.LambertAxes": [ - "doc/api/artist_api.rst:189" - ], - "matplotlib.projections.geo.MollweideAxes": [ - "doc/api/artist_api.rst:189" + "matplotlib.projections.geo._GeoTransform": [ + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.AitoffAxes.AitoffTransform:1", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.AitoffAxes.InvertedAitoffTransform:1", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.HammerAxes.HammerTransform:1", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.HammerAxes.InvertedHammerTransform:1", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.LambertAxes.InvertedLambertTransform:1", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.LambertAxes.LambertTransform:1", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.MollweideAxes.InvertedMollweideTransform:1", + "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.MollweideAxes.MollweideTransform:1" ], "matplotlib.text._AnnotationBase": [ - "doc/api/artist_api.rst:189", + "doc/api/artist_api.rst:190", "lib/matplotlib/offsetbox.py:docstring of matplotlib.offsetbox.AnnotationBbox:1", "lib/matplotlib/text.py:docstring of matplotlib.text.Annotation:1" ], @@ -415,16 +405,6 @@ "lib/matplotlib/transforms.py:docstring of matplotlib.transforms.BlendedAffine2D:1", "lib/matplotlib/transforms.py:docstring of matplotlib.transforms.BlendedGenericTransform:1" ], - "matplotlib.tri.trifinder.TriFinder": [ - "lib/matplotlib/tri/trifinder.py:docstring of matplotlib.tri.trifinder.TrapezoidMapTriFinder:1" - ], - "matplotlib.tri.triinterpolate.TriInterpolator": [ - "lib/matplotlib/tri/triinterpolate.py:docstring of matplotlib.tri.triinterpolate.CubicTriInterpolator:1", - "lib/matplotlib/tri/triinterpolate.py:docstring of matplotlib.tri.triinterpolate.LinearTriInterpolator:1" - ], - "matplotlib.tri.trirefine.TriRefiner": [ - "lib/matplotlib/tri/trirefine.py:docstring of matplotlib.tri.trirefine.UniformTriRefiner:1" - ], "matplotlib.widgets._SelectorWidget": [ "lib/matplotlib/widgets.py:docstring of matplotlib.widgets.LassoSelector:1", "lib/matplotlib/widgets.py:docstring of matplotlib.widgets.PolygonSelector:1", @@ -456,7 +436,6 @@ "doc/api/_as_gen/mpl_toolkits.axes_grid1.parasite_axes.rst:31::1" ], "mpl_toolkits.axisartist.Axes": [ - "doc/api/toolkits/axisartist.rst:5", "doc/api/toolkits/axisartist.rst:6" ], "mpl_toolkits.axisartist.axisline_style.AxislineStyle._Base": [ @@ -472,7 +451,7 @@ "lib/mpl_toolkits/axisartist/axislines.py:docstring of mpl_toolkits.axisartist.axislines.AxisArtistHelper.Fixed:1", "lib/mpl_toolkits/axisartist/axislines.py:docstring of mpl_toolkits.axisartist.axislines.AxisArtistHelper.Floating:1" ], - "mpl_toolkits.axisartist.floating_axes.Floating AxesHostAxes": [ + "mpl_toolkits.axisartist.floating_axes.FloatingAxesHostAxes": [ ":1", "doc/api/_as_gen/mpl_toolkits.axisartist.floating_axes.rst:31::1" ], @@ -485,22 +464,11 @@ }, "py:data": { "matplotlib.axes.Axes.transAxes": [ - "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.legend:219", - "lib/matplotlib/figure.py:docstring of matplotlib.figure.FigureBase.legend:220", - "lib/matplotlib/legend.py:docstring of matplotlib.legend.Legend:179", - "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.figlegend:220", - "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.legend:219" - ] - }, - "py:func": { - "matplotlib.Axis.set_ticks_position": [ - "doc/users/prev_whats_new/whats_new_1.4.rst:141" - ], - "matplotlib.InvertedPolarTransform.transform_non_affine": [ - "doc/users/prev_whats_new/whats_new_1.4.rst:236" - ], - "matplotlib.axis.Tick.label1On": [ - "doc/users/prev_whats_new/whats_new_2.1.0.rst:409" + "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.legend:234", + "lib/matplotlib/figure.py:docstring of matplotlib.figure.FigureBase.legend:235", + "lib/matplotlib/legend.py:docstring of matplotlib.legend.Legend:182", + "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.figlegend:235", + "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.legend:234" ] }, "py:meth": { @@ -521,96 +489,83 @@ "doc/api/prev_api_changes/api_changes_2.2.0.rst:199" ], "IPython.terminal.interactiveshell.TerminalInteractiveShell.inputhook": [ - "doc/users/interactive_guide.rst:421" + "doc/users/explain/interactive_guide.rst:422" ], "_find_tails": [ "lib/matplotlib/quiver.py:docstring of matplotlib.quiver.Barbs:9" ], - "_iter_collection": [ - "lib/matplotlib/backend_bases.py:docstring of matplotlib.backend_bases.RendererBase.draw_path_collection:12", - "lib/matplotlib/backends/backend_agg.py:docstring of matplotlib.backends.backend_agg.RendererAgg.draw_path_collection:12", - "lib/matplotlib/backends/backend_pdf.py:docstring of matplotlib.backends.backend_pdf.RendererPdf.draw_path_collection:12", - "lib/matplotlib/backends/backend_ps.py:docstring of matplotlib.backends.backend_ps.RendererPS.draw_path_collection:12", - "lib/matplotlib/backends/backend_svg.py:docstring of matplotlib.backends.backend_svg.RendererSVG.draw_path_collection:12", - "lib/matplotlib/patheffects.py:docstring of matplotlib.patheffects.PathEffectRenderer.draw_path_collection:12" - ], - "_iter_collection_raw_paths": [ - "lib/matplotlib/backend_bases.py:docstring of matplotlib.backend_bases.RendererBase.draw_path_collection:12", - "lib/matplotlib/backends/backend_agg.py:docstring of matplotlib.backends.backend_agg.RendererAgg.draw_path_collection:12", - "lib/matplotlib/backends/backend_pdf.py:docstring of matplotlib.backends.backend_pdf.RendererPdf.draw_path_collection:12", - "lib/matplotlib/backends/backend_ps.py:docstring of matplotlib.backends.backend_ps.RendererPS.draw_path_collection:12", - "lib/matplotlib/backends/backend_svg.py:docstring of matplotlib.backends.backend_svg.RendererSVG.draw_path_collection:12", - "lib/matplotlib/patheffects.py:docstring of matplotlib.patheffects.PathEffectRenderer.draw_path_collection:12" - ], "_make_barbs": [ "lib/matplotlib/quiver.py:docstring of matplotlib.quiver.Barbs:9" ], "autoscale_view": [ "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.margins:32" ], - "colorbar.Colobar.minorticks_off": [ - "doc/users/prev_whats_new/whats_new_3.0.rst:63" - ], - "colorbar.Colobar.minorticks_on": [ - "doc/users/prev_whats_new/whats_new_3.0.rst:63" - ], - "draw_image": [ - "lib/matplotlib/backends/backend_agg.py:docstring of matplotlib.backends.backend_agg.RendererAgg.option_scale_image:2" - ], "get_matrix": [ "lib/matplotlib/transforms.py:docstring of matplotlib.transforms.Affine2DBase:13" ], - "matplotlib.dates.DateFormatter.__call__": [ - "doc/users/prev_whats_new/whats_new_1.5.rst:497" + "matplotlib.collections._CollectionWithSizes.set_sizes": [ + "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.barbs:176", + "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.broken_barh:86", + "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.fill_between:118", + "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.fill_betweenx:118", + "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.hexbin:173", + "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.pcolor:168", + "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.quiver:211", + "lib/matplotlib/collections.py:docstring of matplotlib.artist.AsteriskPolygonCollection.set:43", + "lib/matplotlib/collections.py:docstring of matplotlib.artist.BrokenBarHCollection.set:44", + "lib/matplotlib/collections.py:docstring of matplotlib.artist.CircleCollection.set:43", + "lib/matplotlib/collections.py:docstring of matplotlib.artist.PathCollection.set:44", + "lib/matplotlib/collections.py:docstring of matplotlib.artist.PolyCollection.set:44", + "lib/matplotlib/collections.py:docstring of matplotlib.artist.RegularPolyCollection.set:43", + "lib/matplotlib/collections.py:docstring of matplotlib.artist.StarPolygonCollection.set:43", + "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.barbs:176", + "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.broken_barh:86", + "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.fill_between:118", + "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.fill_betweenx:118", + "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.hexbin:173", + "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.pcolor:168", + "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.quiver:211", + "lib/matplotlib/quiver.py:docstring of matplotlib.artist.Barbs.set:45", + "lib/matplotlib/quiver.py:docstring of matplotlib.artist.Quiver.set:45", + "lib/matplotlib/quiver.py:docstring of matplotlib.quiver.Barbs:209", + "lib/matplotlib/quiver.py:docstring of matplotlib.quiver.Quiver:247", + "lib/mpl_toolkits/mplot3d/art3d.py:docstring of matplotlib.artist.Path3DCollection.set:46", + "lib/mpl_toolkits/mplot3d/art3d.py:docstring of matplotlib.artist.Poly3DCollection.set:44" ], "matplotlib.dates.MicrosecondLocator.__call__": [ "doc/api/prev_api_changes/api_changes_1.5.0.rst:122" - ], - "option_scale_image": [ - "lib/matplotlib/backends/backend_cairo.py:docstring of matplotlib.backends.backend_cairo.RendererCairo.draw_image:22", - "lib/matplotlib/backends/backend_pdf.py:docstring of matplotlib.backends.backend_pdf.RendererPdf.draw_image:22", - "lib/matplotlib/backends/backend_ps.py:docstring of matplotlib.backends.backend_ps.RendererPS.draw_image:22", - "lib/matplotlib/backends/backend_template.py:docstring of matplotlib.backends.backend_template.RendererTemplate.draw_image:22" ] }, "py:mod": { "IPython.terminal.pt_inputhooks": [ - "doc/users/interactive_guide.rst:421" + "doc/users/explain/interactive_guide.rst:422" ], "dateutil": [ "lib/matplotlib/dates.py:docstring of matplotlib.dates:1" ], - "matplotlib": [ - "doc/api/prev_api_changes/api_changes_0.91.2.rst:15" - ], "matplotlib.ft2font": [ "doc/api/prev_api_changes/api_changes_0.91.0.rst:34" ] }, "py:obj": { "Artist.stale_callback": [ - "doc/users/interactive_guide.rst:323" + "doc/users/explain/interactive_guide.rst:325" ], "Artist.sticky_edges": [ - "doc/api/axes_api.rst:364::1", + "doc/api/axes_api.rst:363::1", "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes.Axes.use_sticky_edges:2" ], "ArtistInspector.aliasd": [ - "doc/api/prev_api_changes/api_changes_3.0.0.rst:332", - "doc/users/prev_whats_new/whats_new_3.1.0.rst:171" + "doc/api/prev_api_changes/api_changes_3.0.0.rst:332" ], "ArtistInspector.get_aliases": [ "doc/api/prev_api_changes/api_changes_3.0.0.rst:327" ], "Axes.dataLim": [ - "doc/api/axes_api.rst:304::1", + "doc/api/axes_api.rst:303::1", "lib/matplotlib/axes/_base.py:docstring of matplotlib.axes._base._AxesBase.update_datalim:2", "lib/mpl_toolkits/mplot3d/axes3d.py:docstring of mpl_toolkits.mplot3d.axes3d.Axes3D.update_datalim:2" ], - "Axes.datalim": [ - "doc/api/axes_api.rst:304::1", - "lib/matplotlib/axes/_base.py:docstring of matplotlib.axes._base._AxesBase.update_datalim_bounds:2" - ], "Axes.fmt_xdata": [ "doc/api/prev_api_changes/api_changes_3.1.0.rst:397", "doc/api/prev_api_changes/api_changes_3.1.0.rst:400" @@ -623,30 +578,25 @@ "doc/api/prev_api_changes/api_changes_3.2.0/behavior.rst:91", "doc/api/prev_api_changes/api_changes_3.2.0/behavior.rst:93", "doc/api/prev_api_changes/api_changes_3.2.0/behavior.rst:95", - "doc/api/prev_api_changes/api_changes_3.2.0/behavior.rst:98" + "doc/api/prev_api_changes/api_changes_3.2.0/behavior.rst:98", + "doc/users/prev_whats_new/whats_new_3.5.0.rst:126" ], "AxesBase": [ - "doc/api/axes_api.rst:456::1", + "doc/api/axes_api.rst:455::1", "lib/matplotlib/axes/_base.py:docstring of matplotlib.axes._base._AxesBase.add_child_axes:2" ], "Axis._update_ticks": [ "doc/api/prev_api_changes/api_changes_3.1.0.rst:1072" ], - "Axis.set_tick_params": [ - "doc/users/prev_whats_new/whats_new_2.1.0.rst:394" - ], "Axis.units": [ "doc/api/prev_api_changes/api_changes_2.2.0.rst:77" ], "FT2Font": [ - "doc/gallery/misc/ftface_props.rst:16", + "doc/gallery/misc/ftface_props.rst:25", "lib/matplotlib/font_manager.py:docstring of matplotlib.font_manager.ttfFontProperty:8" ], "Figure.stale_callback": [ - "doc/users/interactive_guide.rst:333" - ], - "FigureCanvas": [ - "lib/matplotlib/backend_tools.py:docstring of matplotlib.backend_tools.ToolBase:25" + "doc/users/explain/interactive_guide.rst:335" ], "Formatter.__call__": [ "doc/api/prev_api_changes/api_changes_3.1.0.rst:1122", @@ -656,11 +606,8 @@ "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.violinplot:46", "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.violinplot:46" ], - "Glue": [ - "lib/matplotlib/mathtext.py:docstring of matplotlib.mathtext.GlueSpec:2" - ], "Glyph": [ - "doc/gallery/misc/ftface_props.rst:16" + "doc/gallery/misc/ftface_props.rst:25" ], "Image": [ "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.gci:4" @@ -672,58 +619,55 @@ "doc/api/prev_api_changes/api_changes_3.1.0.rst:706" ], "Line2D.pick": [ - "doc/users/event_handling.rst:438" + "doc/users/explain/event_handling.rst:468" ], "MicrosecondLocator.__call__": [ "doc/api/prev_api_changes/api_changes_1.5.0.rst:119" ], "MovieWriter.saving": [ - "doc/api/animation_api.rst:221" + "doc/api/animation_api.rst:232" ], "MovieWriterBase": [ - "lib/matplotlib/animation.py:docstring of matplotlib.animation.AVConvBase:4", "lib/matplotlib/animation.py:docstring of matplotlib.animation.FFMpegBase:4", "lib/matplotlib/animation.py:docstring of matplotlib.animation.ImageMagickBase:4" ], - "MovieWriterRegistry": [ - "doc/api/prev_api_changes/api_changes_3.2.0/behavior.rst:49", - "doc/api/prev_api_changes/api_changes_3.2.0/behavior.rst:51", - "doc/api/prev_api_changes/api_changes_3.2.0/deprecations.rst:121" - ], "QuadContourSet.changed()": [ - "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.contour:131", - "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.contourf:131", - "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.contour:131", - "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.contourf:131" + "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.contour:133", + "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.contourf:133", + "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.contour:133", + "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.contourf:133" ], "Quiver.pivot": [ "doc/api/prev_api_changes/api_changes_1.5.0.rst:44" ], "Rectangle.contains": [ - "doc/users/event_handling.rst:150" + "doc/users/explain/event_handling.rst:180" ], "Size.from_any": [ - "lib/mpl_toolkits/axes_grid1/axes_grid.py:docstring of mpl_toolkits.axes_grid1.axes_grid.ImageGrid.__init__:61", - "lib/mpl_toolkits/axes_grid1/axes_grid.py:docstring of mpl_toolkits.axes_grid1.axes_grid.ImageGrid:61", - "lib/mpl_toolkits/axisartist/axes_grid.py:docstring of mpl_toolkits.axisartist.axes_grid.ImageGrid:61" + "lib/mpl_toolkits/axes_grid1/axes_grid.py:docstring of mpl_toolkits.axes_grid1.axes_grid.ImageGrid:57", + "lib/mpl_toolkits/axisartist/axes_grid.py:docstring of mpl_toolkits.axisartist.axes_grid.ImageGrid:57" ], "Timer": [ "lib/matplotlib/backend_bases.py:docstring of matplotlib.backend_bases.FigureCanvasBase.new_timer:2", "lib/matplotlib/backend_bases.py:docstring of matplotlib.backend_bases.TimerBase:14" ], "ToolContainer": [ - "doc/users/prev_whats_new/whats_new_1.5.rst:615", "lib/matplotlib/backend_bases.py:docstring of matplotlib.backend_bases.ToolContainerBase.remove_toolitem:2", "lib/matplotlib/backend_bases.py:docstring of matplotlib.backend_bases.ToolContainerBase:20" ], - "ToolContainers": [ - "doc/users/prev_whats_new/whats_new_1.5.rst:615" - ], - "Toolbars": [ - "doc/users/prev_whats_new/whats_new_1.5.rst:615" + "_iter_collection": [ + "lib/matplotlib/backend_bases.py:docstring of matplotlib.backend_bases.RendererBase.draw_path_collection:12", + "lib/matplotlib/backends/backend_pdf.py:docstring of matplotlib.backends.backend_pdf.RendererPdf.draw_path_collection:12", + "lib/matplotlib/backends/backend_ps.py:docstring of matplotlib.backends.backend_ps.RendererPS.draw_path_collection:12", + "lib/matplotlib/backends/backend_svg.py:docstring of matplotlib.backends.backend_svg.RendererSVG.draw_path_collection:12", + "lib/matplotlib/patheffects.py:docstring of matplotlib.patheffects.PathEffectRenderer.draw_path_collection:12" ], - "Tools": [ - "doc/users/prev_whats_new/whats_new_1.5.rst:608" + "_iter_collection_raw_paths": [ + "lib/matplotlib/backend_bases.py:docstring of matplotlib.backend_bases.RendererBase.draw_path_collection:12", + "lib/matplotlib/backends/backend_pdf.py:docstring of matplotlib.backends.backend_pdf.RendererPdf.draw_path_collection:12", + "lib/matplotlib/backends/backend_ps.py:docstring of matplotlib.backends.backend_ps.RendererPS.draw_path_collection:12", + "lib/matplotlib/backends/backend_svg.py:docstring of matplotlib.backends.backend_svg.RendererSVG.draw_path_collection:12", + "lib/matplotlib/patheffects.py:docstring of matplotlib.patheffects.PathEffectRenderer.draw_path_collection:12" ], "_read": [ "lib/matplotlib/dviread.py:docstring of matplotlib.dviread.Vf:20" @@ -750,11 +694,11 @@ "doc/api/prev_api_changes/api_changes_1.3.x.rst:36" ], "axes.bbox": [ - "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.legend:128", - "lib/matplotlib/figure.py:docstring of matplotlib.figure.FigureBase.legend:129", + "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.legend:140", + "lib/matplotlib/figure.py:docstring of matplotlib.figure.FigureBase.legend:141", "lib/matplotlib/legend.py:docstring of matplotlib.legend.Legend:88", - "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.figlegend:129", - "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.legend:128" + "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.figlegend:141", + "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.legend:140" ], "axes3d.Axes3D.xaxis": [ "doc/api/prev_api_changes/api_changes_3.1.0.rst:939" @@ -768,9 +712,6 @@ "axis.Axis.get_ticks_position": [ "doc/api/prev_api_changes/api_changes_3.1.0.rst:826" ], - "backend_bases.RendererBase": [ - "lib/matplotlib/backends/backend_template.py:docstring of matplotlib.backends.backend_template.RendererTemplate:4" - ], "backend_bases.ToolContainerBase": [ "lib/matplotlib/backend_tools.py:docstring of matplotlib.backend_tools.add_tools_to_container:8" ], @@ -790,21 +731,18 @@ "converter": [ "lib/matplotlib/testing/compare.py:docstring of matplotlib.testing.compare.compare_images:4" ], - "fc-list": [ - "lib/matplotlib/font_manager.py:docstring of matplotlib.font_manager.get_fontconfig_fonts:2" - ], - "figure.Figure.canvas.set_window_title()": [ - "doc/users/prev_whats_new/whats_new_3.0.rst:84" + "draw_image": [ + "lib/matplotlib/backends/backend_agg.py:docstring of matplotlib.backends.backend_agg.RendererAgg.option_scale_image:2" ], "figure.bbox": [ - "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.legend:128", - "lib/matplotlib/figure.py:docstring of matplotlib.figure.FigureBase.legend:129", + "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.legend:140", + "lib/matplotlib/figure.py:docstring of matplotlib.figure.FigureBase.legend:141", "lib/matplotlib/legend.py:docstring of matplotlib.legend.Legend:88", - "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.figlegend:129", - "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.legend:128" + "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.figlegend:141", + "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.legend:140" ], "floating_axes.FloatingSubplot": [ - "doc/gallery/axisartist/demo_floating_axes.rst:22" + "doc/gallery/axisartist/demo_floating_axes.rst:31" ], "fmt_xdata": [ "lib/matplotlib/axes/_base.py:docstring of matplotlib.axes._base._AxesBase.format_xdata:4" @@ -812,14 +750,7 @@ "fmt_ydata": [ "lib/matplotlib/axes/_base.py:docstring of matplotlib.axes._base._AxesBase.format_ydata:4" ], - "font.*": [ - "doc/users/prev_whats_new/whats_new_1.3.rst:302" - ], - "gaussian_kde": [ - "lib/matplotlib/mlab.py:docstring of matplotlib.mlab.GaussianKDE:32" - ], "get_size": [ - "doc/users/prev_whats_new/whats_new_1.4.rst:223", "lib/mpl_toolkits/axes_grid1/axes_size.py:docstring of mpl_toolkits.axes_grid1.axes_size:1" ], "get_xbound": [ @@ -828,14 +759,12 @@ "get_ybound": [ "lib/mpl_toolkits/mplot3d/axes3d.py:docstring of mpl_toolkits.mplot3d.axes3d.Axes3D.get_ylim3d:22" ], - "h_pad": [ - "lib/matplotlib/figure.py:docstring of matplotlib.figure.Figure.set_constrained_layout:5" - ], "image": [ - "lib/matplotlib/sphinxext/plot_directive.py:docstring of matplotlib.sphinxext.plot_directive:78" + "lib/matplotlib/sphinxext/plot_directive.py:docstring of matplotlib.sphinxext.plot_directive:79" ], "interactive": [ - "lib/matplotlib/backends/backend_nbagg.py:docstring of matplotlib.backends.backend_nbagg._BackendNbAgg.show:4" + "lib/matplotlib/backends/backend_nbagg.py:docstring of matplotlib.backends.backend_nbagg._BackendNbAgg.show:4", + "lib/matplotlib/backends/backend_webagg.py:docstring of matplotlib.backends.backend_webagg._BackendWebAgg.show:4" ], "invert_xaxis": [ "lib/mpl_toolkits/mplot3d/axes3d.py:docstring of mpl_toolkits.mplot3d.axes3d.Axes3D.get_xlim3d:24" @@ -844,7 +773,7 @@ "lib/mpl_toolkits/mplot3d/axes3d.py:docstring of mpl_toolkits.mplot3d.axes3d.Axes3D.get_ylim3d:24" ], "ipykernel.pylab.backend_inline": [ - "doc/users/interactive.rst:257" + "doc/users/explain/interactive.rst:261" ], "kde.covariance_factor": [ "lib/matplotlib/mlab.py:docstring of matplotlib.mlab.GaussianKDE:41" @@ -855,111 +784,16 @@ "lib/matplotlib/mlab.py:docstring of matplotlib.mlab.GaussianKDE:45", "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.violinplot:46" ], - "labelcolor": [ - "doc/users/prev_whats_new/whats_new_2.0.0.rst:120", - "doc/users/prev_whats_new/whats_new_2.0.0.rst:123" - ], - "legend.Legend.set_draggable()": [ - "doc/api/prev_api_changes/api_changes_3.1.0.rst:499" - ], - "levels": [ - "doc/api/prev_api_changes/api_changes_3.0.0.rst:197" - ], "load_char": [ - "doc/gallery/misc/ftface_props.rst:16" + "doc/gallery/misc/ftface_props.rst:25" ], "mainloop": [ - "lib/matplotlib/backends/backend_nbagg.py:docstring of matplotlib.backends.backend_nbagg._BackendNbAgg.show:4" + "lib/matplotlib/backends/backend_nbagg.py:docstring of matplotlib.backends.backend_nbagg._BackendNbAgg.show:4", + "lib/matplotlib/backends/backend_webagg.py:docstring of matplotlib.backends.backend_webagg._BackendWebAgg.show:4" ], "make_image": [ "lib/matplotlib/image.py:docstring of matplotlib.image.composite_images:9" ], - "markevery": [ - "doc/users/prev_whats_new/whats_new_1.4.rst:212", - "doc/users/prev_whats_new/whats_new_1.4.rst:214", - "doc/users/prev_whats_new/whats_new_3.0.rst:110" - ], - "matplotlib.animation.AVConvBase.output_args": [ - "lib/matplotlib/animation.py:docstring of matplotlib.animation.MovieWriter.isAvailable:1::1" - ], - "matplotlib.animation.AVConvFileWriter.args_key": [ - "doc/api/_as_gen/matplotlib.animation.AVConvFileWriter.rst:39::1" - ], - "matplotlib.animation.AVConvFileWriter.bin_path": [ - "doc/api/_as_gen/matplotlib.animation.AVConvFileWriter.rst:28::1" - ], - "matplotlib.animation.AVConvFileWriter.cleanup": [ - "doc/api/_as_gen/matplotlib.animation.AVConvFileWriter.rst:28::1" - ], - "matplotlib.animation.AVConvFileWriter.clear_temp": [ - "doc/api/_as_gen/matplotlib.animation.AVConvFileWriter.rst:39::1" - ], - "matplotlib.animation.AVConvFileWriter.exec_key": [ - "doc/api/_as_gen/matplotlib.animation.AVConvFileWriter.rst:39::1" - ], - "matplotlib.animation.AVConvFileWriter.finish": [ - "doc/api/_as_gen/matplotlib.animation.AVConvFileWriter.rst:28::1" - ], - "matplotlib.animation.AVConvFileWriter.frame_format": [ - "doc/api/_as_gen/matplotlib.animation.AVConvFileWriter.rst:39::1" - ], - "matplotlib.animation.AVConvFileWriter.frame_size": [ - "doc/api/_as_gen/matplotlib.animation.AVConvFileWriter.rst:39::1" - ], - "matplotlib.animation.AVConvFileWriter.grab_frame": [ - "doc/api/_as_gen/matplotlib.animation.AVConvFileWriter.rst:28::1" - ], - "matplotlib.animation.AVConvFileWriter.isAvailable": [ - "doc/api/_as_gen/matplotlib.animation.AVConvFileWriter.rst:28::1" - ], - "matplotlib.animation.AVConvFileWriter.output_args": [ - "doc/api/_as_gen/matplotlib.animation.AVConvFileWriter.rst:39::1" - ], - "matplotlib.animation.AVConvFileWriter.saving": [ - "doc/api/_as_gen/matplotlib.animation.AVConvFileWriter.rst:28::1" - ], - "matplotlib.animation.AVConvFileWriter.setup": [ - "doc/api/_as_gen/matplotlib.animation.AVConvFileWriter.rst:28::1" - ], - "matplotlib.animation.AVConvFileWriter.supported_formats": [ - "doc/api/_as_gen/matplotlib.animation.AVConvFileWriter.rst:39::1" - ], - "matplotlib.animation.AVConvWriter.args_key": [ - "doc/api/_as_gen/matplotlib.animation.AVConvWriter.rst:37::1" - ], - "matplotlib.animation.AVConvWriter.bin_path": [ - "doc/api/_as_gen/matplotlib.animation.AVConvWriter.rst:28::1" - ], - "matplotlib.animation.AVConvWriter.cleanup": [ - "doc/api/_as_gen/matplotlib.animation.AVConvWriter.rst:28::1" - ], - "matplotlib.animation.AVConvWriter.exec_key": [ - "doc/api/_as_gen/matplotlib.animation.AVConvWriter.rst:37::1" - ], - "matplotlib.animation.AVConvWriter.finish": [ - "doc/api/_as_gen/matplotlib.animation.AVConvWriter.rst:28::1" - ], - "matplotlib.animation.AVConvWriter.frame_size": [ - "doc/api/_as_gen/matplotlib.animation.AVConvWriter.rst:37::1" - ], - "matplotlib.animation.AVConvWriter.grab_frame": [ - "doc/api/_as_gen/matplotlib.animation.AVConvWriter.rst:28::1" - ], - "matplotlib.animation.AVConvWriter.isAvailable": [ - "doc/api/_as_gen/matplotlib.animation.AVConvWriter.rst:28::1" - ], - "matplotlib.animation.AVConvWriter.output_args": [ - "doc/api/_as_gen/matplotlib.animation.AVConvWriter.rst:37::1" - ], - "matplotlib.animation.AVConvWriter.saving": [ - "doc/api/_as_gen/matplotlib.animation.AVConvWriter.rst:28::1" - ], - "matplotlib.animation.AVConvWriter.setup": [ - "doc/api/_as_gen/matplotlib.animation.AVConvWriter.rst:28::1" - ], - "matplotlib.animation.AVConvWriter.supported_formats": [ - "doc/api/_as_gen/matplotlib.animation.AVConvWriter.rst:37::1" - ], "matplotlib.animation.ArtistAnimation.new_frame_seq": [ "doc/api/_as_gen/matplotlib.animation.ArtistAnimation.rst:23::1" ], @@ -981,21 +815,12 @@ "matplotlib.animation.ArtistAnimation.to_jshtml": [ "doc/api/_as_gen/matplotlib.animation.ArtistAnimation.rst:23::1" ], - "matplotlib.animation.FFMpegFileWriter.args_key": [ - "lib/matplotlib/animation.py:docstring of matplotlib.animation.FFMpegFileWriter.supported_formats:1::1" - ], "matplotlib.animation.FFMpegFileWriter.bin_path": [ "doc/api/_as_gen/matplotlib.animation.FFMpegFileWriter.rst:28::1" ], "matplotlib.animation.FFMpegFileWriter.cleanup": [ "doc/api/_as_gen/matplotlib.animation.FFMpegFileWriter.rst:28::1" ], - "matplotlib.animation.FFMpegFileWriter.clear_temp": [ - "lib/matplotlib/animation.py:docstring of matplotlib.animation.FFMpegFileWriter.supported_formats:1::1" - ], - "matplotlib.animation.FFMpegFileWriter.exec_key": [ - "lib/matplotlib/animation.py:docstring of matplotlib.animation.FFMpegFileWriter.supported_formats:1::1" - ], "matplotlib.animation.FFMpegFileWriter.finish": [ "doc/api/_as_gen/matplotlib.animation.FFMpegFileWriter.rst:28::1" ], @@ -1020,23 +845,17 @@ "matplotlib.animation.FFMpegFileWriter.setup": [ "doc/api/_as_gen/matplotlib.animation.FFMpegFileWriter.rst:28::1" ], - "matplotlib.animation.FFMpegWriter.args_key": [ - "doc/api/_as_gen/matplotlib.animation.FFMpegWriter.rst:37::1" - ], "matplotlib.animation.FFMpegWriter.bin_path": [ "doc/api/_as_gen/matplotlib.animation.FFMpegWriter.rst:28::1" ], "matplotlib.animation.FFMpegWriter.cleanup": [ "doc/api/_as_gen/matplotlib.animation.FFMpegWriter.rst:28::1" ], - "matplotlib.animation.FFMpegWriter.exec_key": [ - "doc/api/_as_gen/matplotlib.animation.FFMpegWriter.rst:37::1" - ], "matplotlib.animation.FFMpegWriter.finish": [ "doc/api/_as_gen/matplotlib.animation.FFMpegWriter.rst:28::1" ], "matplotlib.animation.FFMpegWriter.frame_size": [ - "doc/api/_as_gen/matplotlib.animation.FFMpegWriter.rst:37::1" + "doc/api/_as_gen/matplotlib.animation.FFMpegWriter.rst:35::1" ], "matplotlib.animation.FFMpegWriter.grab_frame": [ "doc/api/_as_gen/matplotlib.animation.FFMpegWriter.rst:28::1" @@ -1045,7 +864,7 @@ "doc/api/_as_gen/matplotlib.animation.FFMpegWriter.rst:28::1" ], "matplotlib.animation.FFMpegWriter.output_args": [ - "doc/api/_as_gen/matplotlib.animation.FFMpegWriter.rst:37::1" + "doc/api/_as_gen/matplotlib.animation.FFMpegWriter.rst:35::1" ], "matplotlib.animation.FFMpegWriter.saving": [ "doc/api/_as_gen/matplotlib.animation.FFMpegWriter.rst:28::1" @@ -1054,10 +873,7 @@ "doc/api/_as_gen/matplotlib.animation.FFMpegWriter.rst:28::1" ], "matplotlib.animation.FFMpegWriter.supported_formats": [ - "doc/api/_as_gen/matplotlib.animation.FFMpegWriter.rst:37::1" - ], - "matplotlib.animation.FileMovieWriter.args_key": [ - "lib/matplotlib/animation.py:docstring of matplotlib.animation.FileMovieWriter.clear_temp:1::1" + "doc/api/_as_gen/matplotlib.animation.FFMpegWriter.rst:35::1" ], "matplotlib.animation.FileMovieWriter.bin_path": [ "doc/api/_as_gen/matplotlib.animation.FileMovieWriter.rst:28::1" @@ -1065,11 +881,8 @@ "matplotlib.animation.FileMovieWriter.cleanup": [ "doc/api/_as_gen/matplotlib.animation.FileMovieWriter.rst:28::1" ], - "matplotlib.animation.FileMovieWriter.exec_key": [ - "lib/matplotlib/animation.py:docstring of matplotlib.animation.FileMovieWriter.clear_temp:1::1" - ], "matplotlib.animation.FileMovieWriter.frame_size": [ - "lib/matplotlib/animation.py:docstring of matplotlib.animation.FileMovieWriter.clear_temp:1::1" + "lib/matplotlib/animation.py:docstring of matplotlib.animation.FileMovieWriter.finish:1::1" ], "matplotlib.animation.FileMovieWriter.isAvailable": [ "doc/api/_as_gen/matplotlib.animation.FileMovieWriter.rst:28::1" @@ -1078,7 +891,7 @@ "doc/api/_as_gen/matplotlib.animation.FileMovieWriter.rst:28::1" ], "matplotlib.animation.FileMovieWriter.supported_formats": [ - "lib/matplotlib/animation.py:docstring of matplotlib.animation.FileMovieWriter.clear_temp:1::1" + "lib/matplotlib/animation.py:docstring of matplotlib.animation.FileMovieWriter.finish:1::1" ], "matplotlib.animation.FuncAnimation.pause": [ "lib/matplotlib/animation.py:docstring of matplotlib.animation.FuncAnimation.new_frame_seq:1::1" @@ -1101,39 +914,24 @@ "matplotlib.animation.HTMLWriter.cleanup": [ "doc/api/_as_gen/matplotlib.animation.HTMLWriter.rst:28::1" ], - "matplotlib.animation.HTMLWriter.clear_temp": [ - "lib/matplotlib/animation.py:docstring of matplotlib.animation.HTMLWriter.args_key:1::1" - ], - "matplotlib.animation.HTMLWriter.exec_key": [ - "lib/matplotlib/animation.py:docstring of matplotlib.animation.HTMLWriter.args_key:1::1" - ], "matplotlib.animation.HTMLWriter.frame_format": [ - "lib/matplotlib/animation.py:docstring of matplotlib.animation.HTMLWriter.args_key:1::1" + "lib/matplotlib/animation.py:docstring of matplotlib.animation.HTMLWriter.finish:1::1" ], "matplotlib.animation.HTMLWriter.frame_size": [ - "lib/matplotlib/animation.py:docstring of matplotlib.animation.HTMLWriter.args_key:1::1" + "lib/matplotlib/animation.py:docstring of matplotlib.animation.HTMLWriter.finish:1::1" ], "matplotlib.animation.HTMLWriter.saving": [ "doc/api/_as_gen/matplotlib.animation.HTMLWriter.rst:28::1" ], - "matplotlib.animation.ImageMagickFileWriter.args_key": [ - "lib/matplotlib/animation.py:docstring of matplotlib.animation.ImageMagickFileWriter.supported_formats:1::1" - ], "matplotlib.animation.ImageMagickFileWriter.bin_path": [ "doc/api/_as_gen/matplotlib.animation.ImageMagickFileWriter.rst:28::1" ], "matplotlib.animation.ImageMagickFileWriter.cleanup": [ "doc/api/_as_gen/matplotlib.animation.ImageMagickFileWriter.rst:28::1" ], - "matplotlib.animation.ImageMagickFileWriter.clear_temp": [ - "lib/matplotlib/animation.py:docstring of matplotlib.animation.ImageMagickFileWriter.supported_formats:1::1" - ], "matplotlib.animation.ImageMagickFileWriter.delay": [ "lib/matplotlib/animation.py:docstring of matplotlib.animation.ImageMagickFileWriter.supported_formats:1::1" ], - "matplotlib.animation.ImageMagickFileWriter.exec_key": [ - "lib/matplotlib/animation.py:docstring of matplotlib.animation.ImageMagickFileWriter.supported_formats:1::1" - ], "matplotlib.animation.ImageMagickFileWriter.finish": [ "doc/api/_as_gen/matplotlib.animation.ImageMagickFileWriter.rst:28::1" ], @@ -1158,9 +956,6 @@ "matplotlib.animation.ImageMagickFileWriter.setup": [ "doc/api/_as_gen/matplotlib.animation.ImageMagickFileWriter.rst:28::1" ], - "matplotlib.animation.ImageMagickWriter.args_key": [ - "doc/api/_as_gen/matplotlib.animation.ImageMagickWriter.rst:38::1" - ], "matplotlib.animation.ImageMagickWriter.bin_path": [ "doc/api/_as_gen/matplotlib.animation.ImageMagickWriter.rst:28::1" ], @@ -1168,16 +963,13 @@ "doc/api/_as_gen/matplotlib.animation.ImageMagickWriter.rst:28::1" ], "matplotlib.animation.ImageMagickWriter.delay": [ - "doc/api/_as_gen/matplotlib.animation.ImageMagickWriter.rst:38::1" - ], - "matplotlib.animation.ImageMagickWriter.exec_key": [ - "doc/api/_as_gen/matplotlib.animation.ImageMagickWriter.rst:38::1" + "doc/api/_as_gen/matplotlib.animation.ImageMagickWriter.rst:36::1" ], "matplotlib.animation.ImageMagickWriter.finish": [ "doc/api/_as_gen/matplotlib.animation.ImageMagickWriter.rst:28::1" ], "matplotlib.animation.ImageMagickWriter.frame_size": [ - "doc/api/_as_gen/matplotlib.animation.ImageMagickWriter.rst:38::1" + "doc/api/_as_gen/matplotlib.animation.ImageMagickWriter.rst:36::1" ], "matplotlib.animation.ImageMagickWriter.grab_frame": [ "doc/api/_as_gen/matplotlib.animation.ImageMagickWriter.rst:28::1" @@ -1186,7 +978,7 @@ "doc/api/_as_gen/matplotlib.animation.ImageMagickWriter.rst:28::1" ], "matplotlib.animation.ImageMagickWriter.output_args": [ - "doc/api/_as_gen/matplotlib.animation.ImageMagickWriter.rst:38::1" + "doc/api/_as_gen/matplotlib.animation.ImageMagickWriter.rst:36::1" ], "matplotlib.animation.ImageMagickWriter.saving": [ "doc/api/_as_gen/matplotlib.animation.ImageMagickWriter.rst:28::1" @@ -1195,10 +987,10 @@ "doc/api/_as_gen/matplotlib.animation.ImageMagickWriter.rst:28::1" ], "matplotlib.animation.ImageMagickWriter.supported_formats": [ - "doc/api/_as_gen/matplotlib.animation.ImageMagickWriter.rst:38::1" + "doc/api/_as_gen/matplotlib.animation.ImageMagickWriter.rst:36::1" ], "matplotlib.animation.MovieWriter.frame_size": [ - "lib/matplotlib/animation.py:docstring of matplotlib.animation.MovieWriter.args_key:1::1" + "lib/matplotlib/animation.py:docstring of matplotlib.animation.MovieWriter.bin_path:1::1" ], "matplotlib.animation.MovieWriter.saving": [ "doc/api/_as_gen/matplotlib.animation.MovieWriter.rst:28::1" @@ -1240,10 +1032,8 @@ "doc/api/prev_api_changes/api_changes_3.1.0.rst:290" ], "matplotlib.dates.rrulewrapper": [ - "lib/matplotlib/dates.py:docstring of matplotlib.dates:121" - ], - "matplotlib.sphinxext.mathmpl": [ - "doc/users/prev_whats_new/whats_new_3.0.rst:224" + "doc/gallery/ticks/date_formatters_locators.rst:136", + "lib/matplotlib/dates.py:docstring of matplotlib.dates:143" ], "matplotlib.style.core.STYLE_BLACKLIST": [ "doc/api/prev_api_changes/api_changes_3.0.0.rst:298", @@ -1256,11 +1046,17 @@ "doc/api/prev_api_changes/api_changes_3.1.0.rst:947" ], "mpl_toolkits.axislines.Axes": [ - "lib/mpl_toolkits/axisartist/axis_artist.py:docstring of mpl_toolkits.axisartist.axis_artist:26" + "lib/mpl_toolkits/axisartist/axis_artist.py:docstring of mpl_toolkits.axisartist.axis_artist:7" ], "next_whats_new": [ "doc/users/next_whats_new/README.rst:6" ], + "option_scale_image": [ + "lib/matplotlib/backends/backend_cairo.py:docstring of matplotlib.backends.backend_cairo.RendererCairo.draw_image:22", + "lib/matplotlib/backends/backend_pdf.py:docstring of matplotlib.backends.backend_pdf.RendererPdf.draw_image:22", + "lib/matplotlib/backends/backend_ps.py:docstring of matplotlib.backends.backend_ps.RendererPS.draw_image:22", + "lib/matplotlib/backends/backend_template.py:docstring of matplotlib.backends.backend_template.RendererTemplate.draw_image:22" + ], "plot": [ "lib/matplotlib/sphinxext/plot_directive.py:docstring of matplotlib.sphinxext.plot_directive:4" ], @@ -1270,11 +1066,8 @@ "print_xyz": [ "lib/matplotlib/backends/backend_template.py:docstring of matplotlib.backends.backend_template:22" ], - "pyplot.set_loglevel": [ - "doc/users/prev_whats_new/whats_new_3.1.0.rst:374" - ], "rrulewrapper": [ - "lib/matplotlib/dates.py:docstring of matplotlib.dates:121" + "lib/matplotlib/dates.py:docstring of matplotlib.dates:143" ], "scipy.stats.norm.pdf": [ "doc/api/prev_api_changes/api_changes_3.1.0.rst:569", @@ -1286,9 +1079,6 @@ "self.vertices": [ "lib/matplotlib/path.py:docstring of matplotlib.path.Path.codes:2" ], - "set_size": [ - "doc/users/prev_whats_new/whats_new_1.4.rst:223" - ], "set_xbound": [ "lib/mpl_toolkits/mplot3d/axes3d.py:docstring of mpl_toolkits.mplot3d.axes3d.Axes3D.get_xlim3d:22" ], @@ -1298,35 +1088,15 @@ "streamplot.Grid": [ "doc/api/prev_api_changes/api_changes_3.1.0.rst:479" ], - "style.available": [ - "lib/matplotlib/style/__init__.py:docstring of matplotlib.style.core.context:12", - "lib/matplotlib/style/__init__.py:docstring of matplotlib.style.core.use:19" - ], - "to_html5_video": [ - "doc/users/prev_whats_new/whats_new_1.5.rst:728" - ], "toggled": [ "lib/matplotlib/backend_tools.py:docstring of matplotlib.backend_tools.AxisScaleBase.disable:4", "lib/matplotlib/backend_tools.py:docstring of matplotlib.backend_tools.AxisScaleBase.enable:4", "lib/matplotlib/backend_tools.py:docstring of matplotlib.backend_tools.AxisScaleBase.trigger:2", - "lib/matplotlib/backend_tools.py:docstring of matplotlib.backend_tools.ToolFullScreen.disable:4", - "lib/matplotlib/backend_tools.py:docstring of matplotlib.backend_tools.ToolFullScreen.enable:4", "lib/matplotlib/backend_tools.py:docstring of matplotlib.backend_tools.ZoomPanBase.trigger:2" ], "tool_removed_event": [ "lib/matplotlib/backend_bases.py:docstring of matplotlib.backend_bases.ToolContainerBase.remove_toolitem:6" ], - "toolbar": [ - "doc/users/prev_whats_new/whats_new_1.5.rst:615" - ], - "trigger": [ - "doc/users/prev_whats_new/whats_new_1.5.rst:615", - "lib/matplotlib/backend_tools.py:docstring of matplotlib.backend_tools.ToolFullScreen.disable:4", - "lib/matplotlib/backend_tools.py:docstring of matplotlib.backend_tools.ToolFullScreen.enable:4" - ], - "w_pad": [ - "lib/matplotlib/figure.py:docstring of matplotlib.figure.Figure.set_constrained_layout:5" - ], "whats_new.rst": [ "doc/users/next_whats_new/README.rst:6" ], @@ -1337,4 +1107,4 @@ "lib/mpl_toolkits/mplot3d/axes3d.py:docstring of mpl_toolkits.mplot3d.axes3d.Axes3D.get_ylim3d:24" ] } -} \ No newline at end of file +} diff --git a/doc/resources/index.rst b/doc/resources/index.rst deleted file mode 100644 index 3dd15348f091..000000000000 --- a/doc/resources/index.rst +++ /dev/null @@ -1,63 +0,0 @@ -.. _resources-index: - -******************* - External Resources -******************* - - -============================= - Books, Chapters and Articles -============================= - -* `Mastering matplotlib - `_ - by Duncan M. McGreggor - -* `Interactive Applications Using Matplotlib - `_ - by Benjamin Root - -* `Matplotlib for Python Developers - `_ - by Sandro Tosi - -* `Matplotlib chapter `_ - by John Hunter and Michael Droettboom in The Architecture of Open Source - Applications - -* `Ten Simple Rules for Better Figures - `_ - by Nicolas P. Rougier, Michael Droettboom and Philip E. Bourne - -* `Learning Scientific Programming with Python chapter 7 - `_ - by Christian Hill - -======= - Videos -======= - -* `Plotting with matplotlib `_ - by Mike Müller - -* `Introduction to NumPy and Matplotlib - `_ by Eric Jones - -* `Anatomy of Matplotlib - `_ - by Benjamin Root - -* `Data Visualization Basics with Python (O'Reilly) - `_ - by Randal S. Olson - -========== - Tutorials -========== - -* `Matplotlib tutorial `_ - by Nicolas P. Rougier - -* `Anatomy of Matplotlib - IPython Notebooks - `_ - by Benjamin Root diff --git a/doc/sphinxext/custom_roles.py b/doc/sphinxext/custom_roles.py index 5faa42172a64..3dcecc3df733 100644 --- a/doc/sphinxext/custom_roles.py +++ b/doc/sphinxext/custom_roles.py @@ -1,30 +1,75 @@ +from urllib.parse import urlsplit, urlunsplit + from docutils import nodes -from os.path import sep + from matplotlib import rcParamsDefault -def rcparam_role(name, rawtext, text, lineno, inliner, options={}, content=[]): - rendered = nodes.Text(f'rcParams["{text}"]') +class QueryReference(nodes.Inline, nodes.TextElement): + """ + Wraps a reference or pending reference to add a query string. + + The query string is generated from the attributes added to this node. + + Also equivalent to a `~docutils.nodes.literal` node. + """ + + def to_query_string(self): + """Generate query string from node attributes.""" + return '&'.join(f'{name}={value}' for name, value in self.attlist()) + + +def visit_query_reference_node(self, node): + """ + Resolve *node* into query strings on its ``reference`` children. - source = inliner.document.attributes['source'].replace(sep, '/') - rel_source = source.split('/doc/', 1)[1] + Then act as if this is a `~docutils.nodes.literal`. + """ + query = node.to_query_string() + for refnode in node.findall(nodes.reference): + uri = urlsplit(refnode['refuri'])._replace(query=query) + refnode['refuri'] = urlunsplit(uri) - levels = rel_source.count('/') - refuri = ('../' * levels + - 'tutorials/introductory/customizing.html' + - f"?highlight={text}#a-sample-matplotlibrc-file") + self.visit_literal(node) - ref = nodes.reference(rawtext, rendered, refuri=refuri) - node_list = [nodes.literal('', '', ref)] - if text in rcParamsDefault: + +def depart_query_reference_node(self, node): + """ + Act as if this is a `~docutils.nodes.literal`. + """ + self.depart_literal(node) + + +def rcparam_role(name, rawtext, text, lineno, inliner, options={}, content=[]): + # Generate a pending cross-reference so that Sphinx will ensure this link + # isn't broken at some point in the future. + title = f'rcParams["{text}"]' + target = 'matplotlibrc-sample' + ref_nodes, messages = inliner.interpreted(title, f'{title} <{target}>', + 'ref', lineno) + + qr = QueryReference(rawtext, highlight=text) + qr += ref_nodes + node_list = [qr] + + # The default backend would be printed as "agg", but that's not correct (as + # the default is actually determined by fallback). + if text in rcParamsDefault and text != "backend": node_list.extend([ nodes.Text(' (default: '), nodes.literal('', repr(rcParamsDefault[text])), nodes.Text(')'), ]) - return node_list, [] + + return node_list, messages def setup(app): app.add_role("rc", rcparam_role) + app.add_node( + QueryReference, + html=(visit_query_reference_node, depart_query_reference_node), + latex=(visit_query_reference_node, depart_query_reference_node), + text=(visit_query_reference_node, depart_query_reference_node), + ) return {"parallel_read_safe": True, "parallel_write_safe": True} diff --git a/doc/sphinxext/gallery_order.py b/doc/sphinxext/gallery_order.py index 1c1034ec5819..3632666d336e 100644 --- a/doc/sphinxext/gallery_order.py +++ b/doc/sphinxext/gallery_order.py @@ -23,7 +23,13 @@ '../examples/showcase', '../tutorials/introductory', '../tutorials/intermediate', - '../tutorials/advanced'] + '../tutorials/advanced', + '../plot_types/basic', + '../plot_types/arrays', + '../plot_types/stats', + '../plot_types/unstructured', + '../plot_types/3D', + ] class MplExplicitOrder(ExplicitOrder): @@ -45,9 +51,9 @@ def __call__(self, item): list_all = [ # **Tutorials** # introductory - "usage", "pyplot", "sample_plots", "images", "lifecycle", "customizing", + "quick_start", "pyplot", "images", "lifecycle", "customizing", # intermediate - "artists", "legend_guide", "color_cycle", "gridspec", + "artists", "legend_guide", "color_cycle", "constrainedlayout_guide", "tight_layout_guide", # advanced # text @@ -60,6 +66,18 @@ def __call__(self, item): "color_demo", # pies "pie_features", "pie_demo2", + + # **Plot Types + # Basic + "plot", "scatter_plot", "bar", "stem", "step", "fill_between", + # Arrays + "imshow", "pcolormesh", "contour", "contourf", + "barbs", "quiver", "streamplot", + # Stats + "hist_plot", "boxplot_plot", "errorbar_plot", "violin", + "eventplot", "hist2d", "hexbin", "pie", + # Unstructured + "tricontour", "tricontourf", "tripcolor", "triplot", ] explicit_subsection_order = [item + ".py" for item in list_all] diff --git a/doc/sphinxext/math_symbol_table.py b/doc/sphinxext/math_symbol_table.py index 3041b15b36a8..6609f91bbd10 100644 --- a/doc/sphinxext/math_symbol_table.py +++ b/doc/sphinxext/math_symbol_table.py @@ -1,6 +1,6 @@ from docutils.parsers.rst import Directive -from matplotlib import mathtext +from matplotlib import _mathtext, _mathtext_data symbols = [ @@ -103,9 +103,9 @@ def run(state_machine): def render_symbol(sym): if sym.startswith("\\"): sym = sym[1:] - if sym not in {*mathtext.Parser._overunder_functions, - *mathtext.Parser._function_names}: - sym = chr(mathtext.tex2uni[sym]) + if sym not in (_mathtext.Parser._overunder_functions | + _mathtext.Parser._function_names): + sym = chr(_mathtext_data.tex2uni[sym]) return f'\\{sym}' if sym in ('\\', '|') else sym lines = [] @@ -149,7 +149,6 @@ def setup(app): if __name__ == "__main__": # Do some verification of the tables - from matplotlib import _mathtext_data print("SYMBOLS NOT IN STIX:") all_symbols = {} diff --git a/doc/sphinxext/missing_references.py b/doc/sphinxext/missing_references.py index 6aa82a4dd17d..12d836f296f1 100644 --- a/doc/sphinxext/missing_references.py +++ b/doc/sphinxext/missing_references.py @@ -90,7 +90,7 @@ def get_location(node, app): Usually, this will be of the form "path/to/file:linenumber". Two special values can be emitted, "" for paths which are not contained in this source tree (e.g. docstrings included from - other modules) or "", inidcating that the sphinx application + other modules) or "", indicating that the sphinx application cannot locate the original source file (usually because an extension has injected text into the sphinx parsing engine). """ diff --git a/doc/sphinxext/redirect_from.py b/doc/sphinxext/redirect_from.py index ff38260569f5..09e3af4493de 100644 --- a/doc/sphinxext/redirect_from.py +++ b/doc/sphinxext/redirect_from.py @@ -3,7 +3,7 @@ ==================================== If an rst file is moved or its content subsumed in a different file, it -is desireable to redirect the old file to the new or existing file. This +is desirable to redirect the old file to the new or existing file. This extension enables this with a simple html refresh. For example suppose ``doc/topic/old-page.rst`` is removed and its content @@ -15,8 +15,10 @@ This creates in the build directory a file ``build/html/topic/old-page.html`` that contains a relative refresh:: + + @@ -32,13 +34,16 @@ from pathlib import Path from docutils.parsers.rst import Directive +from sphinx.domains import Domain from sphinx.util import logging logger = logging.getLogger(__name__) -HTML_TEMPLATE = """ +HTML_TEMPLATE = """ + + @@ -48,25 +53,53 @@ def setup(app): RedirectFrom.app = app app.add_directive("redirect-from", RedirectFrom) + app.add_domain(RedirectFromDomain) app.connect("build-finished", _generate_redirects) + metadata = {'parallel_read_safe': True} + return metadata + + +class RedirectFromDomain(Domain): + """ + The sole purpose of this domain is a parallel_read_safe data store for the + redirects mapping. + """ + name = 'redirect_from' + label = 'redirect_from' + + @property + def redirects(self): + """The mapping of the redirects.""" + return self.data.setdefault('redirects', {}) + + def clear_doc(self, docnames): + self.redirects.clear() + + def merge_domaindata(self, docnames, otherdata): + for src, dst in otherdata['redirects'].items(): + if src not in self.redirects: + self.redirects[src] = dst + elif self.redirects[src] != dst: + raise ValueError( + f"Inconsistent redirections from {src} to " + F"{self.redirects[src]} and {otherdata.redirects[src]}") + class RedirectFrom(Directive): required_arguments = 1 - redirects = {} def run(self): redirected_doc, = self.arguments env = self.app.env - builder = self.app.builder + domain = env.get_domain('redirect_from') current_doc = env.path2doc(self.state.document.current_source) redirected_reldoc, _ = env.relfn2path(redirected_doc, current_doc) - if redirected_reldoc in self.redirects: + if redirected_reldoc in domain.redirects: raise ValueError( f"{redirected_reldoc} is already noted as redirecting to " - f"{self.redirects[redirected_reldoc]}") - self.redirects[redirected_reldoc] = builder.get_relative_uri( - redirected_reldoc, current_doc) + f"{domain.redirects[redirected_reldoc]}") + domain.redirects[redirected_reldoc] = current_doc return [] @@ -74,9 +107,9 @@ def _generate_redirects(app, exception): builder = app.builder if builder.name != "html" or exception: return - for k, v in RedirectFrom.redirects.items(): + for k, v in app.env.get_domain('redirect_from').redirects.items(): p = Path(app.outdir, k + builder.out_suffix) - html = HTML_TEMPLATE.format(v=v) + html = HTML_TEMPLATE.format(v=builder.get_relative_uri(k, v)) if p.is_file(): if p.read_text() != html: logger.warning(f'A redirect-from directive is trying to ' @@ -85,4 +118,4 @@ def _generate_redirects(app, exception): else: logger.info(f'making refresh html file: {k} redirect to {v}') p.parent.mkdir(parents=True, exist_ok=True) - p.write_text(html) + p.write_text(html, encoding='utf-8') diff --git a/doc/thirdpartypackages/index.rst b/doc/thirdpartypackages/index.rst index 6667e6c23dbc..81dc4d710a52 100644 --- a/doc/thirdpartypackages/index.rst +++ b/doc/thirdpartypackages/index.rst @@ -1,347 +1,5 @@ -.. _thirdparty-index: +:orphan: -******************** -Third party packages -******************** +.. raw:: html -Several external packages that extend or build on Matplotlib functionality are -listed below. They are maintained and distributed separately from Matplotlib -and thus need to be installed individually. - -Please submit an issue or pull request on GitHub if you have created -a package that you would like to have included. We are also happy to -host third party packages within the `Matplotlib GitHub Organization -`_. - -Mapping toolkits -**************** - -Basemap -======= -`Basemap `_ plots data on map projections, -with continental and political boundaries. - -.. image:: /_static/basemap_contour1.png - :height: 400px - -Cartopy -======= -`Cartopy `_ builds on top -of Matplotlib to provide object oriented map projection definitions -and close integration with Shapely for powerful yet easy-to-use vector -data processing tools. An example plot from the `Cartopy gallery -`_: - -.. image:: /_static/cartopy_hurricane_katrina_01_00.png - :height: 400px - -Geoplot -======= -`Geoplot `_ builds on top -of Matplotlib and Cartopy to provide a "standard library" of simple, powerful, -and customizable plot types. An example plot from the `Geoplot gallery -`_: - -.. image:: /_static/geoplot_nyc_traffic_tickets.png - :height: 400px - -Ridge Map -========= -`ridge_map `_ uses Matplotlib, -SRTM.py, NumPy, and scikit-image to make ridge plots of your favorite -ridges. - -.. image:: /_static/ridge_map_white_mountains.png - :height: 364px - -Declarative libraries -********************* - -ggplot -====== -`ggplot `_ is a port of the R ggplot2 package -to python based on Matplotlib. - -.. image:: /_static/ggplot.png - :height: 195px - -holoviews -========= -`holoviews `_ makes it easier to visualize data -interactively, especially in a `Jupyter notebook `_, by -providing a set of declarative plotting objects that store your data and -associated metadata. Your data is then immediately visualizable alongside or -overlaid with other data, either statically or with automatically provided -widgets for parameter exploration. - -.. image:: /_static/holoviews.png - :height: 354px - -plotnine -======== - -`plotnine `_ implements a grammar -of graphics, similar to R's `ggplot2 `_. -The grammar allows users to compose plots by explicitly mapping data to the -visual objects that make up the plot. - -.. image:: /_static/plotnine.png - -Specialty plots -*************** - -Broken Axes -=========== -`brokenaxes `_ supplies an axes -class that can have a visual break to indicate a discontinuous range. - -.. image:: /_static/brokenaxes.png - -DeCiDa -====== - -`DeCiDa `_ is a library of functions -and classes for electron device characterization, electronic circuit design and -general data visualization and analysis. - -matplotlib-scalebar -=================== - -`matplotlib-scalebar `_ provides a new artist to display a scale bar, aka micron bar. -It is particularly useful when displaying calibrated images plotted using ``plt.imshow(...)``. - -.. image:: /_static/gold_on_carbon.jpg - -Matplotlib-Venn -=============== -`Matplotlib-Venn `_ provides a -set of functions for plotting 2- and 3-set area-weighted (or unweighted) Venn -diagrams. - -mpl-probscale -============= -`mpl-probscale `_ is a small extension -that allows Matplotlib users to specify probability scales. Simply importing the -``probscale`` module registers the scale with Matplotlib, making it accessible -via e.g., ``ax.set_xscale('prob')`` or ``plt.yscale('prob')``. - -.. image:: /_static/probscale_demo.png - -mpl-scatter-density -=================== - -`mpl-scatter-density `_ is a -small package that makes it easy to make scatter plots of large numbers -of points using a density map. The following example contains around 13 million -points and the plotting (excluding reading in the data) took less than a -second on an average laptop: - -.. image:: /_static/mpl-scatter-density.png - :height: 400px - -When used in interactive mode, the density map is downsampled on-the-fly while -panning/zooming in order to provide a smooth interactive experience. - -mplstereonet -============ -`mplstereonet `_ provides -stereonets for plotting and analyzing orientation data in Matplotlib. - -Natgrid -======= -`mpl_toolkits.natgrid `_ is an interface -to the natgrid C library for gridding irregularly spaced data. - -pyUpSet -======= -`pyUpSet `_ is a -static Python implementation of the `UpSet suite by Lex et al. -`_ to explore complex intersections of -sets and data frames. - -seaborn -======= -`seaborn `_ is a high level interface for drawing -statistical graphics with Matplotlib. It aims to make visualization a central -part of exploring and understanding complex datasets. - -.. image:: /_static/seaborn.png - :height: 157px - -WCSAxes -======= - -The `Astropy `_ core package includes a submodule -called WCSAxes (available at `astropy.visualization.wcsaxes -`_) which -adds Matplotlib projections for Astronomical image data. The following is an -example of a plot made with WCSAxes which includes the original coordinate -system of the image and an overlay of a different coordinate system: - -.. image:: /_static/wcsaxes.jpg - :height: 400px - -Windrose -======== -`Windrose `_ is a Python Matplotlib, -Numpy library to manage wind data, draw windroses (also known as polar rose -plots), draw probability density functions and fit Weibull distributions. - -Yellowbrick -=========== -`Yellowbrick `_ is a suite of visual diagnostic tools for machine learning that enables human steering of the model selection process. Yellowbrick combines scikit-learn with matplotlib using an estimator-based API called the ``Visualizer``, which wraps both sklearn models and matplotlib Axes. ``Visualizer`` objects fit neatly into the machine learning workflow allowing data scientists to integrate visual diagnostic and model interpretation tools into experimentation without extra steps. - -.. image:: /_static/yellowbrick.png - :height: 400px - -Animations -********** - -animatplot -========== -`animatplot `_ is a library for -producing interactive animated plots with the goal of making production of -animated plots almost as easy as static ones. - -.. image:: /_static/animatplot.png - -For an animated version of the above picture and more examples, see the -`animatplot gallery. `_ - -gif -=== -`gif `_ is an ultra lightweight animated gif API. - -.. image:: /_static/gif_attachment_example.png - -numpngw -======= - -`numpngw `_ provides functions for writing -NumPy arrays to PNG and animated PNG files. It also includes the class -``AnimatedPNGWriter`` that can be used to save a Matplotlib animation as an -animated PNG file. See the example on the PyPI page or at the ``numpngw`` -`github repository `_. - -.. image:: /_static/numpngw_animated_example.png - -Interactivity -************* - -mplcursors -========== -`mplcursors `_ provides interactive data -cursors for Matplotlib. - -MplDataCursor -============= -`MplDataCursor `_ is a toolkit -written by Joe Kington to provide interactive "data cursors" (clickable -annotation boxes) for Matplotlib. - -mpl_interactions -================ -`mpl_interactions `_ -makes it easy to create interactive plots controlled by sliders and other -widgets. It also provides several handy capabilities such as manual -image segmentation, comparing cross-sections of arrays, and using the -scroll wheel to zoom. - -.. image:: /_static/mpl-interactions-slider-animated.png - -Rendering backends -****************** - -mplcairo -======== -`mplcairo `_ is a cairo backend for -Matplotlib, with faster and more accurate marker drawing, support for a wider -selection of font formats and complex text layout, and various other features. - -gr -== -`gr `_ is a framework for cross-platform -visualisation applications, which can be used as a high-performance Matplotlib -backend. - -GUI integration -*************** - -wxmplot -======= -`WXMPlot `_ provides advanced wxPython -widgets for plotting and image display of numerical data based on Matplotlib. - -Miscellaneous -************* - -adjustText -========== -`adjustText `_ is a small library for -automatically adjusting text position in Matplotlib plots to minimize overlaps -between them, specified points and other objects. - -.. image:: /_static/adjustText.png - -iTerm2 terminal backend -======================= -`matplotlib_iterm2 `_ is an -external Matplotlib backend using the iTerm2 nightly build inline image display -feature. - -.. image:: /_static/matplotlib_iterm2_demo.png - -mpl-template -============ -`mpl-template `_ provides -a customizable way to add engineering figure elements such as a title block, -border, and logo. - -.. image:: /_static/mpl_template_example.png - :height: 330px - -figpager -======== -`figpager `_ provides customizable figure -elements such as text, lines and images and subplot layout control for single -or multi page output. - - .. image:: /_static/figpager.png - -blume -===== - -`blume `_ provides a replacement for -the Matplotlib ``table`` module. It fixes a number of issues with the -existing table. See the `blume github repository -`_ for more details. - -.. image:: /_static/blume_table_example.png - - -DNA Features Viewer -=================== - -`DNA Features Viewer `_ -provides methods to plot annotated DNA sequence maps (possibly along other Matplotlib -plots) for Bioinformatics and Synthetic Biology applications. - -.. image:: /_static/dna_features_viewer_screenshot.png - -GUI applications -**************** - -sviewgui -======== - -`sviewgui `_ is a PyQt-based GUI for -visualisation of data from csv files or `pandas.DataFrame`\s. Main features: - -- Scatter, line, density, histogram, and box plot types -- Settings for the marker size, line width, number of bins of histogram, - colormap (from cmocean) -- Save figure as editable PDF -- Code of the plotted graph is available so that it can be reused and modified - outside of sviewgui - -.. image:: /_static/sviewgui_sample.png + diff --git a/doc/users/explain/api_interfaces.rst b/doc/users/explain/api_interfaces.rst new file mode 100644 index 000000000000..9a4976c14ff0 --- /dev/null +++ b/doc/users/explain/api_interfaces.rst @@ -0,0 +1,288 @@ +.. _api_interfaces: + +======================================== +Matplotlib Application Interfaces (APIs) +======================================== + +Matplotlib has two major application interfaces, or styles of using the library: + +- An explicit "Axes" interface that uses methods on a Figure or Axes object to + create other Artists, and build a visualization step by step. This has also + been called an "object-oriented" interface. +- An implicit "pyplot" interface that keeps track of the last Figure and Axes + created, and adds Artists to the object it thinks the user wants. + +In addition, a number of downstream libraries (like `pandas` and xarray_) offer +a ``plot`` method implemented directly on their data classes so that users can +call ``data.plot()``. + +.. _xarray: https://xarray.pydata.org + +The difference between these interfaces can be a bit confusing, particularly +given snippets on the web that use one or the other, or sometimes multiple +interfaces in the same example. Here we attempt to point out how the "pyplot" +and downstream interfaces relate to the explicit "Axes" interface to help users +better navigate the library. + + +Native Matplotlib interfaces +---------------------------- + +The explicit "Axes" interface +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The "Axes" interface is how Matplotlib is implemented, and many customizations +and fine-tuning end up being done at this level. + +This interface works by instantiating an instance of a +`~.matplotlib.figure.Figure` class (``fig`` below), using a method +`~.Figure.subplots` method (or similar) on that object to create one or more +`~.matplotlib.axes.Axes` objects (``ax`` below), and then calling drawing +methods on the Axes (``plot`` in this example): + +.. plot:: + :include-source: + :align: center + + import matplotlib.pyplot as plt + + fig = plt.figure() + ax = fig.subplots() + ax.plot([1, 2, 3, 4], [0, 0.5, 1, 0.2]) + +We call this an "explicit" interface because each object is explicitly +referenced, and used to make the next object. Keeping references to the objects +is very flexible, and allows us to customize the objects after they are created, +but before they are displayed. + + +The implicit "pyplot" interface +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The `~.matplotlib.pyplot` module shadows most of the +`~.matplotlib.axes.Axes` plotting methods to give the equivalent of +the above, where the creation of the Figure and Axes is done for the user: + +.. plot:: + :include-source: + :align: center + + import matplotlib.pyplot as plt + + plt.plot([1, 2, 3, 4], [0, 0.5, 1, 0.2]) + +This can be convenient, particularly when doing interactive work or simple +scripts. A reference to the current Figure can be retrieved using +`~.pyplot.gcf` and to the current Axes by `~.pyplot.gca`. The `~.pyplot` module +retains a list of Figures, and each Figure retains a list of Axes on the figure +for the user so that the following: + +.. plot:: + :include-source: + :align: center + + import matplotlib.pyplot as plt + + plt.subplot(1, 2, 1) + plt.plot([1, 2, 3], [0, 0.5, 0.2]) + + plt.subplot(1, 2, 2) + plt.plot([3, 2, 1], [0, 0.5, 0.2]) + +is equivalent to: + +.. plot:: + :include-source: + :align: center + + import matplotlib.pyplot as plt + + plt.subplot(1, 2, 1) + ax = plt.gca() + ax.plot([1, 2, 3], [0, 0.5, 0.2]) + + plt.subplot(1, 2, 2) + ax = plt.gca() + ax.plot([3, 2, 1], [0, 0.5, 0.2]) + +In the explicit interface, this would be: + +.. plot:: + :include-source: + :align: center + + import matplotlib.pyplot as plt + + fig, axs = plt.subplots(1, 2) + axs[0].plot([1, 2, 3], [0, 0.5, 0.2]) + axs[1].plot([3, 2, 1], [0, 0.5, 0.2]) + +Why be explicit? +^^^^^^^^^^^^^^^^ + +What happens if you have to backtrack, and operate on an old axes that is not +referenced by ``plt.gca()``? One simple way is to call ``subplot`` again with +the same arguments. However, that quickly becomes inelegant. You can also +inspect the Figure object and get its list of Axes objects, however, that can be +misleading (colorbars are Axes too!). The best solution is probably to save a +handle to every Axes you create, but if you do that, why not simply create the +all the Axes objects at the start? + +The first approach is to call ``plt.subplot`` again: + +.. plot:: + :include-source: + :align: center + + import matplotlib.pyplot as plt + + plt.subplot(1, 2, 1) + plt.plot([1, 2, 3], [0, 0.5, 0.2]) + + plt.subplot(1, 2, 2) + plt.plot([3, 2, 1], [0, 0.5, 0.2]) + + plt.suptitle('Implicit Interface: re-call subplot') + + for i in range(1, 3): + plt.subplot(1, 2, i) + plt.xlabel('Boo') + +The second is to save a handle: + +.. plot:: + :include-source: + :align: center + + import matplotlib.pyplot as plt + + axs = [] + ax = plt.subplot(1, 2, 1) + axs += [ax] + plt.plot([1, 2, 3], [0, 0.5, 0.2]) + + ax = plt.subplot(1, 2, 2) + axs += [ax] + plt.plot([3, 2, 1], [0, 0.5, 0.2]) + + plt.suptitle('Implicit Interface: save handles') + + for i in range(2): + plt.sca(axs[i]) + plt.xlabel('Boo') + +However, the recommended way would be to be explicit from the outset: + +.. plot:: + :include-source: + :align: center + + import matplotlib.pyplot as plt + + fig, axs = plt.subplots(1, 2) + axs[0].plot([1, 2, 3], [0, 0.5, 0.2]) + axs[1].plot([3, 2, 1], [0, 0.5, 0.2]) + fig.suptitle('Explicit Interface') + for i in range(2): + axs[i].set_xlabel('Boo') + + +Third-party library "Data-object" interfaces +-------------------------------------------- + +Some third party libraries have chosen to implement plotting for their data +objects, e.g. ``data.plot()``, is seen in `pandas`, xarray_, and other +third-party libraries. For illustrative purposes, a downstream library may +implement a simple data container that has ``x`` and ``y`` data stored together, +and then implements a ``plot`` method: + +.. plot:: + :include-source: + :align: center + + import matplotlib.pyplot as plt + + # supplied by downstream library: + class DataContainer: + + def __init__(self, x, y): + """ + Proper docstring here! + """ + self._x = x + self._y = y + + def plot(self, ax=None, **kwargs): + if ax is None: + ax = plt.gca() + ax.plot(self._x, self._y, **kwargs) + ax.set_title('Plotted from DataClass!') + return ax + + + # what the user usually calls: + data = DataContainer([0, 1, 2, 3], [0, 0.2, 0.5, 0.3]) + data.plot() + +So the library can hide all the nitty-gritty from the user, and can make a +visualization appropriate to the data type, often with good labels, choices of +colormaps, and other convenient features. + +In the above, however, we may not have liked the title the library provided. +Thankfully, they pass us back the Axes from the ``plot()`` method, and +understanding the explicit Axes interface, we could call: +``ax.set_title('My preferred title')`` to customize the title. + +Many libraries also allow their ``plot`` methods to accept an optional *ax* +argument. This allows us to place the visualization in an Axes that we have +placed and perhaps customized. + +Summary +------- + +Overall, it is useful to understand the explicit "Axes" interface since it is +the most flexible and underlies the other interfaces. A user can usually +figure out how to drop down to the explicit interface and operate on the +underlying objects. While the explicit interface can be a bit more verbose +to setup, complicated plots will often end up simpler than trying to use +the implicit "pyplot" interface. + +.. note:: + + It is sometimes confusing to people that we import ``pyplot`` for both + interfaces. Currently, the ``pyplot`` module implements the "pyplot" + interface, but it also provides top-level Figure and Axes creation + methods, and ultimately spins up the graphical user interface, if one + is being used. So ``pyplot`` is still needed regardless of the + interface chosen. + +Similarly, the declarative interfaces provided by partner libraries use the +objects accessible by the "Axes" interface, and often accept these as arguments +or pass them back from methods. It is usually essential to use the explicit +"Axes" interface to perform any customization of the default visualization, or +to unpack the data into NumPy arrays and pass directly to Matplotlib. + +Appendix: "Axes" interface with data structures +----------------------------------------------- + +Most `~.axes.Axes` methods allow yet another API addressing by passing a +*data* object to the method and specifying the arguments as strings: + +.. plot:: + :include-source: + :align: center + + import matplotlib.pyplot as plt + + data = {'xdat': [0, 1, 2, 3], 'ydat': [0, 0.2, 0.4, 0.1]} + fig, ax = plt.subplots(figsize=(2, 2)) + ax.plot('xdat', 'ydat', data=data) + + +Appendix: "pylab" interface +--------------------------- + +There is one further interface that is highly discouraged, and that is to +basically do ``from matplotlib.pyplot import *``. This allows users to simply +call ``plot(x, y)``. While convenient, this can lead to obvious problems if the +user unwittingly names a variable the same name as a pyplot method. diff --git a/doc/users/explain/backends.rst b/doc/users/explain/backends.rst new file mode 100644 index 000000000000..88c3a927fd57 --- /dev/null +++ b/doc/users/explain/backends.rst @@ -0,0 +1,254 @@ +.. _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`_ output. +PS ps, eps vector_ graphics -- PostScript_ output. +SVG svg vector_ graphics -- `Scalable Vector Graphics`_ output. +PGF pgf, pdf vector_ graphics -- using the pgf_ package. +Cairo png, ps, raster_ or vector_ graphics -- using the Cairo_ library + pdf, svg (requires pycairo_ or cairocffi_). +======== ========= ======================================================= + +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_). This backend can be activated in IPython with + ``%matplotlib gtk3``. +GTK4Agg Agg rendering to a GTK_ 4.x canvas (requires PyGObject_ and + pycairo_). 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_). +GTK4Cairo Cairo rendering to a GTK_ 4.x canvas (requires PyGObject_ and + pycairo_). +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: https://www.matplotlib.org/ipympl + +ipympl +^^^^^^ + +The Jupyter widget ecosystem is moving too fast to support directly in +Matplotlib. To install ipympl: + +.. code-block:: bash + + pip install ipympl + +or + +.. code-block:: bash + + conda install ipympl -c conda-forge + +See `installing ipympl `__ for more details. + +.. _QT_API-usage: + +How do I select the Qt implementation? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The QtAgg and QtCairo backends support both Qt 5 and 6, as well as both Python +bindings (`PyQt`_ or `Qt for Python`_, a.k.a. PySide). If any binding has +already been loaded, then it will be used for the Qt backend. Otherwise, the +first available binding is used, in the order: PyQt6, PySide6, PyQt5, PySide2. + +The :envvar:`QT_API` environment variable can be set to override the search +when nothing has already been loaded. It may be set to (case-insensitively) +PyQt6, PySide6, PyQt5, or PySide2 to pick the version and binding to use. If +the chosen implementation is unavailable, the Qt backend will fail to load +without attempting any other Qt implementations. See :ref:`QT_bindings` for +more details. + +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')``. diff --git a/doc/users/event_handling.rst b/doc/users/explain/event_handling.rst similarity index 83% rename from doc/users/event_handling.rst rename to doc/users/explain/event_handling.rst index 3a368d3b5312..909e9f4b9ff8 100644 --- a/doc/users/event_handling.rst +++ b/doc/users/explain/event_handling.rst @@ -1,3 +1,5 @@ +.. redirect-from:: /users/event_handling + .. _event-handling-tutorial: ************************** @@ -80,29 +82,28 @@ Event name Class Description you may encounter inconsistencies between the different user interface toolkits that Matplotlib works with. This is due to inconsistencies/limitations of the user interface toolkit. The following table shows some basic examples of - what you may expect to receive as key(s) from the different user interface toolkits, - where a comma separates different keys: - - ============== ============================= ============================== ============================= ============================== ============================== - Key(s) Pressed WxPython Qt WebAgg Gtk Tkinter - ============== ============================= ============================== ============================= ============================== ============================== - Shift+2 shift, shift+2 shift, " shift, " shift, " shift, " - Shift+F1 shift, shift+f1 shift, shift+f1 shift, shift+f1 shift, shift+f1 shift, shift+f1 - Shift shift shift shift shift shift - Control control control control control control - Alt alt alt alt alt alt - AltGr Nothing Nothing alt iso_level3_shift iso_level3_shift - CapsLock caps_lock caps_lock caps_lock caps_lock caps_lock - A a a A A A - a a a a a a - Shift+a shift, A shift, A shift, A shift, A shift, A - Shift+A shift, A shift, A shift, a shift, a shift, a - Ctrl+Shift+Alt control, ctrl+shift, ctrl+alt control, ctrl+shift, ctrl+meta control, ctrl+shit, ctrl+meta control, ctrl+shift, ctrl+meta control, ctrl+shift, ctrl+meta - Ctrl+Shift+a control, ctrl+shift, ctrl+A control, ctrl+shift, ctrl+A control, ctrl+shit, ctrl+A control, ctrl+shift, ctrl+A control, ctrl+shift, ctrl+a - Ctrl+Shift+A control, ctrl+shift, ctrl+A control, ctrl+shift, ctrl+A control, ctrl+shit, ctrl+a control, ctrl+shift, ctrl+a control, ctrl+shift, ctrl+a - F1 f1 f1 f1 f1 f1 - Ctrl+F1 control, ctrl+f1 control, ctrl+f1 control, ctrl+f1 control, ctrl+f1 control, ctrl+f1 - ============== ============================= ============================== ============================= ============================== ============================== + what you may expect to receive as key(s) (using a QWERTY keyboard layout) + from the different user interface toolkits, where a comma separates different keys: + + ================ ============================= ============================== ============================== ============================== ============================== =================================== + Key(s) Pressed WxPython Qt WebAgg Gtk Tkinter macosx + ================ ============================= ============================== ============================== ============================== ============================== =================================== + Shift+2 shift, shift+2 shift, @ shift, @ shift, @ shift, @ shift, @ + Shift+F1 shift, shift+f1 shift, shift+f1 shift, shift+f1 shift, shift+f1 shift, shift+f1 shift, shift+f1 + Shift shift shift shift shift shift shift + Control control control control control control control + Alt alt alt alt alt alt alt + AltGr Nothing Nothing alt iso_level3_shift iso_level3_shift + CapsLock caps_lock caps_lock caps_lock caps_lock caps_lock caps_lock + CapsLock+a caps_lock, a caps_lock, a caps_lock, A caps_lock, A caps_lock, A caps_lock, a + a a a a a a a + Shift+a shift, A shift, A shift, A shift, A shift, A shift, A + CapsLock+Shift+a caps_lock, shift, A caps_lock, shift, A caps_lock, shift, a caps_lock, shift, a caps_lock, shift, a caps_lock, shift, A + Ctrl+Shift+Alt control, ctrl+shift, ctrl+alt control, ctrl+shift, ctrl+meta control, ctrl+shift, ctrl+meta control, ctrl+shift, ctrl+meta control, ctrl+shift, ctrl+meta control, ctrl+shift, ctrl+alt+shift + Ctrl+Shift+a control, ctrl+shift, ctrl+A control, ctrl+shift, ctrl+A control, ctrl+shift, ctrl+A control, ctrl+shift, ctrl+A control, ctrl+shift, ctrl+a control, ctrl+shift, ctrl+A + F1 f1 f1 f1 f1 f1 f1 + Ctrl+F1 control, ctrl+f1 control, ctrl+f1 control, ctrl+f1 control, ctrl+f1 control, ctrl+f1 control, Nothing + ================ ============================= ============================== ============================== ============================== ============================== =================================== Matplotlib attaches some keypress callbacks by default for interactivity; they are documented in the :ref:`key-event-handling` section. diff --git a/doc/users/explain/fonts.rst b/doc/users/explain/fonts.rst new file mode 100644 index 000000000000..b6a7baec2e52 --- /dev/null +++ b/doc/users/explain/fonts.rst @@ -0,0 +1,197 @@ +.. redirect-from:: /users/fonts + +Fonts in Matplotlib +=================== + +Matplotlib needs fonts to work with its text engine, some of which are shipped +alongside the installation. The default font is `DejaVu Sans +`_ which covers most European writing systems. +However, users can configure the default fonts, and provide their own custom +fonts. See :doc:`Customizing text properties ` for +details and :ref:`font-nonlatin` in particular for glyphs not supported by +DejaVu Sans. + +Matplotlib also provides an option to offload text rendering to a TeX engine +(``usetex=True``), see :doc:`Text rendering with LaTeX +`. + +Fonts in PDF and PostScript +--------------------------- + +Fonts have a long (and sometimes incompatible) history in computing, leading to +different platforms supporting different types of fonts. In practice, there +are 3 types of font specifications Matplotlib supports (in addition to 'core +fonts' in pdf which is explained later in the guide): + +.. list-table:: Type of Fonts + :header-rows: 1 + + * - Type 1 (PDF) + - Type 3 (PDF/PS) + - TrueType (PDF) + * - One of the oldest types, introduced by Adobe + - Similar to Type 1 in terms of introduction + - Newer than previous types, used commonly today, introduced by Apple + * - Restricted subset of PostScript, charstrings are in bytecode + - Full PostScript language, allows embedding arbitrary code + (in theory, even render fractals when rasterizing!) + - Include a virtual machine that can execute code! + * - These fonts support font hinting + - Do not support font hinting + - Hinting supported (virtual machine processes the "hints") + * - Non-subsetted through Matplotlib + - Subsetted via external module `ttconv `_ + - Subsetted via external module `fonttools `__ + +NOTE: Adobe will disable support for authoring with Type 1 fonts in +January 2023. `Read more here. `_ + + +Other font specifications which Matplotlib supports: + +- Type 42 fonts (PS): + + - PostScript wrapper around TrueType fonts + - 42 is the `Answer to Life, the Universe, and Everything! `_ + - Matplotlib uses an external library called `fonttools `__ + to subset these types of fonts + +- OpenType fonts: + + - OpenType is a new standard for digital type fonts, developed jointly by + Adobe and Microsoft + - Generally contain a much larger character set! + - Limited Support with Matplotlib + +Font Subsetting +~~~~~~~~~~~~~~~ + +The PDF and PostScript formats support embedding fonts in files allowing the +display program to correctly render the text, independent of what fonts are +installed on the viewer's computer and without the need to pre-rasterize the text. +This ensures that if the output is zoomed or resized the text does not become +pixelated. However, embedding full fonts in the file can lead to large output +files, particularly with fonts with many glyphs such as those that support CJK +(Chinese/Japanese/Korean). + +The solution to this problem is to subset the fonts used in the document and +only embed the glyphs actually used. This gets both vector text and small +files sizes. Computing the subset of the font required and writing the new +(reduced) font are both complex problem and thus Matplotlib relies on +`fontTools `__ and a vendored fork +of `ttconv `_. + +Currently Type 3, Type 42, and TrueType fonts are subseted. Type 1 fonts are not. + + +Core Fonts +~~~~~~~~~~ + +In addition to the ability to embed fonts, as part of the `PostScript +`_ and `PDF +specification +`_ +there are 14 Core Font that compliant viewers must ensure are available. If +you restrict your document to only these fonts you do not have to embed any +font information in the document but still get vector text. + +This is especially helpful to generate *really lightweight* documents.:: + + # trigger core fonts for PDF backend + plt.rcParams["pdf.use14corefonts"] = True + # trigger core fonts for PS backend + plt.rcParams["ps.useafm"] = True + + chars = "AFM ftw!" + fig, ax = plt.subplots() + ax.text(0.5, 0.5, chars) + + fig.savefig("AFM_PDF.pdf", format="pdf") + fig.savefig("AFM_PS.ps", format="ps) + + +Fonts in SVG +------------ + +Text can output to SVG in two ways controlled by :rc:`svg.fonttype`: + +- as a path (``'path'``) in the SVG +- as string in the SVG with font styling on the element (``'none'``) + + +When saving via ``'path'`` Matplotlib will compute the path of the glyphs used +as vector paths and write those to the output. The advantage of this is that +the SVG will look the same on all computers independent of what fonts are +installed. However the text will not be editable after the fact. +In contrast saving with ``'none'`` will result in smaller files and the +text will appear directly in the markup. However, the appearance may vary +based on the SVG viewer and what fonts are available. + +Fonts in Agg +------------ + +To output text to raster formats via Agg, Matplotlib relies on `FreeType +`_. Because the exact rendering of the glyphs +changes between FreeType versions we pin to a specific version for our image +comparison tests. + + +How Matplotlib selects fonts +---------------------------- + +Internally using a Font in Matplotlib is a three step process: + +1. a `.FontProperties` object is created (explicitly or implicitly) +2. based on the `.FontProperties` object the methods on `.FontManager` are used + to select the closest "best" font Matplotlib is aware of (except for + ``'none'`` mode of SVG). +3. the Python proxy for the font object is used by the backend code to render + the text -- the exact details depend on the backend via `.font_manager.get_font`. + +The algorithm to select the "best" font is a modified version of the algorithm +specified by the `CSS1 Specifications +`_ which is used by web browsers. +This algorithm takes into account the font family name (e.g. "Arial", "Noto +Sans CJK", "Hack", ...), the size, style, and weight. In addition to family +names that map directly to fonts there are five "generic font family names" ( +serif, monospace, fantasy, cursive, and sans-serif) that will internally be +mapped to any one of a set of fonts. + +Currently the public API for doing step 2 is `.FontManager.findfont` (and that +method on the global `.FontManager` instance is aliased at the module level as +`.font_manager.findfont`), which will only find a single font and return the absolute +path to the font on the filesystem. + +Font Fallback +------------- + +There is no font that covers the entire Unicode space thus it is possible for the +users to require a mix of glyphs that can not be satisfied from a single font. +While it has been possible to use multiple fonts within a Figure, on distinct +`.Text` instances, it was not previous possible to use multiple fonts in the +same `.Text` instance (as a web browser does). As of Matplotlib 3.6 the Agg, +SVG, PDF, and PS backends will "fallback" through multiple fonts in a single +`.Text` instance: + + +.. plot:: + :include-source: + :caption: The string "There are 几个汉字 in between!" rendered with 2 fonts. + + fig, ax = plt.subplots() + ax.text( + .5, .5, "There are 几个汉字 in between!", + family=['DejaVu Sans', 'WenQuanYi Zen Hei'], + ha='center' + ) + + +Internally this is implemented by setting The "font family" on +`.FontProperties` objects to a list of font families. A (currently) +private API extracts a list of paths to all of the fonts found and then +constructs a single `.ft2font.FT2Font` object that is aware of all of the fonts. +Each glyph of the string is rendered using the first font in the list that +contains that glyph. + +A majority of this work was done by Aitik Gupta supported by Google Summer of +Code 2021. diff --git a/doc/users/explain/index.rst b/doc/users/explain/index.rst new file mode 100644 index 000000000000..c2121667da4b --- /dev/null +++ b/doc/users/explain/index.rst @@ -0,0 +1,17 @@ +.. _users-guide-explain: + +.. redirect-from:: /users/explain + +Explanations +------------ + +.. toctree:: + :maxdepth: 2 + + api_interfaces.rst + backends.rst + interactive.rst + fonts.rst + event_handling.rst + performance.rst + interactive_guide.rst diff --git a/doc/users/interactive.rst b/doc/users/explain/interactive.rst similarity index 91% rename from doc/users/interactive.rst rename to doc/users/explain/interactive.rst index b4b51ff591ac..3488580a75a6 100644 --- a/doc/users/interactive.rst +++ b/doc/users/explain/interactive.rst @@ -1,13 +1,12 @@ +.. redirect-from:: /users/interactive + .. currentmodule:: matplotlib .. _mpl-shell: -===================== - Interactive Figures -===================== - -.. toctree:: - +=================== +Interactive figures +=================== When working with data, interactivity can be invaluable. The pan/zoom and mouse-location tools built into the Matplotlib GUI windows are often sufficient, but @@ -17,7 +16,7 @@ Matplotlib ships with :ref:`backends ` binding to several GUI toolkits (Qt, Tk, Wx, GTK, macOS, JavaScript) and third party packages provide bindings to `kivy `__ and `Jupyter Lab -`__. For the figures to be responsive to +`__. For the figures to be responsive to mouse, keyboard, and paint events, the GUI event loop needs to be integrated with an interactive prompt. We recommend using IPython (see :ref:`below `). @@ -26,15 +25,15 @@ that include interactive tools, a toolbar, a tool-tip, and :ref:`key bindings `: `.pyplot.figure` - Creates a new empty `.figure.Figure` or selects an existing figure + Creates a new empty `.Figure` or selects an existing figure `.pyplot.subplots` - Creates a new `.figure.Figure` and fills it with a grid of `.axes.Axes` + Creates a new `.Figure` and fills it with a grid of `~.axes.Axes` `.pyplot` has a notion of "The Current Figure" which can be accessed through `.pyplot.gcf` and a notion of "The Current Axes" accessed through `.pyplot.gca`. Almost all of the functions in `.pyplot` pass -through the current `.Figure` / `.axes.Axes` (or create one) as +through the current `.Figure` / `~.axes.Axes` (or create one) as appropriate. Matplotlib keeps a reference to all of the open figures @@ -45,11 +44,8 @@ collected. `.Figure`\s can be closed and deregistered from `.pyplot` individuall For more discussion of Matplotlib's event system and integrated event loops, please read: -.. toctree:: - :maxdepth: 1 - - interactive_guide.rst - event_handling.rst + - :ref:`interactive_figures_and_eventloops` + - :ref:`event-handling-tutorial` .. _ipython-pylab: @@ -63,7 +59,7 @@ it also ensures that the GUI toolkit event loop is properly integrated with the command line (see :ref:`cp_integration`). In this example, we create and modify a figure via an IPython prompt. -The figure displays in a Qt5Agg GUI window. To configure the integration +The figure displays in a QtAgg GUI window. To configure the integration and enable :ref:`interactive mode ` use the ``%matplotlib`` magic: @@ -72,7 +68,7 @@ and enable :ref:`interactive mode ` use the :: In [1]: %matplotlib - Using matplotlib backend: Qt5Agg + Using matplotlib backend: QtAgg In [2]: import matplotlib.pyplot as plt @@ -153,9 +149,11 @@ If in interactive mode: If not in interactive mode: - newly created figures and changes to figures are not displayed until - * `.pyplot.show()` is called - * `.pyplot.pause()` is called - * `.FigureCanvasBase.flush_events()` is called + + * `.pyplot.show()` is called + * `.pyplot.pause()` is called + * `.FigureCanvasBase.flush_events()` is called + - `pyplot.show()` runs the GUI event loop and does not return until all the plot windows are closed If you are in non-interactive mode (or created figures while in @@ -183,7 +181,7 @@ the GUI main loop in some other way. .. warning:: - Using `.figure.Figure.show` it is possible to display a figure on + Using `.Figure.show` it is possible to display a figure on the screen without starting the event loop and without being in interactive mode. This may work (depending on the GUI toolkit) but will likely result in a non-responsive figure. @@ -201,7 +199,7 @@ helpful keybindings are registered by default. .. _key-event-handling: -Navigation Keyboard Shortcuts +Navigation keyboard shortcuts ----------------------------- The following table holds all the default keys, which can be @@ -249,8 +247,8 @@ documentation of your GUI toolkit for details. -Jupyter Notebooks / Lab ------------------------ +Jupyter Notebooks / JupyterLab +------------------------------ .. note:: diff --git a/doc/users/interactive_guide.rst b/doc/users/explain/interactive_guide.rst similarity index 92% rename from doc/users/interactive_guide.rst rename to doc/users/explain/interactive_guide.rst index 645384c217e3..377487df1545 100644 --- a/doc/users/interactive_guide.rst +++ b/doc/users/explain/interactive_guide.rst @@ -1,11 +1,13 @@ .. _interactive_figures_and_eventloops: +.. redirect-from:: /users/interactive_guide + .. currentmodule:: matplotlib -================================================== - Interactive Figures and Asynchronous Programming -================================================== +================================================ +Interactive figures and asynchronous programming +================================================ Matplotlib supports rich interactive figures by embedding figures into a GUI window. The basic interactions of panning and zooming in an @@ -21,7 +23,7 @@ handling system `, `Interactive Tutorial `Interactive Applications using Matplotlib `__. -Event Loops +Event loops =========== Fundamentally, all user interaction (and networking) is implemented as @@ -71,7 +73,7 @@ interfaces. .. _cp_integration: -Command Prompt Integration +Command prompt integration ========================== So far, so good. We have the REPL (like the IPython terminal) that @@ -92,7 +94,7 @@ responsive we need a method to allow the loops to 'timeshare' : .. _cp_block_the_prompt: -Blocking the Prompt +Blocking the prompt ------------------- .. autosummary:: @@ -117,7 +119,7 @@ non-responsive) you will be able to use the prompt again. Re-starting the event loop will make any open figure responsive again (and will process any queued up user interaction). -To start the event loop until all open figures are closed use +To start the event loop until all open figures are closed, use `.pyplot.show` as :: pyplot.show(block=True) @@ -138,7 +140,7 @@ between polling for additional data. See :ref:`interactive_scripts` for more details. -Input Hook integration +Input hook integration ---------------------- While running the GUI event loop in a blocking mode or explicitly @@ -211,7 +213,7 @@ Blocking functions ------------------ If you only need to collect points in an Axes you can use -`.figure.Figure.ginput` or more generally the tools from +`.Figure.ginput` or more generally the tools from `.blocking_input` the tools will take care of starting and stopping the event loop for you. However if you have written some custom event handling or are using `.widgets` you will need to manually run the GUI @@ -236,7 +238,7 @@ which would poll for new data and update the figure at 1Hz. .. _spin_event_loop: -Explicitly spinning the Event Loop +Explicitly spinning the event Loop ---------------------------------- .. autosummary:: @@ -285,7 +287,7 @@ to call `~.FigureCanvasBase.draw_idle` to request that the canvas be re-drawn. This method can be thought of *draw_soon* in analogy to `asyncio.loop.call_soon`. -We can add this our example above as :: +We can add this to our example above as :: def slow_loop(N, ln): for j in range(N): @@ -306,14 +308,14 @@ resources on the visualization and less on your computation. .. _stale_artists: -Stale Artists +Stale artists ============= Artists (as of Matplotlib 1.5) have a **stale** attribute which is `True` if the internal state of the artist has changed since the last time it was rendered. By default the stale state is propagated up to the Artists parents in the draw tree, e.g., if the color of a `.Line2D` -instance is changed, the `.axes.Axes` and `.figure.Figure` that +instance is changed, the `~.axes.Axes` and `.Figure` that contain it will also be marked as "stale". Thus, ``fig.stale`` will report if any artist in the figure has been modified and is out of sync with what is displayed on the screen. This is intended to be used to @@ -330,11 +332,11 @@ which by default is set to a function that forwards the stale state to the artist's parent. If you wish to suppress a given artist from propagating set this attribute to None. -`.figure.Figure` instances do not have a containing artist and their +`.Figure` instances do not have a containing artist and their default callback is `None`. If you call `.pyplot.ion` and are not in ``IPython`` we will install a callback to invoke `~.backend_bases.FigureCanvasBase.draw_idle` whenever the -`.figure.Figure` becomes stale. In ``IPython`` we use the +`.Figure` becomes stale. In ``IPython`` we use the ``'post_execute'`` hook to invoke `~.backend_bases.FigureCanvasBase.draw_idle` on any stale figures after having executed the user's input, but before returning the prompt @@ -345,7 +347,7 @@ become stale. .. _draw_idle: -Draw Idle +Idle draw ========= .. autosummary:: @@ -393,19 +395,18 @@ CPython / readline ------------------ The Python C API provides a hook, :c:data:`PyOS_InputHook`, to register a -function to be run "The function will be called when Python's +function to be run ("The function will be called when Python's interpreter prompt is about to become idle and wait for user input -from the terminal.". This hook can be used to integrate a second +from the terminal."). This hook can be used to integrate a second event loop (the GUI event loop) with the python input prompt loop. The hook functions typically exhaust all pending events on the GUI event queue, run the main loop for a short fixed amount of time, or run the event loop until a key is pressed on stdin. - Matplotlib does not currently do any management of :c:data:`PyOS_InputHook` due to the wide range of ways that Matplotlib is used. This management is left to downstream libraries -- either user code or the shell. Interactive figures, -even with matplotlib in 'interactive mode', may not work in the vanilla python +even with Matplotlib in 'interactive mode', may not work in the vanilla python repl if an appropriate :c:data:`PyOS_InputHook` is not registered. Input hooks, and helpers to install them, are usually included with @@ -415,25 +416,25 @@ Matplotlib supports which can be installed via ``%matplotlib``. This is the recommended method of integrating Matplotlib and a prompt. -IPython / prompt toolkit +IPython / prompt_toolkit ------------------------ -With IPython >= 5.0 IPython has changed from using cpython's readline +With IPython >= 5.0 IPython has changed from using CPython's readline based prompt to a ``prompt_toolkit`` based prompt. ``prompt_toolkit`` has the same conceptual input hook, which is fed into ``prompt_toolkit`` via the :meth:`IPython.terminal.interactiveshell.TerminalInteractiveShell.inputhook` method. The source for the ``prompt_toolkit`` input hooks lives at -:mod:`IPython.terminal.pt_inputhooks` +``IPython.terminal.pt_inputhooks``. .. rubric:: Footnotes .. [#f1] A limitation of this design is that you can only wait for one - input, if there is a need to multiplex between multiple sources - then the loop would look something like :: + input, if there is a need to multiplex between multiple sources + then the loop would look something like :: - fds = [...] + fds = [...] while True: # Loop inp = select(fds).read() # Read eval(inp) # Evaluate / Print @@ -442,6 +443,6 @@ method. The source for the ``prompt_toolkit`` input hooks lives at `__ if you must. .. [#f3] These examples are aggressively dropping many of the - complexities that must be dealt with in the real world such as - keyboard interrupts, timeouts, bad input, resource - allocation and cleanup, etc. + complexities that must be dealt with in the real world such as + keyboard interrupts, timeouts, bad input, resource + allocation and cleanup, etc. diff --git a/doc/users/explain/performance.rst b/doc/users/explain/performance.rst new file mode 100644 index 000000000000..03bff404b7ab --- /dev/null +++ b/doc/users/explain/performance.rst @@ -0,0 +1,146 @@ +.. _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 subsampling +------------------ + +Markers can also be simplified, albeit less robustly than line +segments. Marker subsampling is only available to `.Line2D` objects +(through the ``markevery`` property). Wherever `.Line2D` construction +parameters are passed through, such as `.pyplot.plot` and `.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/faq/environment_variables_faq.rst b/doc/users/faq/environment_variables_faq.rst similarity index 85% rename from doc/faq/environment_variables_faq.rst rename to doc/users/faq/environment_variables_faq.rst index 76dd3696a3e6..36460ae1b0cd 100644 --- a/doc/faq/environment_variables_faq.rst +++ b/doc/users/faq/environment_variables_faq.rst @@ -1,20 +1,14 @@ .. _environment-variables: +.. redirect-from:: /faq/environment_variables_faq + ********************* -Environment Variables +Environment variables ********************* .. contents:: :backlinks: none - -.. envvar:: DISPLAY - - The server and screen on which to place windows. This is interpreted by GUI - toolkits in a backend-specific manner, but generally refers to an `X.org - display name - `_. - .. envvar:: HOME The user's home directory. On Linux, :envvar:`~ ` is shorthand for :envvar:`HOME`. @@ -35,6 +29,13 @@ Environment Variables used to find a base directory in which the :file:`matplotlib` subdirectory is created. +.. envvar:: MPLSETUPCFG + + This optional variable can be set to the full path of a :file:`mplsetup.cfg` + configuration file used to customize the Matplotlib build. By default, a + :file:`mplsetup.cfg` file in the root of the Matplotlib source tree will be + read. Supported build options are listed in :file:`mplsetup.cfg.template`. + .. envvar:: PATH The list of directories searched to find executable programs. @@ -81,7 +82,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/users/faq/howto_faq.rst b/doc/users/faq/howto_faq.rst new file mode 100644 index 000000000000..4ea220d969b0 --- /dev/null +++ b/doc/users/faq/howto_faq.rst @@ -0,0 +1,309 @@ +.. _howto-faq: + +.. redirect-from:: /faq/howto_faq + +****** +How-to +****** + +.. contents:: + :backlinks: none + + +.. _how-to-too-many-ticks: + +Why do I have so many ticks, and/or why are they out of order? +-------------------------------------------------------------- + +One common cause for unexpected tick behavior is passing a *list of strings +instead of numbers or datetime objects*. This can easily happen without notice +when reading in a comma-delimited text file. Matplotlib treats lists of strings +as *categorical* variables +(:doc:`/gallery/lines_bars_and_markers/categorical_variables`), and by default +puts one tick per category, and plots them in the order in which they are +supplied. + +.. plot:: + :include-source: + :align: center + + import matplotlib.pyplot as plt + import numpy as np + + fig, ax = plt.subplots(1, 2, constrained_layout=True, figsize=(6, 2)) + + ax[0].set_title('Ticks seem out of order / misplaced') + x = ['5', '20', '1', '9'] # strings + y = [5, 20, 1, 9] + ax[0].plot(x, y, 'd') + ax[0].tick_params(axis='x', labelcolor='red', labelsize=14) + + ax[1].set_title('Many ticks') + x = [str(xx) for xx in np.arange(100)] # strings + y = np.arange(100) + ax[1].plot(x, y) + ax[1].tick_params(axis='x', labelcolor='red', labelsize=14) + +The solution is to convert the list of strings to numbers or +datetime objects (often ``np.asarray(numeric_strings, dtype='float')`` or +``np.asarray(datetime_strings, dtype='datetime64[s]')``). + +For more information see :doc:`/gallery/ticks/ticks_too_many`. + +.. _howto-determine-artist-extent: + +Determine the extent of Artists in the Figure +--------------------------------------------- + +Sometimes we want to know the extent of an Artist. Matplotlib `.Artist` objects +have a method `.Artist.get_window_extent` that will usually return the extent of +the artist in pixels. However, some artists, in particular text, must be +rendered at least once before their extent is known. Matplotlib supplies +`.Figure.draw_without_rendering`, which should be called before calling +``get_window_extent``. + +.. _howto-figure-empty: + +Check whether a figure is empty +------------------------------- +Empty can actually mean different things. Does the figure contain any artists? +Does a figure with an empty `~.axes.Axes` still count as empty? Is the figure +empty if it was rendered pure white (there may be artists present, but they +could be outside the drawing area or transparent)? + +For the purpose here, we define empty as: "The figure does not contain any +artists except it's background patch." The exception for the background is +necessary, because by default every figure contains a `.Rectangle` as it's +background patch. This definition could be checked via:: + + def is_empty(figure): + """ + Return whether the figure contains no Artists (other than the default + background patch). + """ + contained_artists = figure.get_children() + return len(contained_artists) <= 1 + +We've decided not to include this as a figure method because this is only one +way of defining empty, and checking the above is only rarely necessary. +Usually the user or program handling the figure know if they have added +something to the figure. + +Checking whether a figure would render empty cannot be reliably checked except +by actually rendering the figure and investigating the rendered result. + +.. _howto-findobj: + +Find all objects in a figure of a certain type +---------------------------------------------- + +Every Matplotlib artist (see :doc:`/tutorials/intermediate/artists`) has a method +called :meth:`~matplotlib.artist.Artist.findobj` that can be used to +recursively search the artist for any artists it may contain that meet +some criteria (e.g., match all :class:`~matplotlib.lines.Line2D` +instances or match some arbitrary filter function). For example, the +following snippet finds every object in the figure which has a +``set_color`` property and makes the object blue:: + + def myfunc(x): + return hasattr(x, 'set_color') + + for o in fig.findobj(myfunc): + o.set_color('blue') + +You can also filter on class instances:: + + import matplotlib.text as text + for o in fig.findobj(text.Text): + o.set_fontstyle('italic') + +.. _howto-suppress_offset: + +Prevent ticklabels from having an offset +---------------------------------------- +The default formatter will use an offset to reduce +the length of the ticklabels. To turn this feature +off on a per-axis basis:: + + ax.get_xaxis().get_major_formatter().set_useOffset(False) + +set :rc:`axes.formatter.useoffset`, or use a different +formatter. See :mod:`~matplotlib.ticker` for details. + +.. _howto-transparent: + +Save transparent figures +------------------------ + +The :meth:`~matplotlib.pyplot.savefig` command has a keyword argument +*transparent* which, if 'True', will make the figure and axes +backgrounds transparent when saving, but will not affect the displayed +image on the screen. + +If you need finer grained control, e.g., you do not want full transparency +or you want to affect the screen displayed version as well, you can set +the alpha properties directly. The figure has a +:class:`~matplotlib.patches.Rectangle` instance called *patch* +and the axes has a Rectangle instance called *patch*. You can set +any property on them directly (*facecolor*, *edgecolor*, *linewidth*, +*linestyle*, *alpha*). e.g.:: + + fig = plt.figure() + fig.patch.set_alpha(0.5) + ax = fig.add_subplot(111) + ax.patch.set_alpha(0.5) + +If you need *all* the figure elements to be transparent, there is +currently no global alpha setting, but you can set the alpha channel +on individual elements, e.g.:: + + ax.plot(x, y, alpha=0.5) + ax.set_xlabel('volts', alpha=0.5) + +.. _howto-multipage: + +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, 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: + +Make room for tick labels +------------------------- + +By default, Matplotlib uses fixed percentage margins around subplots. This can +lead to labels overlapping or being cut off at the figure boundary. There are +multiple ways to fix this: + +- Manually adapt the subplot parameters using `.Figure.subplots_adjust` / + `.pyplot.subplots_adjust`. +- Use one of the automatic layout mechanisms: + + - constrained layout (:doc:`/tutorials/intermediate/constrainedlayout_guide`) + - tight layout (:doc:`/tutorials/intermediate/tight_layout_guide`) + +- Calculate good values from the size of the plot elements yourself + (:doc:`/gallery/subplots_axes_and_figures/auto_subplots_adjust`) + +.. _howto-align-label: + +Align my ylabels across multiple subplots +----------------------------------------- + +If you have multiple subplots over one another, and the y data have +different scales, you can often get ylabels that do not align +vertically across the multiple subplots, which can be unattractive. +By default, Matplotlib positions the x location of the ylabel so that +it does not overlap any of the y ticks. You can override this default +behavior by specifying the coordinates of the label. The example +below shows the default behavior in the left subplots, and the manual +setting in the right subplots. + +.. figure:: ../../gallery/text_labels_and_annotations/images/sphx_glr_align_ylabels_001.png + :target: ../../gallery/text_labels_and_annotations/align_ylabels.html + :align: center + :scale: 50 + +.. _howto-set-zorder: + +Control the draw order of plot elements +--------------------------------------- + +The draw order of plot elements, and thus which elements will be on top, is +determined by the `~.Artist.set_zorder` property. +See :doc:`/gallery/misc/zorder_demo` for a detailed description. + +.. _howto-axis-equal: + +Make the aspect ratio for plots equal +------------------------------------- + +The Axes property :meth:`~matplotlib.axes.Axes.set_aspect` controls the +aspect ratio of the axes. You can set it to be 'auto', 'equal', or +some ratio which controls the ratio:: + + ax = fig.add_subplot(111, aspect='equal') + +.. only:: html + + See :doc:`/gallery/subplots_axes_and_figures/axis_equal_demo` for a + complete example. + +.. _howto-twoscale: + +Draw multiple y-axis scales +--------------------------- + +A frequent request is to have two scales for the left and right +y-axis, which is possible using :func:`~matplotlib.pyplot.twinx` (more +than two scales are not currently supported, though it is on the wish +list). This works pretty well, though there are some quirks when you +are trying to interactively pan and zoom, because both scales do not get +the signals. + +The approach uses :func:`~matplotlib.pyplot.twinx` (and its sister +:func:`~matplotlib.pyplot.twiny`) to use *2 different axes*, +turning the axes rectangular frame off on the 2nd axes to keep it from +obscuring the first, and manually setting the tick locs and labels as +desired. You can use separate ``matplotlib.ticker`` formatters and +locators as desired because the two axes are independent. + +.. plot:: + + import numpy as np + import matplotlib.pyplot as plt + + fig = plt.figure() + ax1 = fig.add_subplot(111) + t = np.arange(0.01, 10.0, 0.01) + s1 = np.exp(t) + ax1.plot(t, s1, 'b-') + ax1.set_xlabel('time (s)') + ax1.set_ylabel('exp') + + ax2 = ax1.twinx() + s2 = np.sin(2*np.pi*t) + ax2.plot(t, s2, 'r.') + ax2.set_ylabel('sin') + plt.show() + + +.. only:: html + + See :doc:`/gallery/subplots_axes_and_figures/two_scales` for a + complete example. + +.. _howto-batch: + +Generate images without having a window appear +---------------------------------------------- + +Simply do not call `~matplotlib.pyplot.show`, and directly save the figure to +the desired format:: + + import matplotlib.pyplot as plt + plt.plot([1, 2, 3]) + plt.savefig('myfig.png') + +.. seealso:: + + :doc:`/gallery/user_interfaces/web_application_server_sgskip` for + information about running matplotlib inside of a web application. + +.. _how-to-threads: + +Work with threads +----------------- + +Matplotlib is not thread-safe: in fact, there are known race conditions +that affect certain artists. Hence, if you work with threads, it is your +responsibility to set up the proper locks to serialize access to Matplotlib +artists. + +You may be able to work on separate figures from separate threads. However, +you must in that case use a *non-interactive backend* (typically Agg), because +most GUI backends *require* being run from the main thread as well. diff --git a/doc/faq/index.rst b/doc/users/faq/index.rst similarity index 53% rename from doc/faq/index.rst rename to doc/users/faq/index.rst index bf71718e622b..636c90904aba 100644 --- a/doc/faq/index.rst +++ b/doc/users/faq/index.rst @@ -1,20 +1,21 @@ .. _faq-index: -################## -The Matplotlib FAQ -################## +.. redirect-from:: /faq/index + +########################## +How-to and troubleshooting +########################## .. only:: html :Release: |version| :Date: |today| - Frequently asked questions about matplotlib + Frequently asked questions about Matplotlib: .. toctree:: :maxdepth: 2 - installing_faq.rst howto_faq.rst troubleshooting_faq.rst environment_variables_faq.rst diff --git a/doc/faq/troubleshooting_faq.rst b/doc/users/faq/troubleshooting_faq.rst similarity index 92% rename from doc/faq/troubleshooting_faq.rst rename to doc/users/faq/troubleshooting_faq.rst index f0644003201b..db1c39319933 100644 --- a/doc/faq/troubleshooting_faq.rst +++ b/doc/users/faq/troubleshooting_faq.rst @@ -1,5 +1,7 @@ .. _troubleshooting-faq: +.. redirect-from:: /faq/troubleshooting_faq + *************** Troubleshooting *************** @@ -46,10 +48,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 +59,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:: @@ -82,18 +84,19 @@ Getting help There are a number of good resources for getting help with Matplotlib. There is a good chance your question has already been asked: -- The `mailing list archive `_. +- The `mailing list archive + `_. - `GitHub issues `_. - Stackoverflow questions tagged `matplotlib - `_. + `_. 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/users/getting_started/index.rst b/doc/users/getting_started/index.rst new file mode 100644 index 000000000000..4b90ab09b860 --- /dev/null +++ b/doc/users/getting_started/index.rst @@ -0,0 +1,55 @@ +Getting started +=============== + +Installation quick-start +------------------------ + +.. grid:: 1 1 2 2 + + .. grid-item:: + + Install using `pip `__: + + .. code-block:: bash + + pip install matplotlib + + .. grid-item:: + + 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: + +.. 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/github_stats.rst b/doc/users/github_stats.rst index d67ec55f2bde..3aa48834b0f3 100644 --- a/doc/users/github_stats.rst +++ b/doc/users/github_stats.rst @@ -1,1181 +1,158 @@ .. _github-stats: -GitHub Stats -============ +GitHub statistics for 3.6.2 (Nov 02, 2022) +========================================== -GitHub stats for 2020/07/16 - 2021/03/25 (tag: v3.3.0) +GitHub statistics for 2022/10/08 (tag: v3.6.1) - 2022/11/02 These lists are automatically generated, and may be incomplete or contain duplicates. -We closed 204 issues and merged 772 pull requests. -The full list can be seen `on GitHub `__ +We closed 21 issues and merged 86 pull requests. +The full list can be seen `on GitHub `__ -The following 177 authors contributed 3852 commits. +The following 22 authors contributed 27 commits. -* A N U S H -* Adam Brown -* Aditya Malhotra -* aflah02 -* Aitik Gupta -* Alejandro García -* Alex Henrie -* Alexander Schlüter -* Alexis de Almeida Coutinho -* Andreas C Mueller -* andrzejnovak * Antony Lee -* Arthur Milchior -* bakes -* BAKEZQ -* BaoGiang HoangVu -* Ben Root -* BH4 -* Bradley Dice -* Braxton Lamey -* Brian McFee -* Bruno Beltran -* Bryan Kok -* Byron Boulton -* Carsten Schelp -* ceelo777 -* Charles -* CharlesHe16 -* Christian Baumann -* Contextualist -* DangoMelon -* Daniel -* Daniel Ingram -* David Meyer -* David Stansby -* David Young -* deep-jkl -* Diego Leal -* Dr. Thomas A Caswell -* Dylan Cutler -* Eben Pendleton -* EBenkler -* ebenp -* ecotner +* Carsten Schnober +* dependabot[bot] * Elliott Sales de Andrade -* Emily FY -* Eric Firing -* Eric Larson -* Eric Prestat -* Erik Benkler -* Evan Berkowitz -* Ewan Sutherland -* Federico Ariza -* Forrest -* Frank Sauerburger -* FrankTheCodeMonkey -* Greg Lucas * hannah -* Harry Knight -* Harsh Sharma -* Hassan Kibirige -* Hugo van Kemenade -* Iain-S -* Ian Hunt-Isaak -* Ian Thomas -* ianhi -* Ilya V. Schurov -* ImportanceOfBeingErnest -* Isuru Fernando -* ItsRLuo -* J. Scott Berg +* j1642 +* Jaco Verster +* jacoverster * Jae-Joon Lee -* Jakub Klus -* Janakarajan Natarajan -* Jann Paul Mattern -* jbhopkins -* jeetvora331 -* Jerome F. Villegas -* Jerome Villegas -* jfbu -* Jirka Hladky +* Jeffrey Aaron Paul +* jeffreypaul15 * Jody Klymak -* Johan von Forstner -* johan12345 -* john imperial -* John Losito -* John Peloquin -* johnthagen -* Jouni K. Seppänen -* Kate Perkins -* kate-perkins -* katrielester -* kolibril13 -* kwgchi -* Lee Johnston -* Leo Singer -* linchiwei123 -* Lucy Liu -* luz paz -* luzpaz -* Léonard Gérard -* majorwitty -* mansoor96g -* Maria Ilie -* Maria-Alexandra Ilie -* Marianne Corvellec -* Mark Harfouche -* Martin Spacek -* Mary Chris Go -* Matthew Petroff -* Matthias Bussonnier -* Matthias Geier -* Max Chen -* McToel -* Michael Grupp -* Michaël Defferrard -* Mihai Anton -* Mohammad Aflah Khan -* Neilzon Viloria -* neok-m4700 -* Nora Moseman -* Pamela Wu -* pankajchetry1168 -* Petar Mlinarić -* Peter Williams -* Phil Nagel -* philip-sparks -* Philipp Arras -* Philipp Nagel -* Pratyush Raj -* Péter Leéh -* rajpratyush -* Randall Ung -* reshamas -* Rezangyal -* Richard Sheridan -* richardsheridan -* Rob McDonald -* Rohit Rawat -* Ruben Verweij +* Kostya Farber +* Kyle Sunden +* Martok +* Muhammad Abdur Rakib +* Oscar Gustafsson +* Pavel Grunt * Ruth Comer -* Ryan May -* Sam Tygier -* shawnchen -* shawnchen1996 -* ShawnChen1996 -* Sidharth Bansal -* Srihitha Maryada -* Stephen Sinclair -* Struan Murray -* Theodor Athanasiadis * Thomas A Caswell -* Thorvald Johannessen -* Tim Gates +* Tiger Nie * Tim Hoffmann -* Tobias Hangleiter -* tohc1 -* Tom Charrett -* Tom Neep -* Tomas Fiers -* ulijh -* Ulrich J. Herter -* Utkarshp1 -* Uwe F. Mayer -* Valentin Valls -* Vincent Cuenca -* Vineyard -* Vlas Sokolov -* Xianxiang Li -* xlilos -* Ye Chang -* Yichao Yu -* yozhikoff -* Yun Liu -* z0rgy -* zitorelova GitHub issues and pull requests: -Pull Requests (772): - -* :ghpull:`19775`: Fix deprecation for imread on URLs. -* :ghpull:`19772`: Backport PR #19535 on branch v3.4.x (Fix example's BasicUnit array conversion.) -* :ghpull:`19771`: Backport PR #19757 on branch v3.4.x (Fixed python -mpip typo) -* :ghpull:`19770`: Backport PR #19739 on branch v3.4.x (Changed 'python -mpip' to 'python -m pip' for consistency) -* :ghpull:`19535`: Fix example's BasicUnit array conversion. -* :ghpull:`19767`: Backport PR #19766 on branch v3.4.x (Set colormap modification removal to 3.6.) -* :ghpull:`19766`: Set colormap modification removal to 3.6. -* :ghpull:`19764`: Backport PR #19762 on branch v3.4.x (FIX: do not report that webagg supports blitting) -* :ghpull:`19762`: FIX: do not report that webagg supports blitting -* :ghpull:`19689`: Prepare API docs for v3.4.0 -* :ghpull:`19761`: Backport PR #19746 on branch v3.4.x (Fix resizing in nbAgg.) -* :ghpull:`19746`: Fix resizing in nbAgg. -* :ghpull:`19757`: Fixed python -mpip typo -* :ghpull:`19739`: Changed 'python -mpip' to 'python -m pip' for consistency -* :ghpull:`19713`: DOC: Prepare What's new page for 3.4.0. -* :ghpull:`19742`: Backport PR #19741 on branch v3.4.x (Only override pickradius when picker is not a bool.) -* :ghpull:`19741`: Only override pickradius when picker is not a bool. -* :ghpull:`19726`: Backport PR #19505 on branch v3.4.x (Move some advanced documentation away from Installation Guide) -* :ghpull:`19505`: Move some advanced documentation away from Installation Guide -* :ghpull:`19712`: Backport PR #19707 on branch v3.4.x (DOC: fix dx in Arrow guide) -* :ghpull:`19711`: Backport PR #19709 on branch v3.4.x (Fix arrow_guide.py typo) -* :ghpull:`19709`: Fix arrow_guide.py typo -* :ghpull:`19707`: DOC: fix dx in Arrow guide -* :ghpull:`19699`: Backport PR #19695 on branch v3.4.x (DOC: Increase size of headings) -* :ghpull:`19695`: DOC: Increase size of headings -* :ghpull:`19697`: Backport PR #19690 on branch v3.4.x (Only warn about existing redirects if content differs.) -* :ghpull:`19690`: Only warn about existing redirects if content differs. -* :ghpull:`19696`: Backport PR #19665 on branch v3.4.x (Changed FormatStrFormatter documentation to include how to get unicode minus) -* :ghpull:`19680`: Backport PR #19402 on branch v3.4.x (Build aarch64 wheels) -* :ghpull:`19678`: Backport PR #19671 on branch v3.4.x (Fix crash in early window raise in gtk3.) -* :ghpull:`19671`: Fix crash in early window raise in gtk3. -* :ghpull:`19665`: Changed FormatStrFormatter documentation to include how to get unicode minus -* :ghpull:`19402`: Build aarch64 wheels -* :ghpull:`19669`: Backport PR #19661 on branch v3.4.x (Fix CoC link) -* :ghpull:`19668`: Backport PR #19663 on branch v3.4.x (ENH: add a copy method to colormaps) -* :ghpull:`19663`: ENH: add a copy method to colormaps -* :ghpull:`19661`: Fix CoC link -* :ghpull:`19652`: Backport PR #19649 on branch v3.4.x (Use globals() instead of locals() for adding colormaps as names to cm module) -* :ghpull:`19649`: Use globals() instead of locals() for adding colormaps as names to cm module -* :ghpull:`19651`: Backport PR #19618 on branch v3.4.x (FIX: make the cache in font_manager._get_font keyed by thread id) -* :ghpull:`19650`: Backport PR #19625 on branch v3.4.x (Restore _AxesStack to track a Figure's Axes order.) -* :ghpull:`19647`: Backport PR #19645 on branch v3.4.x (Fix comment in RectangleSelector) -* :ghpull:`19618`: FIX: make the cache in font_manager._get_font keyed by thread id -* :ghpull:`19648`: Backport PR #19643 on branch v3.4.x (Don't turn check_for_pgf into public API.) -* :ghpull:`19625`: Restore _AxesStack to track a Figure's Axes order. -* :ghpull:`19643`: Don't turn check_for_pgf into public API. -* :ghpull:`19645`: Fix comment in RectangleSelector -* :ghpull:`19644`: Backport PR #19611 on branch v3.4.x (Fix double picks.) -* :ghpull:`19611`: Fix double picks. -* :ghpull:`19640`: Backport PR #19639 on branch v3.4.x (FIX: do not allow single element list of str in subplot_mosaic) -* :ghpull:`19639`: FIX: do not allow single element list of str in subplot_mosaic -* :ghpull:`19638`: Backport PR #19632 on branch v3.4.x (Fix handling of warn keyword in in Figure.show.) -* :ghpull:`19637`: Backport PR #19582 on branch v3.4.x (Add kerning to single-byte strings in PDFs) -* :ghpull:`19632`: Fix handling of warn keyword in in Figure.show. -* :ghpull:`19582`: Add kerning to single-byte strings in PDFs -* :ghpull:`19629`: Backport PR #19548 on branch v3.4.x (Increase tolerances for other arches.) -* :ghpull:`19630`: Backport PR #19596 on branch v3.4.x (Fix for issue 17769: wx interactive figure close cause crash) -* :ghpull:`19596`: Fix for issue 17769: wx interactive figure close cause crash -* :ghpull:`19548`: Increase tolerances for other arches. -* :ghpull:`19616`: Backport PR #19577 on branch v3.4.x (Fix "return"->"enter" mapping in key names.) -* :ghpull:`19617`: Backport PR #19571 on branch v3.4.x (Fail early when setting Text color to a non-colorlike.) -* :ghpull:`19615`: Backport PR #19583 on branch v3.4.x (FIX: check for a set during color conversion) -* :ghpull:`19614`: Backport PR #19597 on branch v3.4.x (Fix IPython import issue) -* :ghpull:`19613`: Backport PR #19546 on branch v3.4.x (Move unrendered README.wx to thirdpartypackages/index.rst.) -* :ghpull:`19583`: FIX: check for a set during color conversion -* :ghpull:`19597`: Fix IPython import issue -* :ghpull:`19571`: Fail early when setting Text color to a non-colorlike. -* :ghpull:`19595`: Backport PR #19589 on branch v3.4.x (Changes linestyle parameter of flierprops) -* :ghpull:`19577`: Fix "return"->"enter" mapping in key names. -* :ghpull:`19589`: Changes linestyle parameter of flierprops -* :ghpull:`19592`: Backport PR #19587 on branch v3.4.x (DOC: fix plot_date doc) -* :ghpull:`19587`: DOC: fix plot_date doc -* :ghpull:`19580`: Backport PR #19456 on branch v3.4.x (Doc implement reredirects) -* :ghpull:`19579`: Backport PR #19567 on branch v3.4.x (DOC: fix typos) -* :ghpull:`19456`: Doc implement reredirects -* :ghpull:`19567`: DOC: fix typos -* :ghpull:`19542`: Backport PR #19532 on branch v3.4.x (Add note on interaction between text wrapping and bbox_inches='tight') -* :ghpull:`19549`: Backport PR #19545 on branch v3.4.x (Replace references to pygtk by pygobject in docs.) -* :ghpull:`19546`: Move unrendered README.wx to thirdpartypackages/index.rst. -* :ghpull:`19545`: Replace references to pygtk by pygobject in docs. -* :ghpull:`19532`: Add note on interaction between text wrapping and bbox_inches='tight' -* :ghpull:`19541`: MAINT: fix typo from #19438 -* :ghpull:`19480`: Fix CallbackRegistry memory leak -* :ghpull:`19539`: In scatter, fix single rgb edgecolors handling -* :ghpull:`19438`: FIX: restore creating new axes via plt.subplot with different kwargs -* :ghpull:`18436`: Sync 3D errorbar with 2D -* :ghpull:`19472`: Fix default label visibility for top-or-left-labeled shared subplots(). -* :ghpull:`19496`: MNT: Restore auto-adding Axes3D to their parent figure on init -* :ghpull:`19533`: Clarify the animated property and reword blitting tutorial a bit -* :ghpull:`19146`: Fix #19128: webagg reports incorrect values for non-alphanumeric key events on non-qwerty keyboards -* :ghpull:`18068`: Add note on writing binary formats to stdout using savefig() -* :ghpull:`19507`: FIX: ensure we import when the user cwd does not exist -* :ghpull:`19413`: FIX: allow add option for Axes3D(fig) -* :ghpull:`19498`: Dedupe implementations of {XAxis,YAxis}._get_tick_boxes_siblings. -* :ghpull:`19502`: Prefer projection="polar" over polar=True. -* :ghpull:`18480`: Clarify color priorities in collections -* :ghpull:`19501`: Fix text position with usetex and xcolor -* :ghpull:`19460`: Implement angles for bracket arrow styles. -* :ghpull:`18408`: FIX/API: ``fig.canvas.draw`` always updates internal state -* :ghpull:`19504`: Remove remaining references to Travis CI -* :ghpull:`13358`: 3D margins consistency for mplot3d (isometric projection) -* :ghpull:`19529`: Simplify checking for tex packages. -* :ghpull:`19516`: Ignore files from annotate coverage reports -* :ghpull:`19500`: Remove workaround for numpy<1.16, and update version check. -* :ghpull:`19518`: Skip setting up a tmpdir in tests that don't need one. -* :ghpull:`19514`: DOC: add fixed-aspect colorbar examples -* :ghpull:`19511`: Clarify axes.autolimit_mode rcParam. -* :ghpull:`19503`: Fix tight_layout() on "canvasless" figures. -* :ghpull:`19410`: Set the GTK background color to white. -* :ghpull:`19497`: Add overset/underset whatsnew entry -* :ghpull:`19490`: Fix error message in plt.close(). -* :ghpull:`19461`: Move ToolManager warnings to rcParam validator -* :ghpull:`19488`: Prefer ``tr1-tr2`` to ``tr1+tr2.inverted()``. -* :ghpull:`19485`: fix regression of axline behavior with non-linear scales -* :ghpull:`19314`: Fix over/under mathtext symbols -* :ghpull:`19468`: Include tex output in pdf LatexError. -* :ghpull:`19478`: Fix trivial typo in error message. -* :ghpull:`19449`: Switch array-like (M, N) to (M, N) array-like. -* :ghpull:`19459`: Merge v3.3.4 into master -* :ghpull:`18746`: Make figure parameter optional when constructing canvases. -* :ghpull:`19455`: Add note that pyplot cannot be used for 3D. -* :ghpull:`19457`: Use absolute link for discourse -* :ghpull:`19440`: Slightly reorganize api docs. -* :ghpull:`19344`: Improvements to Docs for new contributors -* :ghpull:`19435`: Replace gtk3 deprecated APIs that have simple replacements. -* :ghpull:`19452`: Fix the docstring of draw_markers to match the actual behavior. -* :ghpull:`19448`: Remove unnecessary facecolor cache in Patch3D. -* :ghpull:`19396`: CI: remove win prerelease azure + add py39 -* :ghpull:`19426`: Support empty stairs. -* :ghpull:`19399`: Fix empty Poly3DCollections -* :ghpull:`19416`: fixes TypeError constructor returned NULL in wayland session -* :ghpull:`19439`: Move cheatsheet focus to the cheatsheets away -* :ghpull:`19425`: Add units to bar_label padding documentation. -* :ghpull:`19422`: Style fixes to triintepolate docs. -* :ghpull:`19421`: Switch to documenting generic collections in lowercase. -* :ghpull:`19411`: DOC: fix incorrect parameter names -* :ghpull:`19387`: Fix CSS table header layout -* :ghpull:`18683`: Better document font. rcParams entries. -* :ghpull:`19418`: BF: DOCS: fix slash for windows in conf.py -* :ghpull:`18544`: REORG: JoinStyle and CapStyle classes -* :ghpull:`19415`: Make TaggedValue in basic_units a sequence -* :ghpull:`19412`: DOC: correct off by one indentation. -* :ghpull:`19407`: Improve doc of default labelpad. -* :ghpull:`19373`: test for align_ylabel bug with constrained_layout -* :ghpull:`19347`: os.environ-related cleanups. -* :ghpull:`19319`: DOC: make canonical version stable -* :ghpull:`19395`: wx: Use integers in more places -* :ghpull:`17850`: MNT: set the facecolor of nofill markers -* :ghpull:`19334`: Fix qt backend on mac big sur -* :ghpull:`19394`: Don't allow pyzmq 22.0.0 on AppVeyor. -* :ghpull:`19367`: Deprecate imread() reading from URLs -* :ghpull:`19341`: MarkerStyle is considered immutable -* :ghpull:`19337`: Move sphinx extension files into mpl-data. -* :ghpull:`19389`: Temporarily switch intersphinx to latest pytest. -* :ghpull:`19390`: Doc: Minor formatting -* :ghpull:`19383`: Always include sample_data in installs. -* :ghpull:`19378`: Modify indicate_inset default label value -* :ghpull:`19357`: Shorten/make more consistent the half-filled marker definitions. -* :ghpull:`18649`: Deprecate imread() reading from URLs -* :ghpull:`19370`: Force classic ("auto") date converter in classic style. -* :ghpull:`19364`: Fix trivial doc typos. -* :ghpull:`19359`: Replace use of pyplot with OO api in some examples -* :ghpull:`19342`: FIX: fix bbox_inches=tight and constrained layout bad interaction -* :ghpull:`19350`: Describe how to test regular installations of Matplotlib -* :ghpull:`19332`: Prefer concatenate to h/vstack in simple cases. -* :ghpull:`19340`: Remove the deprecated rcParams["datapath"]. -* :ghpull:`19326`: Whitespace in Choosing Colormaps tutorial plots -* :ghpull:`16417`: Deprecate rcParams["datapath"] in favor of mpl.get_data_path(). -* :ghpull:`19336`: Revert "Deprecate setting Line2D's pickradius via set_picker." -* :ghpull:`19153`: MNT: Remove deprecated axes kwargs collision detection (version 2) -* :ghpull:`19330`: Remove register storage class from Agg files. -* :ghpull:`19324`: Improve FT2Font docstrings. -* :ghpull:`19328`: Explain annotation behavior when used in conjunction with arrows -* :ghpull:`19329`: Fix building against system qhull -* :ghpull:`19331`: Skip an ImageMagick test if ffmpeg is unavailable. -* :ghpull:`19333`: Fix PGF with special character paths. -* :ghpull:`19322`: Improve docs of _path C-extension. -* :ghpull:`19317`: Pin to oldest supported PyQt on minver CI instance. -* :ghpull:`19315`: Update the markers part of matplotlib.pyplot.plot document (fix issue #19274) -* :ghpull:`18978`: API: Remove deprecated axes kwargs collision detection -* :ghpull:`19306`: Fix some packaging issues -* :ghpull:`19291`: Cleanup code for format processing -* :ghpull:`19316`: Simplify X11 checking for Qt. -* :ghpull:`19287`: Speedup LinearSegmentedColormap.from_list. -* :ghpull:`19293`: Fix some docstring interpolations -* :ghpull:`19313`: Add missing possible return value to docs of get_verticalalignment() -* :ghpull:`18916`: Add overset and underset support for mathtext -* :ghpull:`18126`: FIX: Allow deepcopy on norms and scales -* :ghpull:`19281`: Make all transforms copiable (and thus scales, too). -* :ghpull:`19294`: Deprecate project argument to Line3DCollection.draw. -* :ghpull:`19307`: DOC: remove stray assignment in "multiple legends" example -* :ghpull:`19303`: Extended the convolution filter for correct dilation -* :ghpull:`19261`: Add machinery for png-only, single-font mathtext tests. -* :ghpull:`16571`: Update Qhull to 2019.1 reentrant version -* :ghpull:`16720`: Download qhull at build-or-sdist time. -* :ghpull:`18653`: ENH: Add func norm -* :ghpull:`19272`: Strip irrelevant information from testing docs -* :ghpull:`19298`: Fix misplaced colon in bug report template. -* :ghpull:`19297`: Clarify return format of Line2D.get_data. -* :ghpull:`19277`: Warn on redundant definition of plot properties -* :ghpull:`19278`: Cleanup and document _plot_args() -* :ghpull:`19282`: Remove the unused TransformNode._gid. -* :ghpull:`19264`: Expand on slider_demo example -* :ghpull:`19244`: Move cbook._check_isinstance() to _api.check_isinstance() -* :ghpull:`19273`: Use proper pytest functionality for warnings and exceptions -* :ghpull:`19262`: more robust check for enter key in TextBox -* :ghpull:`19249`: Clarify Doc for Secondary axis, ad-hoc example -* :ghpull:`19248`: Make return value of _get_patch_verts always an array. -* :ghpull:`19247`: Fix markup for mplot3d example. -* :ghpull:`19216`: Ignore non-draw codes when calculating path extent -* :ghpull:`19215`: Collect information for setting up a development environment -* :ghpull:`19210`: Fix creation of AGG images bigger than 1024**3 pixels -* :ghpull:`18933`: Set clip path for PostScript texts. -* :ghpull:`19162`: Deprecate cbook.warn_deprecated and move internal calls to _api.warn_deprecated -* :ghpull:`16391`: Re-write sym-log-norm -* :ghpull:`19240`: FIX: process lists for inverse norms -* :ghpull:`18737`: Fix data cursor for images with additional transform -* :ghpull:`18642`: Propagate minpos from Collections to Axes.datalim -* :ghpull:`19242`: Update first occurrence of QT to show both 4 and 5 -* :ghpull:`19231`: Add reference section to all statistics examples -* :ghpull:`19217`: Request an autoscale at the end of ax.pie() -* :ghpull:`19176`: Deprecate additional positional args to plot_{surface,wireframe}. -* :ghpull:`19063`: Give plot_directive output a ``max-width: 100%`` -* :ghpull:`19187`: Support callable for formatting of Sankey labels -* :ghpull:`19220`: Remove one TOC level from the release guide -* :ghpull:`19212`: MNT: try to put more whitespace in welcome message -* :ghpull:`19155`: Consolidated the Install from Source docs -* :ghpull:`19208`: added version ask/hint to issue templates, grammar on pr bot -* :ghpull:`19185`: Document Triangulation.triangles -* :ghpull:`19181`: Remove unused imports -* :ghpull:`19207`: Fix Grouper example code -* :ghpull:`19204`: Clarify Date Format Example -* :ghpull:`19200`: Fix incorrect statement regarding test images cache size. -* :ghpull:`19198`: Fix link in contrbuting docs -* :ghpull:`19196`: Fix PR welcome action -* :ghpull:`19188`: Cleanup comparision between X11/CSS4 and xkcd colors -* :ghpull:`19194`: Fix trivial quiver doc typo. -* :ghpull:`19180`: Fix Artist.remove_callback() -* :ghpull:`19192`: Fixed part of Issue - #19100, changed documentation for axisartist -* :ghpull:`19179`: Check that no new figures are created in image comparison tests -* :ghpull:`19184`: Minor doc cleanup -* :ghpull:`19093`: DOCS: Specifying Colors tutorial format & arrange -* :ghpull:`17107`: Add Spines class as a container for all Axes spines -* :ghpull:`18829`: Create a RangeSlider widget -* :ghpull:`18873`: Getting Started GSoD -* :ghpull:`19175`: Fix axes direction for a floating axisartist -* :ghpull:`19130`: DOC: remove reference to 2.2.x branches from list of active branches -* :ghpull:`15212`: Dedupe window-title setting by moving it to FigureManagerBase. -* :ghpull:`19172`: Fix 3D surface example bug for non-square grid -* :ghpull:`19173`: Ensure backend tests are skipped if unavailable -* :ghpull:`19170`: Clarify meaning of facecolors for LineCollection -* :ghpull:`18310`: Add 3D stem plot -* :ghpull:`18127`: Implement lazy autoscaling in mplot3d. -* :ghpull:`16178`: Add multiple label support for Axes.plot() -* :ghpull:`19151`: Deprecate @cbook.deprecated and move internal calls to @_api.deprecated -* :ghpull:`19088`: Ignore CLOSEPOLY vertices when computing dataLim from patches -* :ghpull:`19166`: CI: add github action to post to first-time PRs openers -* :ghpull:`19124`: GOV/DOC: add section to docs on triaging and triage team -* :ghpull:`15602`: Add an auto-labeling helper function for bar charts -* :ghpull:`19164`: docs: fix simple typo, backslahes -> backslashes -* :ghpull:`19161`: Simplify test_backend_pdf::test_multipage_properfinalize. -* :ghpull:`19141`: FIX: suppress offset text in ConciseDateFormatter when largest scale is in years -* :ghpull:`19150`: Move from @cbook._classproperty to @_api.classproperty -* :ghpull:`19144`: Move from cbook._warn_external() to _api.warn_external() -* :ghpull:`19119`: Don't lose unit change handlers when pickling/unpickling. -* :ghpull:`19145`: Move from cbook._deprecate_*() to _api.deprecate_*() -* :ghpull:`19123`: Use Qt events to refresh pixel ratio. -* :ghpull:`19056`: Support raw/rgba frame format in FFMpegFileWriter -* :ghpull:`19140`: Fix the docstring of suptitle/subxlabel/supylabel. -* :ghpull:`19132`: Normalize docstring interpolation label for kwdoc() property lists -* :ghpull:`19134`: Switch internal API function calls from cbook to _api -* :ghpull:`19138`: Added non-code contributions to incubator docs -* :ghpull:`19125`: DOC: contributor incubator -* :ghpull:`18948`: DOC: Fix latexpdf build -* :ghpull:`18753`: Remove several more deprecations -* :ghpull:`19083`: Fix headless tests on Wayland. -* :ghpull:`19127`: Cleanups to webagg & friends. -* :ghpull:`19122`: FIX/DOC - make Text doscstring interp more easily searchable -* :ghpull:`19106`: Support setting rcParams["image.cmap"] to Colormap instances. -* :ghpull:`19085`: FIX: update a transfrom from transFigure to transSubfigure -* :ghpull:`19117`: Rename a confusing variable. -* :ghpull:`18647`: Axes.axline: implement support transform argument (for points but not slope) -* :ghpull:`16220`: Fix interaction with unpickled 3d plots. -* :ghpull:`19059`: Support blitting in webagg backend -* :ghpull:`19107`: Update pyplot.py -* :ghpull:`19044`: Cleanup Animation frame_formats. -* :ghpull:`19087`: FIX/TST: recursively remove ticks -* :ghpull:`19094`: Suppress -Wunused-function about _import_array when compiling tkagg.cpp. -* :ghpull:`19092`: Fix use transform mplot3d -* :ghpull:`19097`: DOC: add FuncScale to set_x/yscale -* :ghpull:`19089`: ENH: allow passing a scale instance to set_scale -* :ghpull:`19086`: FIX: add a default scale to Normalize -* :ghpull:`19073`: Mention in a few more places that artists default to not-pickable. -* :ghpull:`19079`: Remove incorrect statement about ``hist(..., log=True)``. -* :ghpull:`19076`: Small improvements to aitoff projection. -* :ghpull:`19071`: DOC: Add 'blackman' to list of imshow interpolations -* :ghpull:`17524`: ENH: add supxlabel and supylabel -* :ghpull:`18840`: Add tutorial about autoscaling -* :ghpull:`19042`: Simplify GridHelper invalidation. -* :ghpull:`19048`: Remove _draw_{ticks2,label2}; skip extents computation in _update_ticks. -* :ghpull:`18983`: Pass norm argument to spy -* :ghpull:`18802`: Add code of conduct -* :ghpull:`19060`: Fix broken link in Readme -* :ghpull:`18569`: More generic value snapping for Slider widgets -* :ghpull:`19055`: Fix kwargs handling in AnnotationBbox -* :ghpull:`19041`: Reword docs for exception_handler in CallbackRegistry. -* :ghpull:`19046`: Prepare inlining MovieWriter.cleanup() into MovieWriter.finish(). -* :ghpull:`19050`: Better validate tick direction. -* :ghpull:`19038`: Fix markup in interactive figures doc. -* :ghpull:`19035`: grid_helper_curvelinear cleanups. -* :ghpull:`19022`: Update event handling docs. -* :ghpull:`19025`: Remove individual doc entries for some methods Axes inherits from Artist -* :ghpull:`19018`: Inline and optimize ContourLabeler.get_label_coords. -* :ghpull:`19019`: Deprecate never used ``resize_callback`` param to FigureCanvasTk. -* :ghpull:`19023`: Cleanup comments/docs in backend_macosx, backend_pdf. -* :ghpull:`19020`: Replace mathtext assertions by unpacking. -* :ghpull:`19024`: Dedupe docs of GridSpec.subplots. -* :ghpull:`19013`: Improve docs of _get_packed_offsets, _get_aligned_offsets. -* :ghpull:`19009`: Compactify the implementation of ContourLabeler.add_label_near. -* :ghpull:`19008`: Deprecate event processing wrapper methods on FigureManagerBase. -* :ghpull:`19015`: Better document multilinebaseline (and other small TextArea fixes) -* :ghpull:`19012`: Common ``__init__`` for VPacker and HPacker. -* :ghpull:`19014`: Support normalize_kwargs(None) (== {}). -* :ghpull:`19010`: Inline _print_pdf_to_fh, _print_png_to_fh. -* :ghpull:`19003`: Remove reference to unicode-math in pgf preamble. -* :ghpull:`18847`: Cleanup interactive pan/zoom. -* :ghpull:`18868`: Expire _make_keyword_only deprecations from 3.2 -* :ghpull:`18903`: Move cbook._suppress_matplotlib_deprecation_warning() from cbook to _api -* :ghpull:`18997`: Micro-optimize check_isinstance. -* :ghpull:`18995`: Fix the doc of GraphicsContextBase.set_clip_rectangle. -* :ghpull:`18996`: Fix API change message from #18989 -* :ghpull:`18993`: Don't access private renderer attributes in tkagg blit. -* :ghpull:`18980`: DOC: fix typos -* :ghpull:`18989`: The Artist property rasterized cannot be None anymore -* :ghpull:`18987`: Fix punctuation in doc. -* :ghpull:`18894`: Use selectfont instead of findfont + scalefont + setfont in PostScript. -* :ghpull:`18990`: Minor cleanup of categorical example -* :ghpull:`18947`: Strictly increasing check with test coverage for streamplot grid -* :ghpull:`18981`: Cleanup Firefox SVG example. -* :ghpull:`18969`: Improve documentation on rasterization -* :ghpull:`18876`: Support fully-fractional HiDPI added in Qt 5.14. -* :ghpull:`18976`: Simplify contour_label_demo. -* :ghpull:`18975`: Fix typing error in pyplot's docs -* :ghpull:`18956`: Document rasterized parameter in pcolormesh() explicitly -* :ghpull:`18968`: Fix clabel() for backends without canvas.get_renderer() -* :ghpull:`18949`: Deprecate AxisArtist.ZORDER -* :ghpull:`18830`: Pgf plotting -* :ghpull:`18967`: Remove unnecessary calls to lower(). -* :ghpull:`18910`: Remove Artist.eventson and Container.eventson -* :ghpull:`18964`: Remove special-casing for PostScript dpi in pyplot.py. -* :ghpull:`18961`: Replace sphinx-gallery-specific references by standard :doc: refs. -* :ghpull:`18955`: added needs_ghostscript; skip test -* :ghpull:`18857`: Improve hat graph example -* :ghpull:`18943`: Small cleanup to StepPatch._update_path. -* :ghpull:`18937`: Cleanup stem docs and simplify implementation. -* :ghpull:`18895`: Introduce variable since which mpl version the minimal python version -* :ghpull:`18927`: Improve warning message for missing font family specified via alias. -* :ghpull:`18930`: Document limitations of Path.contains_point() and clarify its semantics -* :ghpull:`18892`: Fixes MIME type for svg frame_format in HTMLWriter. -* :ghpull:`18938`: Edit usetex docs. -* :ghpull:`18923`: Use lambdas to prevent gc'ing and deduplication of widget callbacks. -* :ghpull:`16171`: Contour fixes/improvements -* :ghpull:`18901`: Simplify repeat_delay and fix support for it when using iterable frames. -* :ghpull:`18911`: Added Aria-Labels to all inputs with tooltips for generated HTML animations: issue #17910 -* :ghpull:`18912`: Use CallbackRegistry for {Artist,Collection}.add_callback. -* :ghpull:`18919`: DOCS: fix contourf hatch demo legend -* :ghpull:`18905`: Make docs fail on Warning (and fix all existing warnings) -* :ghpull:`18763`: Single-line string notation for subplot_mosaic -* :ghpull:`18902`: Move ImageMagick version exclusion to _get_executable_info. -* :ghpull:`18915`: Remove hard-coded API removal version mapping. -* :ghpull:`18914`: Fix typo in error message: interable -> iterable. -* :ghpull:`15065`: step-between as drawstyle [Alternative approach to #15019] -* :ghpull:`18532`: Consistent behavior of draw_if_interactive across interactive backends. -* :ghpull:`18908`: Rework interactive backends tests. -* :ghpull:`18817`: MAINT: deprecate validCap, validJoin -* :ghpull:`18907`: Unmark wx-threading-test-failure as strict xfail. -* :ghpull:`18896`: Add note on keeping a reference to animation docstrings -* :ghpull:`18862`: Resolve mathtext.fontset at FontProperties creation time. -* :ghpull:`18877`: Remove fallback to nonexistent setDevicePixelRatioF. -* :ghpull:`18823`: Move from @cbook.deprecated to @_api.deprecated -* :ghpull:`18889`: Switch Tk to using PNG files for buttons -* :ghpull:`18888`: Update version of Matplotlib that needs Python 3.7 -* :ghpull:`18867`: Remove "Demo" from example titles (part 2) -* :ghpull:`18863`: Reword FontProperties docstring. -* :ghpull:`18866`: Fix RGBAxes docs markup. -* :ghpull:`18874`: Slightly compress down the pgf tests. -* :ghpull:`18565`: Make Tkagg blit thread safe -* :ghpull:`18858`: Remove "Demo" from example titles -* :ghpull:`15177`: Bind WX_CHAR_HOOK instead of WX_KEY_DOWN for wx key_press_event. -* :ghpull:`18821`: Simplification of animated histogram example -* :ghpull:`18844`: Fix sphinx formatting issues -* :ghpull:`18834`: Add cross-references to Artist tutorial -* :ghpull:`18827`: Update Qt version in event handling docs. -* :ghpull:`18825`: Warn in pgf backend when unknown font is requested. -* :ghpull:`18822`: Remove deprecate -* :ghpull:`18733`: Time series histogram plot example -* :ghpull:`18812`: Change LogFormatter coeff computation -* :ghpull:`18820`: Fix axes -> Axes changes in figure.py -* :ghpull:`18657`: Move cbook.deprecation to _api.deprecation -* :ghpull:`18818`: Clarify behavior of CallbackRegistry.disconnect with nonexistent cids. -* :ghpull:`18811`: DOC Use 'Axes' instead of 'axes' in figure.py -* :ghpull:`18814`: [Example] update Anscombe's Quartet -* :ghpull:`18806`: DOC Use 'Axes' in _axes.py docstrings -* :ghpull:`18799`: Remove unused wx private attribute. -* :ghpull:`18772`: BF: text not drawn shouldn't count for tightbbox -* :ghpull:`18793`: Consistently use axs to refer to a set of Axes (v2) -* :ghpull:`18792`: Cmap cleanup -* :ghpull:`18798`: Deprecate ps.useafm for mathtext -* :ghpull:`18302`: Remove 3D attributes from renderer -* :ghpull:`18795`: Make inset indicator more visible in the example -* :ghpull:`18781`: Update description of web application server example. -* :ghpull:`18791`: Fix documentation of edgecolors precedence for scatter() -* :ghpull:`14645`: Add a helper to copy a colormap and set its extreme colors. -* :ghpull:`17709`: Enh: SymNorm for normalizing symmetrical data around a center -* :ghpull:`18780`: CI: pydocstyle>=5.1.0, flake8-docstrings>=1.4.0 verified to work -* :ghpull:`18200`: Unpin pydocstyle -* :ghpull:`18767`: Turn "How to use Matplotlib in a web application server" into a sphinx-gallery example -* :ghpull:`18765`: Remove some unused tick private attributes. -* :ghpull:`18688`: Shorter property deprecation. -* :ghpull:`18748`: Allow dependabot to check GitHub actions daily -* :ghpull:`18529`: Synchronize view limits of shared axes after setting ticks -* :ghpull:`18575`: Colorbar grid position -* :ghpull:`18744`: DOCS: document log locator's ``numticks`` -* :ghpull:`18687`: Deprecate GraphicsContextPS. -* :ghpull:`18706`: Consistently use 3D, 2D, 1D for dimensionality -* :ghpull:`18702`: _make_norm_from_scale fixes. -* :ghpull:`18558`: Support usetex in date Formatters -* :ghpull:`18493`: MEP22 toolmanager set axes navigate_mode -* :ghpull:`18730`: TST: skip if known-bad version of imagemagick -* :ghpull:`18583`: Support binary comms in nbagg. -* :ghpull:`18728`: Disable mouseover info for NonUniformImage. -* :ghpull:`18710`: Deprecate cla() methods of Axis and Spines in favor of clear() -* :ghpull:`18719`: Added the trace plot of the end point -* :ghpull:`18729`: Use ax.add_image rather than ax.images.append in NonUniformImage example -* :ghpull:`18707`: Use "Return whether ..." docstring for functions returning bool -* :ghpull:`18724`: Remove extra newlines in contour(f) docs. -* :ghpull:`18696`: removed glossary -* :ghpull:`18721`: Remove the use_cmex font fallback mechanism. -* :ghpull:`18680`: wx backend API cleanups. -* :ghpull:`18709`: Use attributes Axes.x/yaxis instead of Axes.get_x/yaxis() -* :ghpull:`18712`: Shorten GraphicsContextWx.get_wxcolour. -* :ghpull:`18708`: Individualize contour and contourf docstrings -* :ghpull:`18663`: fix: keep baseline scale to baseline 0 even if set to None -* :ghpull:`18704`: Fix docstring of Axes.cla() -* :ghpull:`18675`: Merge ParasiteAxesAuxTransBase into ParasiteAxesBase. -* :ghpull:`18651`: Allow Type3 subsetting of otf fonts in pdf backend. -* :ghpull:`17396`: Improve headlessness detection for backend selection. -* :ghpull:`17737`: Deprecate BoxStyle._Base. -* :ghpull:`18655`: Sync SubplotDivider API with SubplotBase API changes. -* :ghpull:`18582`: Shorten mlab tests. -* :ghpull:`18599`: Simplify wx rubberband drawing. -* :ghpull:`18671`: DOC: fix autoscale docstring -* :ghpull:`18637`: BLD: sync build and run time numpy pinning -* :ghpull:`18693`: Also fix tk key mapping, following the same strategy as for gtk. -* :ghpull:`18691`: Cleanup sample_data. -* :ghpull:`18697`: Catch TypeError when validating rcParams types. -* :ghpull:`18537`: Create security policy -* :ghpull:`18356`: ENH: Subfigures -* :ghpull:`18694`: Document limitations on ``@deprecated`` with multiple-inheritance. -* :ghpull:`18669`: Rework checks for old macosx -* :ghpull:`17791`: More accurate handling of unicode/numpad input in gtk3 backends. -* :ghpull:`18679`: Further simplify pgf tmpdir cleanup. -* :ghpull:`18685`: Cleanup pgf examples -* :ghpull:`18682`: Small API cleanups to plot_directive. -* :ghpull:`18686`: Numpydocify setp. -* :ghpull:`18684`: Small simplification to triage_tests.py. -* :ghpull:`17832`: pdf: Support setting URLs on Text objects -* :ghpull:`18674`: Remove accidentally added swapfile. -* :ghpull:`18673`: Small cleanups to parasite axes. -* :ghpull:`18536`: axes3d panning -* :ghpull:`18667`: TST: Lock cache directory during cleanup. -* :ghpull:`18672`: Created Border for color examples -* :ghpull:`18661`: Define GridFinder.{,inv\_}transform_xy as normal methods. -* :ghpull:`18656`: Fix some missing references. -* :ghpull:`18659`: Small simplifications to BboxImage. -* :ghpull:`18511`: feat: StepPatch to take array as baseline -* :ghpull:`18646`: Support activating figures with plt.figure(figure_instance). -* :ghpull:`18370`: Move PostScript Type3 subsetting to pure python. -* :ghpull:`18645`: Simplify Colorbar.set_label, inline Colorbar._edges. -* :ghpull:`18633`: Support linestyle='none' in Patch -* :ghpull:`18527`: Fold ColorbarPatch into Colorbar, deprecate colorbar_factory. -* :ghpull:`17480`: Regenerate background when RectangleSelector active-flag is set back on. -* :ghpull:`18626`: Specify case when parameter is ignored. -* :ghpull:`18634`: Fix typo in warning message. -* :ghpull:`18603`: bugfix #18600 by using the MarkerStyle copy constructor -* :ghpull:`18628`: Remove outdate comment about canvases with no manager attribute. -* :ghpull:`18591`: Deprecate MathTextParser("bitmap") and associated APIs. -* :ghpull:`18617`: Remove special styling of sidebar heading -* :ghpull:`18616`: Improve instructions for building the docs -* :ghpull:`18623`: Provide a 'cursive' font present in Windows' default font set. -* :ghpull:`18579`: Fix stairs() tests -* :ghpull:`18618`: Correctly separate two fantasy font names. -* :ghpull:`18610`: DOCS: optional doc building dependencies -* :ghpull:`18601`: Simplify Rectangle and RegularPolygon. -* :ghpull:`18573`: add_subplot(..., axes_class=...) for more idiomatic mpl_toolkits usage. -* :ghpull:`18605`: Correctly sync state of wx toolbar buttons when triggered by keyboard. -* :ghpull:`18606`: Revert "FIX: pin pytest" -* :ghpull:`18587`: Fix docstring of zaxis_date. -* :ghpull:`18589`: Factor out pdf Type3 glyph drawing. -* :ghpull:`18586`: Text cleanups. -* :ghpull:`18594`: FIX: pin pytest -* :ghpull:`18577`: Random test cleanups -* :ghpull:`18578`: Merge all axisartist axis_direction demos together. -* :ghpull:`18588`: Use get_x/yaxis_transform more. -* :ghpull:`18585`: FIx precision in pie and donut example -* :ghpull:`18564`: Prepare for merging SubplotBase into AxesBase. -* :ghpull:`15127`: ENH/API: improvements to register_cmap -* :ghpull:`18576`: DOC: prefer colormap over color map -* :ghpull:`18340`: Colorbar grid postion -* :ghpull:`18568`: Added Reporting to code_of_conduct.md -* :ghpull:`18555`: Convert _math_style_dict into an Enum. -* :ghpull:`18567`: Replace subplot(ijk) calls by subplots(i, j) -* :ghpull:`18554`: Replace some usages of plt.subplot() by plt.subplots() in tests -* :ghpull:`18556`: Accept same types to errorevery as markevery -* :ghpull:`15932`: Use test cache for test result images too. -* :ghpull:`18557`: DOC: Add an option to disable Google Analytics. -* :ghpull:`18560`: Remove incorrect override of pcolor/contour in parasite axes. -* :ghpull:`18566`: Use fig, ax = plt.subplots() in tests (part 2) -* :ghpull:`18553`: Use fig, ax = plt.subplots() in tests -* :ghpull:`11748`: get_clip_path checks for nan -* :ghpull:`8987`: Tick formatter does not support grouping with locale -* :ghpull:`18552`: Change \*subplot(111, ...) to \*subplot(...) as 111 is the default. -* :ghpull:`18189`: FIX: Add get/set methods for 3D collections -* :ghpull:`18430`: FIX: do not reset ylabel ha when changing position -* :ghpull:`18515`: Remove deprecated backend code. -* :ghpull:`17935`: MNT: improve error messages on bad pdf metadata input -* :ghpull:`18525`: Add Text3D position getter/setter -* :ghpull:`18542`: CLEANUP: validate join/cap style centrally -* :ghpull:`18501`: TST: Add test for _repr_html_ -* :ghpull:`18528`: Deprecate TextArea minimumdescent. -* :ghpull:`18543`: Documentation improvements for stairs() -* :ghpull:`18531`: Unit handling improvements -* :ghpull:`18523`: Don't leak file paths into PostScript metadata -* :ghpull:`18526`: Templatize _image.resample to deduplicate it. -* :ghpull:`18522`: Remove mlab, toolkits, and misc deprecations -* :ghpull:`18516`: Remove deprecated font-related things. -* :ghpull:`18535`: Add a code of conduct link to github -* :ghpull:`17521`: Remove font warning when legend is added while using Tex -* :ghpull:`18517`: Include kerning when outputting pdf strings. -* :ghpull:`18521`: Inline some helpers in ColorbarBase. -* :ghpull:`18512`: Private api2 -* :ghpull:`18519`: Correctly position text with nonzero descent with afm fonts / ps output. -* :ghpull:`18513`: Remove Locator.autoscale. -* :ghpull:`18497`: Merge v3.3.x into master -* :ghpull:`18502`: Remove the deprecated matplotlib.cm.revcmap() -* :ghpull:`18506`: Inline ScalarFormatter._formatSciNotation. -* :ghpull:`18455`: Fix BoundingBox in EPS files. -* :ghpull:`18275`: feat: StepPatch -* :ghpull:`18507`: Fewer "soft" dependencies on LaTeX packages. -* :ghpull:`18378`: Deprecate public access to many mathtext internals. -* :ghpull:`18494`: Move cbook._check_in_list() to _api.check_in_list() -* :ghpull:`18423`: 2-D array RGB and RGBA values not understood in plt.plot() -* :ghpull:`18492`: Fix doc build failure due to #18440 -* :ghpull:`18435`: New environment terminal language -* :ghpull:`18456`: Reuse InsetLocator to make twinned axes follow their parents. -* :ghpull:`18440`: List existing rcParams in rcParams docstring. -* :ghpull:`18453`: FIX: allow manually placed axes in constrained_layout -* :ghpull:`18473`: Correct link to widgets examples -* :ghpull:`18466`: Remove unnecessary autoscale handling in hist(). -* :ghpull:`18465`: Don't modify bottom argument in place in stacked histograms. -* :ghpull:`18468`: Cleanup multiple_yaxis_with_spines example. -* :ghpull:`18463`: Improve formatting of defaults in docstrings. -* :ghpull:`6268`: ENH: support alpha arrays in collections -* :ghpull:`18449`: Remove the private Axes._set_position. -* :ghpull:`18460`: DOC: example gray level in 'Specifying Colors' tutorial -* :ghpull:`18426`: plot directive: caption-option -* :ghpull:`18444`: Support doubleclick in webagg/nbagg -* :ghpull:`12518`: Example showing scale-invariant angle arc -* :ghpull:`18446`: Normalize properties passed to ToolHandles. -* :ghpull:`18445`: Warn if an animation is gc'd before doing anything. -* :ghpull:`18452`: Move Axes ``__repr__`` from Subplot to AxesBase. -* :ghpull:`15374`: Replace _prod_vectorized by @-multiplication. -* :ghpull:`13643`: RecangleSelector constructor does not handle marker_props -* :ghpull:`18403`: DOC: Remove related topics entries from the sidebar -* :ghpull:`18421`: Move {get,set}_{x,y}label to _AxesBase. -* :ghpull:`18429`: DOC: fix date example -* :ghpull:`18353`: DOCS: describe shared axes behavior with units -* :ghpull:`18420`: Always strip out date in postscript's test_savefig_to_stringio. -* :ghpull:`18422`: Decrease output when running ``pytest -s``. -* :ghpull:`18418`: Cleanup menu example -* :ghpull:`18419`: Avoid demo'ing passing kwargs to gca(). -* :ghpull:`18372`: DOC: Fix various missing references and typos -* :ghpull:`18400`: Clarify argument name in constrained_layout error message -* :ghpull:`18384`: Clarification in ArtistAnimation docstring -* :ghpull:`17892`: Add earlier color validation -* :ghpull:`18367`: Support horizontalalignment in TextArea/AnchoredText. -* :ghpull:`18362`: DOC: Add some types to Returns entries. -* :ghpull:`18365`: move canvas focus after toomanager initialization -* :ghpull:`18360`: Add example for specifying figure size in different units -* :ghpull:`18341`: DOCS: add action items to PR template -* :ghpull:`18349`: Remove redundant angles in ellipse demo. -* :ghpull:`18145`: Created a parameter fontset that can be used in each Text element -* :ghpull:`18344`: More nouns/imperative forms in docs. -* :ghpull:`18308`: Synchronize units change in Axis.set_units for shared axis -* :ghpull:`17494`: Rewrite of constrained_layout.... -* :ghpull:`16646`: update colorbar.py make_axes_gridspec -* :ghpull:`18306`: Fix configure subplots -* :ghpull:`17509`: Fix ``swap_if_landscape`` call in backend_ps -* :ghpull:`18323`: Deleted "Our Favorite Recipes" section and moved the examples. -* :ghpull:`18128`: Change several deprecated symbols in _macosx.m -* :ghpull:`18251`: Merge v3.3.x into master -* :ghpull:`18329`: Change default keymap in toolmanager example. -* :ghpull:`18330`: Dedent rst list. -* :ghpull:`18286`: Fix imshow to work with subclasses of ndarray. -* :ghpull:`18320`: Make Colorbar outline into a Spine. -* :ghpull:`18316`: Safely import pyplot if a GUI framework is already running. -* :ghpull:`18321`: Capture output of CallbackRegistry exception test. -* :ghpull:`17900`: Add getters and _repr_html_ for over/under/bad values of Colormap objects. -* :ghpull:`17930`: Fix errorbar property cycling to match plot. -* :ghpull:`18290`: Remove unused import to fix flake8. -* :ghpull:`16818`: Dedupe implementations of configure_subplots(). -* :ghpull:`18284`: TkTimer interval=0 workaround -* :ghpull:`17901`: DOC: Autoreformating of backend/\*.py -* :ghpull:`17291`: Normalize gridspec ratios to lists in the setter. -* :ghpull:`18226`: Use CallbackRegistry in Widgets and some related cleanup -* :ghpull:`18203`: Force locator and formatter inheritence -* :ghpull:`18279`: boxplot: Add conf_intervals reference to notch docs. -* :ghpull:`18276`: Fix autoscaling to exclude inifinite data limits when possible. -* :ghpull:`18261`: Migrate tk backend tests into subprocesses -* :ghpull:`17961`: DOCS: Remove How-to: Contributing -* :ghpull:`18201`: Remove mpl.colors deprecations for 3.4 -* :ghpull:`18223`: Added example on how to make packed bubble charts -* :ghpull:`18264`: Fix broken links in doc build. -* :ghpull:`8031`: Add errorbars to mplot3d -* :ghpull:`18187`: Add option to create horizontally-oriented stem plots -* :ghpull:`18250`: correctly autolabel Documentation and Maintenance issues -* :ghpull:`18161`: Add more specific GitHub issue templates -* :ghpull:`18181`: Replace ttconv by plain python for pdf subsetting -* :ghpull:`17371`: add context manager functionality to ion and ioff -* :ghpull:`17789`: Tk backend improvements -* :ghpull:`15532`: Resolve 'text ignores rotational part of transformation' (#698) -* :ghpull:`17851`: Fix Axes3D.add_collection3d issues -* :ghpull:`18205`: Hat graph example -* :ghpull:`6168`: #5856: added option to create vertically-oriented stem plots -* :ghpull:`18202`: Remove mpl.testing deprecations for 3.4 -* :ghpull:`18081`: Support scale in ttf composite glyphs -* :ghpull:`18199`: Some cleanup on TickedStroke -* :ghpull:`18190`: Use ``super()`` more in backends -* :ghpull:`18193`: Allow savefig to save SVGs on FIPS enabled systems #18192 -* :ghpull:`17802`: fix FigureManagerTk close behavior if embedded in Tk App -* :ghpull:`15458`: TickedStroke, a stroke style with ticks useful for depicting constraints -* :ghpull:`18178`: DOC: clarify that display space coordinates are not stable -* :ghpull:`18172`: allow webAgg to report middle click events -* :ghpull:`17578`: Search for minus of any font size to get height of tex result -* :ghpull:`17546`: ``func`` argument in ``legend_elements`` with non-monotonically increasing functions -* :ghpull:`17684`: Deprecate passing bytes to FT2Font.set_text. -* :ghpull:`17500`: Tst improve memleak -* :ghpull:`17669`: Small changes to svg font embedding details -* :ghpull:`18095`: Error on unexpected kwargs in scale classes -* :ghpull:`18106`: Copy docstring description from Axes.legend() to Figure.legend() -* :ghpull:`18002`: Deprecate various vector-backend-specific mathtext helpers. -* :ghpull:`18006`: Fix ToolManager inconsistencies with regular toolbar -* :ghpull:`18004`: Typos and docs for mathtext fonts. -* :ghpull:`18133`: DOC: Update paths for moved API/what's new fragments -* :ghpull:`18122`: Document and test legend argument parsing -* :ghpull:`18124`: Fix FuncAnimation._draw_frame exception and testing -* :ghpull:`18125`: pdf: Convert operator list to an Enum. -* :ghpull:`18123`: Cleanup figure title example -* :ghpull:`18121`: Improve rasterization demo -* :ghpull:`18012`: Add explanatory text for rasterization demo -* :ghpull:`18103`: Support data reference for hexbin() parameter C -* :ghpull:`17826`: Add pause() and resume() methods to the base Animation class -* :ghpull:`18090`: Privatize cbook.format_approx. -* :ghpull:`18080`: Reduce numerical precision in Type 1 fonts -* :ghpull:`18044`: Super-ify parts of the code base, part 3 -* :ghpull:`18087`: Add a note on working around limit expansion of set_ticks() -* :ghpull:`18071`: Remove deprecated animation code -* :ghpull:`17822`: Check for float values for min/max values to ax{v,h}line -* :ghpull:`18069`: Remove support for multiple-color strings in to_rgba_array -* :ghpull:`18070`: Remove rcsetup deprecations -* :ghpull:`18073`: Remove disable_internet.py -* :ghpull:`18075`: typo in usetex.py example -* :ghpull:`18043`: Super-ify parts of the code base, part 2 -* :ghpull:`18062`: Bump matplotlib.patches coverage -* :ghpull:`17269`: Fix ConciseDateFormatter when plotting a range included in a second -* :ghpull:`18063`: Remove un-used trivial setters and getters -* :ghpull:`18025`: add figpager as a third party package -* :ghpull:`18046`: Discourage references in section headings. -* :ghpull:`18042`: scatter: Raise if unexpected type of ``s`` argument. -* :ghpull:`18028`: Super-ify parts of the code base, part 1 -* :ghpull:`18029`: Remove some unused imports. -* :ghpull:`18018`: Cache realpath resolution in font_manager. -* :ghpull:`18013`: Use argumentless ``super()`` more. -* :ghpull:`17988`: add test with -OO -* :ghpull:`17993`: Make inset_axes and secondary_axis picklable. -* :ghpull:`17992`: Shorten tight_bbox. -* :ghpull:`18003`: Deprecate the unneeded Fonts.destroy. -* :ghpull:`16457`: Build lognorm/symlognorm from corresponding scales. -* :ghpull:`17966`: Fix some words -* :ghpull:`17803`: Simplify projection-of-point-on-polyline in contour.py. -* :ghpull:`17699`: raise RuntimeError appropriately for animation update func -* :ghpull:`17954`: Remove another overspecified latex geometry. -* :ghpull:`17948`: Sync Cairo's usetex measurement with base class. -* :ghpull:`17788`: Tighten a bit the RendererAgg API. -* :ghpull:`12443`: Warn in colorbar() when mappable.axes != figure.gca(). -* :ghpull:`17926`: Deprecate hatch patterns with invalid values -* :ghpull:`17922`: Rewrite the barcode example -* :ghpull:`17890`: Properly use thin space after math text operator -* :ghpull:`16090`: Change pcolormesh snapping (fixes alpha colorbar/grid issues) [AGG] -* :ghpull:`17842`: Move "Request a new feature" from How-to to Contributing -* :ghpull:`17897`: Force origin='upper' in pyplot.specgram -* :ghpull:`17929`: Improve hatch demo -* :ghpull:`17927`: Remove unnecessary file save during test -* :ghpull:`14896`: Updated doc in images.py by adding direct link to 24-bit stink bug png -* :ghpull:`17909`: frame_format to support all listed by animation writers -* :ghpull:`13569`: Style cleanup to pyplot. -* :ghpull:`17924`: Remove the example "Easily creating subplots" -* :ghpull:`17869`: FIX: new date rcParams weren't being evaluated -* :ghpull:`17921`: Added density and combination hatching examples -* :ghpull:`17159`: Merge consecutive rasterizations -* :ghpull:`17895`: Use indexed color for PNG images in PDF files when possible -* :ghpull:`17894`: DOC: Numpydoc format. -* :ghpull:`17884`: Created Hatch marker styles Demo for Example Gallery -* :ghpull:`17347`: ENH: reuse oldgridspec is possible... -* :ghpull:`17915`: Document that set_ticks() increases view limits if necessary -* :ghpull:`17902`: Fix figure size in path effects guide -* :ghpull:`17899`: Add missing space in cairo error -* :ghpull:`17888`: Add _repr_png_ and _repr_html_ to Colormap objects. -* :ghpull:`17830`: Fix BoundaryNorm for multiple colors and one region -* :ghpull:`17883`: Remove Python 3.6 compatibility shims -* :ghpull:`17889`: Minor doc fixes -* :ghpull:`17879`: Link to style-file example page in style tutorial -* :ghpull:`17876`: Fix description of subplot2grid arguments -* :ghpull:`17856`: Clarify plotnonfinite parameter docs of scatter() -* :ghpull:`17843`: Add fullscreen toggle support to WxAgg backend -* :ghpull:`17022`: ENH: add rcParam for ConciseDate and interval_multiples -* :ghpull:`17799`: Deduplicate attribute docs of ContourSet and its derived classes -* :ghpull:`17847`: Remove overspecified latex geometry. -* :ghpull:`17662`: Mnt drop py36 -* :ghpull:`17845`: Fix size of donate button -* :ghpull:`17825`: Add quick-link buttons for contributing -* :ghpull:`17837`: Remove "Reporting a bug or submitting a patch" from How-to -* :ghpull:`17828`: API: treat xunits=None and yunits=None as "default" -* :ghpull:`17839`: Avoid need to lock in dvi generation, to avoid deadlocks. -* :ghpull:`17824`: Improve categorical converter error message -* :ghpull:`17834`: Keep using a single dividers LineCollection instance in colorbar. -* :ghpull:`17838`: Prefer colorbar(ScalarMappable(...)) to ColorbarBase in tutorial. -* :ghpull:`17836`: More precise axes section names in docs -* :ghpull:`17835`: Colorbar cleanups. -* :ghpull:`17727`: FIX: properly handle dates when intmult is true -* :ghpull:`15617`: Dev docs update -* :ghpull:`17819`: Fix typos in tight layout guide -* :ghpull:`17806`: Set colorbar label only in set_label. -* :ghpull:`17265`: Mnt rearrange next api again -* :ghpull:`17808`: Improve docstring of ColorbarBase.set_label() -* :ghpull:`17723`: Deprecate FigureCanvas.{get,set}_window_title. -* :ghpull:`17798`: Fix overindented bullet/enumerated lists. -* :ghpull:`17767`: Allow list of hatches to {bar, barh} -* :ghpull:`17749`: Deprecate ``FancyBboxPatch(..., boxstyle="custom", bbox_transmuter=...)`` -* :ghpull:`17783`: DOC: point to bbox static "constructor" functions in set_position -* :ghpull:`17782`: MNT: update mailmap -* :ghpull:`17776`: Changes in the image for test_load_from_url -* :ghpull:`17750`: Soft-deprecate mutation_aspect=None. -* :ghpull:`17780`: Reorganize colorbar docstrings. -* :ghpull:`17778`: Fix whatsnew confusing typo. -* :ghpull:`17748`: Don't use bezier helpers in axisartist. -* :ghpull:`17700`: Remove remnants of macosx old-style toolbar. -* :ghpull:`17753`: Support location="left"/"top" for gridspec-based colorbars. -* :ghpull:`17761`: Update hard-coded results in artist tutorial -* :ghpull:`17728`: Move Win32_{Get,Set}ForegroundWindow to c_internal_utils. -* :ghpull:`17754`: Small cleanups to contour() code. -* :ghpull:`17751`: Deprecate dpi_cor property of FancyArrowPatch. -* :ghpull:`15941`: FontManager fixes. -* :ghpull:`17661`: Issue #17659: set tick color and tick labelcolor independently from rcParams -* :ghpull:`17389`: Don't duplicate docstrings of pyplot-level cmap setters. -* :ghpull:`17555`: Set Win32 AppUserModelId to fix taskbar icons. -* :ghpull:`17726`: Clarify docs of box_aspect() -* :ghpull:`17704`: Remove "created-by-matplotlib" comment in svg output. -* :ghpull:`17697`: Add description examples/pyplots/pyplot simple.py -* :ghpull:`17694`: CI: Only skip devdocs deploy if PR is to this repo. -* :ghpull:`17691`: ci: Print out reasons for not deploying docs. -* :ghpull:`17099`: Make Spines accessable by the attributes. - -Issues (204): - -* :ghissue:`19701`: Notebook plotting regression in 3.4.0rc* -* :ghissue:`19754`: add space in python -mpip -* :ghissue:`18364`: ``Axes3d`` attaches itself to a figure, where as ``Axes`` does not -* :ghissue:`19700`: Setting pickradius regression in 3.4.0rc -* :ghissue:`19594`: code of conduct link 404s -* :ghissue:`19576`: duplicate pick events firing -* :ghissue:`19560`: segfault due to font objects when multi-threading -* :ghissue:`19598`: Axes order changed in 3.4.0rc1 -* :ghissue:`19631`: subplot mosaic 1 element list -* :ghissue:`19581`: Missing kerning for single-byte strings in PDF -* :ghissue:`17769`: interactive figure close with wxpython 4.1 causes freeze / crash (segfault?) -* :ghissue:`19427`: Fix mistake in documentation -* :ghissue:`19624`: Cannot add colorbar to figure after pickle -* :ghissue:`19544`: Regression in 3.4.0rc1 in creating ListedColormap from a set -* :ghissue:`5855`: plt.step(..., where="auto") -* :ghissue:`19474`: Memory leak with CallbackRegistry -* :ghissue:`19345`: legend is eating up huge amounts of memory -* :ghissue:`19066`: plt.scatter, error with NaN values and edge color -* :ghissue:`19432`: Unexpected change in behavior in plt.subplot -* :ghissue:`18020`: Scatter3D: facecolor or color to "none" leads to an error -* :ghissue:`18939`: Warn re: Axes3D constructor behavior change in mpl3.4 -* :ghissue:`19128`: webagg reports incorrect values for non-alphanumeric key events on non-qwerty keyboards -* :ghissue:`16558`: Request: for non-interactive backends make fig.canvas.draw() force the render -* :ghissue:`19234`: tick labels displaced vertically with text.usetex and xcolor -* :ghissue:`18407`: pgf backend no longer supports fig.draw -* :ghissue:`2298`: axes.xmargin/ymargin rcParam behaves differently than pyplot.margins() -* :ghissue:`19473`: Animations in Tkinter window advance non-uniformly -* :ghissue:`8688`: document moved examples -* :ghissue:`9553`: Display warning on out-of-date documentation websites -* :ghissue:`9556`: Examples page version is out of date -* :ghissue:`12374`: Examples in docs should be redirected to latest version number -* :ghissue:`19486`: Figure.tight_layout() raises MatplotlibDeprecationWarning -* :ghissue:`19445`: axline transform support broke axline in loglog scale -* :ghissue:`19178`: mathtext \lim is vertically misaligned -* :ghissue:`19446`: Better document and error handle third dimension in pyplot.text() positional argument -* :ghissue:`8790`: Inconsistent doc vs behavior for RendererXXX.draw_markers -* :ghissue:`18815`: Patch3D object does not return correct face color with get_facecolor -* :ghissue:`19152`: Automatically Aligned Labels outside Figure with Constrained Layout in Exported File -* :ghissue:`18934`: stairs() crashes with no values and one edge -* :ghissue:`11296`: Image in github repo does not match matplotlib.org (breaks image tutorial) -* :ghissue:`18699`: Issue with downloading stinkbug for "Image Tutorial" -* :ghissue:`19405`: TypeError constructor returned NULL in wayland session -* :ghissue:`18962`: Table CSS needs cleanup -* :ghissue:`19417`: CI failing on numpy... -* :ghissue:`17849`: Problems caused by changes to logic of scatter coloring in matplotlib 3.3.0.rc1 -* :ghissue:`18648`: Drop support for directly imread()ing urls. -* :ghissue:`19366`: Current CI doc builds fail -* :ghissue:`19372`: matplotlib.axes.Axes.indicate_inset default label value is incompatible with LaTeX -* :ghissue:`17100`: Is it a better solution to acess one of the spines by class atrribute? -* :ghissue:`17375`: Proposal: add_subfigs.... -* :ghissue:`19339`: constrained_layout + fixed-aspect axes + bbox_inches="tight" -* :ghissue:`19308`: Reduce whitespace in Choosing Colormaps tutorial plots -* :ghissue:`18832`: MNT: Remove AxesStack and deprecated behavior of reuse of existing axes with same arguments -* :ghissue:`19084`: Arrow coordinates slightly off when used with annotation text -* :ghissue:`17765`: PGF xelatex can't find fonts in special-character paths -* :ghissue:`19274`: Missing marker in documentation of plot -* :ghissue:`18241`: LaTeX overset: unknown symbol -* :ghissue:`19292`: Non interpolated placeholder value in docstring. -* :ghissue:`18119`: Can no longer deepcopy LogNorm objects on master -* :ghissue:`8665`: Noninteger Bases in mathtext sqrt -* :ghissue:`19243`: matplotlib doesn't build with qhull-2020.2 -* :ghissue:`19275`: Double specifications of plot attributes -* :ghissue:`15066`: Feature request: stem3 -* :ghissue:`19209`: Segfault when trying to create gigapixel image with agg backend -* :ghissue:`4321`: clabel ticks and axes limits with eps zoom output -* :ghissue:`16376`: ``SymLogNorm`` and ``SymLogScale`` give inconsistent results.... -* :ghissue:`19239`: _make_norm_from_scale needs to process values -* :ghissue:`16552`: Scatter autoscaling still has issues with log scaling and zero values -* :ghissue:`18417`: Documentation issue template should ask for matplotlib version -* :ghissue:`19206`: matplotlib.cbook.Grouper: Example raise exception: -* :ghissue:`19203`: Date Tick Labels example -* :ghissue:`18581`: Add a check in check_figures_equal that the test did not accidentally plot on non-fixture figures -* :ghissue:`18563`: Create a RangeSlider widget -* :ghissue:`19099`: axisartist axis_direction bug -* :ghissue:`19171`: 3D surface example bug for non-square grid -* :ghissue:`18112`: set_{x,y,z}bound 3d limits are not persistent upon interactive rotation -* :ghissue:`19078`: _update_patch_limits should not use CLOSEPOLY verticies for updating -* :ghissue:`16123`: test_dpi_ratio_change fails on Windows/Qt5Agg -* :ghissue:`15796`: [DOC] PDF build of matplotlib own documentation crashes with LaTeX error "too deeply nested" -* :ghissue:`19091`: 3D Axes don't work in SubFigures -* :ghissue:`7238`: better document how to configure artists for picking -* :ghissue:`11147`: FR: add a supxlabel and supylabel as the suptitle function which are already exist -* :ghissue:`17417`: tutorial on how autoscaling works -* :ghissue:`18917`: Spy displays nothing for full arrays -* :ghissue:`18562`: Allow slider valstep to be arraylike -* :ghissue:`18942`: AnnotationBbox errors with kwargs -* :ghissue:`11472`: Mention predefined keyboard shortcuts in the docs on event-handling -* :ghissue:`18898`: wrong bounds checking in streamplot start_points -* :ghissue:`18974`: Contour label demo would benefit from some more info and/or references. -* :ghissue:`17708`: Mention rasterized option in more methods -* :ghissue:`18826`: Pgf plots with pdflatex broken -* :ghissue:`18959`: Add sphinx-gallery cross ref instructions to documenting guide -* :ghissue:`18926`: Font not installed, unclear warning -* :ghissue:`18891`: SVG animation doesn't work in HTMLWriter due to wrong type -* :ghissue:`18222`: It is painful as a new user, to figure out what AxesSubplot is -* :ghissue:`16153`: gap size for contour labels is poorly estimated -* :ghissue:`17910`: Improve accessibility of form controls in HTML widgets -* :ghissue:`18273`: Surprising behavior of shared axes with categorical units -* :ghissue:`18731`: Compact string notation for subplot_mosaic -* :ghissue:`18221`: Add example of keys to explore 3D data -* :ghissue:`18882`: Incorrect version requirement message from setup.py -* :ghissue:`18491`: Mostly unused glossary still exists in our docs -* :ghissue:`18548`: add_subplot(..., axes_cls=...) -* :ghissue:`8249`: Bug in mpl_connect(): On Windows, with the wx backend, arrow keys are not reported -* :ghissue:`15609`: [SPRINT] Update Named Colors Example -* :ghissue:`18800`: Log-scale ticker fails at 1e-323 -* :ghissue:`18392`: ``scatter()``: ``edgecolor`` takes precedence over ``edgecolors`` -* :ghissue:`18301`: "How to use Matplotlib in a web application server" should be made an example -* :ghissue:`18386`: Path3DCollection.set_color(self, c) does not change the color of scatter points. -* :ghissue:`8946`: Axes with sharex can have divergent axes after setting tick markers -* :ghissue:`2294`: tex option not respected by date x-axis -* :ghissue:`4382`: use new binary comm in nbagg -* :ghissue:`17088`: ``projection`` kwarg could be better documented. -* :ghissue:`18717`: Tick formatting issues on horizontal histogram with datetime on 3.3.2 -* :ghissue:`12636`: Characters doesn't display correctly when figure saved as pdf with a custom font -* :ghissue:`18377`: Matplotlib picks a headless backend on Linux if Wayland is available but X11 isn't -* :ghissue:`13199`: Examples that use private APIs -* :ghissue:`18662`: Inconsistent setting of axis limits with autoscale=False -* :ghissue:`18690`: Class deprecation machinery and mixins -* :ghissue:`18510`: Build fails on OS X: wrong minimum version -* :ghissue:`18641`: Conversion cache cleaning is broken with xdist -* :ghissue:`15614`: named color examples need borders -* :ghissue:`5519`: The linestyle 'None', ' ' and '' not supported by PathPatch. -* :ghissue:`17487`: Polygon selector with useblit=True - polygon dissapears -* :ghissue:`17476`: RectangleSelector fails to clear itself after being toggled inactive and then back to active. -* :ghissue:`18600`: plt.errorbar raises error when given marker= -* :ghissue:`18355`: Optional components required to build docs aren't documented -* :ghissue:`18428`: small bug in the mtplotlib gallery -* :ghissue:`4438`: inconsistent behaviour of the errorevery option in pyplot.errorbar() to the markevery keyword -* :ghissue:`5823`: pleas dont include the Google Analytics tracking in the off-line doc -* :ghissue:`13035`: Path3DCollection from 3D scatter cannot set_color -* :ghissue:`9725`: scatter - set_facecolors is not working on Axes3D -* :ghissue:`3370`: Patch3DCollection doesn't update color after calling set_color -* :ghissue:`18427`: yaxis.set_label_position("right") resets "horizontalalignment" -* :ghissue:`3129`: super-ify the code base -* :ghissue:`17518`: Plotting legend throws error "font family ['serif'] not found. Falling back to DejaVu Sans" -* :ghissue:`18282`: Bad interaction between kerning and non-latin1 characters in pdf output -* :ghissue:`6669`: [Feature request] Functions for "manually" plotting histograms -* :ghissue:`18411`: 2-D array RGB and RGBA values not understood in plt.plot() -* :ghissue:`18404`: Double-click events are not recognised in Jupyter notebook -* :ghissue:`12027`: marker_props is never used in the constructor of RectangleSelector -* :ghissue:`18438`: Warn when a non-started animation is gc'ed. -* :ghissue:`11259`: Symbols appear as streaks with usetex=True, times font and PDF backend -* :ghissue:`18345`: Specify what sharex and sharey do... -* :ghissue:`18082`: Feature Request: Non overlapping Bubble Plots -* :ghissue:`568`: Support error bars on 3D plots -* :ghissue:`17865`: Earlier validation of color inputs -* :ghissue:`18363`: ha="right" breaks AnchoredText placement. -* :ghissue:`11050`: keyboard shortcuts don't get registered using the experimental toolmanager with qt -* :ghissue:`17906`: Set mathtext.fontset per element -* :ghissue:`18311`: Subplot scatter plot with categorical data on y-axis with 'sharey=True' option overwrites the y-axis labels -* :ghissue:`10304`: No link to shared axes for Axis.set_units -* :ghissue:`17712`: constrained_layout fails on suptitle+colorbars+some figure sizes -* :ghissue:`14638`: colorbar.make_axes doesn't anchor in constrained_layout -* :ghissue:`18299`: New configure_subplots behaves badly on TkAgg backend -* :ghissue:`18300`: Remove the examples category "Our Favorite Recipies" -* :ghissue:`18077`: Imshow breaks if given a unyt_array input -* :ghissue:`7074`: Using a linestyle cycler with plt.errorbar results in strange plots -* :ghissue:`18236`: FuncAnimation fails to display with interval 0 on Tkagg backend -* :ghissue:`8107`: invalid command name "..._on_timer" in FuncAnimation for (too) small interval -* :ghissue:`18272`: Add CI Intervall to boxplot notch documentation -* :ghissue:`18137`: axhspan() in empty plots changes the xlimits of plots sharing the X axis -* :ghissue:`18246`: test_never_update is flaky -* :ghissue:`5856`: Horizontal stem plot -* :ghissue:`18160`: Add feature request template -* :ghissue:`17197`: Missing character upon savefig() with Free Serif font -* :ghissue:`17013`: Request: provide a contextmanager for ioff or allow plt.figure(draw_on_create=False) -* :ghissue:`17537`: hat graphs need an example... -* :ghissue:`17755`: mplot3d: add_collection3d issues -* :ghissue:`18192`: Cannot save SVG file with FIPS compliant Python -* :ghissue:`17574`: Vertical alignment of tick labels containing minus in font size other than 10 with usetex=True -* :ghissue:`18097`: Feature Request: Allow hexbin to use a string for parameter C to refer to column in data (DataFrame) -* :ghissue:`17689`: Add pause/resume methods to Animation baseclass -* :ghissue:`16087`: Error with greek letters in pdf export when using usetex=True and mathptmx -* :ghissue:`17136`: set_ticks() changes view limits of the axis -* :ghissue:`12198`: axvline incorrectly tries to handle unitized ymin, ymax -* :ghissue:`9139`: Python3 matplotlib 2.0.2 with Times New Roman misses unicode minus sign in pdf -* :ghissue:`5970`: pyplot.scatter raises obscure error when mistakenly passed a third string param -* :ghissue:`17936`: documenattion and behavior do not match for suppressing (PDF) metadata -* :ghissue:`17932`: latex textrm does not work in Cairo backend -* :ghissue:`17714`: Universal fullscreen command -* :ghissue:`4584`: ColorbarBase draws edges in slightly wrong positions. -* :ghissue:`17878`: flipping of imshow in specgram -* :ghissue:`6118`: consider using qtpy for qt abstraction layer -* :ghissue:`17908`: rcParams restrictions on frame_formats are out of sync with supported values (HTMLWriter) -* :ghissue:`17867`: datetime plotting broken on master -* :ghissue:`16810`: Docs do not build in parallel -* :ghissue:`17918`: Extend hatch reference -* :ghissue:`17149`: Rasterization creates multiple bitmap elements and large file sizes -* :ghissue:`17855`: Add Hatch Example to gallery -* :ghissue:`15821`: Should constrained_layout work as plt.figure() argument? -* :ghissue:`15616`: Colormaps should have a ``_repr_html_`` that is an image of the colormap -* :ghissue:`17579`: ``BoundaryNorm`` yield a ``ZeroDivisionError: division by zero`` -* :ghissue:`17652`: NEP 29 : Stop support fro Python 3.6 soon ? -* :ghissue:`11095`: Repeated plot calls with xunits=None throws exception -* :ghissue:`17733`: Rename "array" (and perhaps "fields") section of Axes API -* :ghissue:`15610`: Link to most recent DevDocs when installing from Master Source -* :ghissue:`17817`: (documentation, possible first-timer bug) Typo and grammar on Legends and Annotations for tight layout guide page -* :ghissue:`17804`: Setting the norm on imshow object removes colorbar ylabel -* :ghissue:`17758`: bar, barh should take a list of hatches like it does of colors -* :ghissue:`17746`: Antialiasing with colorbars? -* :ghissue:`17659`: Enhancement: Set tick and ticklabel colors separately from matplotlib style file -* :ghissue:`17144`: Wrong icon on windows task bar for figure windows -* :ghissue:`2870`: Wrong symbols from a TrueType font - - -Previous GitHub Stats ---------------------- +Pull Requests (86): + +* :ghpull:`24341`: Backport PR #24301 on branch v3.6.x (Restore get_renderer function in deprecated tight_layout) +* :ghpull:`24301`: Restore get_renderer function in deprecated tight_layout +* :ghpull:`24337`: Backport PR #24238 on branch v3.6.x (Update example and docstring to encourage the use of functools.partial in FuncAnimation) +* :ghpull:`24336`: Backport PR #24335 on branch v3.6.x (Fix missing word in ImageMagickWriter docstring.) +* :ghpull:`20358`: Updates example and docstring to encourage the use of functools.partial in FuncAnimation +* :ghpull:`24238`: Update example and docstring to encourage the use of functools.partial in FuncAnimation +* :ghpull:`24335`: Fix missing word in ImageMagickWriter docstring. +* :ghpull:`24330`: Backport PR #24282 on branch v3.6.x (Fix some minor docstring typos) +* :ghpull:`24323`: Backport PR #24320 on branch v3.6.x (DOC: add warning note to imsave) +* :ghpull:`24282`: Fix some minor docstring typos +* :ghpull:`24327`: Backport PR #24310 on branch v3.6.x (show axes changing in animate decay example) +* :ghpull:`24310`: show axes changing in animate decay example +* :ghpull:`24324`: Backport PR #24259 on branch v3.6.x (Move empty hexbin fix to make_norm_from_scale.) +* :ghpull:`24325`: Backport PR #24095 on branch v3.6.x (nb/webagg: Move mouse events to outer canvas div) +* :ghpull:`24326`: Backport PR #24318 on branch v3.6.x (Bump pypa/cibuildwheel from 2.11.1 to 2.11.2) +* :ghpull:`24318`: Bump pypa/cibuildwheel from 2.11.1 to 2.11.2 +* :ghpull:`24095`: nb/webagg: Move mouse events to outer canvas div +* :ghpull:`24259`: Move empty hexbin fix to make_norm_from_scale. +* :ghpull:`24320`: DOC: add warning note to imsave +* :ghpull:`24297`: Backport PR #24294 on branch v3.6.x (Run test if fontconfig is present) +* :ghpull:`24294`: Run test if fontconfig is present +* :ghpull:`24286`: Backport PR #24284 on branch v3.6.x (Remove comment about cmap from voxels docstring) +* :ghpull:`24284`: Remove comment about cmap from voxels docstring +* :ghpull:`24280`: Backport PR #24145 on branch v3.6.x (Updated Angles on Bracket arrow styles example to make angles clear #23176) +* :ghpull:`24145`: Updated Angles on Bracket arrow styles example to make angles clear #23176 +* :ghpull:`24270`: Backport PR #24265 on branch v3.6.x (Restore (and warn on) seaborn styles in style.library) +* :ghpull:`24271`: Backport PR #24266 on branch v3.6.x (TST: Increase fp tolerance on more tests for new NumPy) +* :ghpull:`24266`: TST: Increase fp tolerance on more tests for new NumPy +* :ghpull:`24265`: Restore (and warn on) seaborn styles in style.library +* :ghpull:`24267`: Backport PR #24261 on branch v3.6.x (Fix pie chart in demo_agg_filter.py) +* :ghpull:`24261`: Fix pie chart in demo_agg_filter.py +* :ghpull:`24258`: Backport PR #24108 on branch v3.6.x (Add 3D plots to plot_types doc page) +* :ghpull:`24108`: Add 3D plots to plot_types doc page +* :ghpull:`24255`: Backport PR #24250 on branch v3.6.x (Fix key reporting in pick events) +* :ghpull:`24250`: Fix key reporting in pick events +* :ghpull:`24237`: Backport PR #24197 on branch v3.6.x (Properly set and inherit backend_version.) +* :ghpull:`24197`: Properly set and inherit backend_version. +* :ghpull:`24234`: Backport PR #23607 on branch v3.6.x (DOC: document that appearance is part of our stable API) +* :ghpull:`24233`: Backport PR #23985 on branch v3.6.x (Improve rubberband rendering in wx and tk) +* :ghpull:`24232`: Backport PR #24096 on branch v3.6.x ([DOC]: Add simple animation scatter plot to the example documentation) +* :ghpull:`24231`: Backport PR #24009 on branch v3.6.x (Fix evaluating colormaps on non-numpy arrays) +* :ghpull:`24230`: Backport PR #24229 on branch v3.6.x (FIX: do not mutate dictionaries passed in by user) +* :ghpull:`23607`: DOC: document that appearance is part of our stable API +* :ghpull:`23985`: Improve rubberband rendering in wx and tk +* :ghpull:`24096`: [DOC]: Add simple animation scatter plot to the example documentation +* :ghpull:`24009`: Fix evaluating colormaps on non-numpy arrays +* :ghpull:`24229`: FIX: do not mutate dictionaries passed in by user +* :ghpull:`24223`: Backport PR #24184 on branch v3.6.x (Add tests for ToolManager) +* :ghpull:`24219`: Backport PR #23995 on branch v3.6.x (DOC: Lowercase some parameter names) +* :ghpull:`23995`: DOC: Lowercase some parameter names +* :ghpull:`24184`: Add tests for ToolManager +* :ghpull:`24211`: Backport PR #24202 on branch v3.6.x (Bump pypa/cibuildwheel from 2.10.2 to 2.11.1) +* :ghpull:`24214`: Backport PR #24169 on branch v3.6.x ([DOC]: added parent link for ``FuncAnimation`` and ``ArtistAnimation``) +* :ghpull:`24169`: [DOC]: add parent link for ``FuncAnimation`` and ``ArtistAnimation`` +* :ghpull:`24202`: Bump pypa/cibuildwheel from 2.10.2 to 2.11.1 +* :ghpull:`24206`: Backport PR #24081 on branch v3.6.x (TST: force test with shared test image to run in serial) +* :ghpull:`24181`: Backport PR #24177 on branch v3.6.x (Don't simplify paths used for autoscaling) +* :ghpull:`24200`: Backport PR #24193 on branch v3.6.x (DOC: Explain gridsize in hexbin()) +* :ghpull:`24201`: Backport PR #24194 on branch v3.6.x (DOC: Improve plot_directive documentation) +* :ghpull:`24194`: DOC: Improve plot_directive documentation +* :ghpull:`24193`: DOC: Explain gridsize in hexbin() +* :ghpull:`24192`: Backport PR #24187 on branch v3.6.x (DOC: Fix toc structure in explain/interactive) +* :ghpull:`24186`: Backport PR #24157 on branch v3.6.x (test only PR milestoning guidance) +* :ghpull:`24187`: DOC: Fix toc structure in explain/interactive +* :ghpull:`24190`: DOC: fix markup +* :ghpull:`24157`: test only PR milestoning guidance +* :ghpull:`24183`: Backport PR #24178 on branch v3.6.x (Fall back to Python-level Thread for GUI warning) +* :ghpull:`24180`: Backport PR #24173 on branch v3.6.x (TST: convert nose-style tests) +* :ghpull:`24178`: Fall back to Python-level Thread for GUI warning +* :ghpull:`24177`: Don't simplify paths used for autoscaling +* :ghpull:`24173`: TST: convert nose-style tests +* :ghpull:`24174`: Backport PR #24171 on branch v3.6.x (Fix example where wrong variable was used) +* :ghpull:`24176`: Backport PR #24167 on branch v3.6.x (FIX: turn off layout engine tightbbox) +* :ghpull:`24167`: FIX: turn off layout engine tightbbox +* :ghpull:`24171`: Fix example where wrong variable was used +* :ghpull:`24172`: Backport PR #24158 on branch v3.6.x (Fix Qt with PySide6 6.4.0) +* :ghpull:`24158`: Fix Qt with PySide6 6.4.0 +* :ghpull:`24165`: Backport PR #24164 on branch v3.6.x (Fix argument order in hist() docstring.) +* :ghpull:`24164`: Fix argument order in hist() docstring. +* :ghpull:`24151`: Backport PR #24149 on branch v3.6.x (FIX: handle input to ax.bar that is all nan) +* :ghpull:`24149`: FIX: handle input to ax.bar that is all nan +* :ghpull:`24146`: Backport PR #24137 on branch v3.6.x (Add note about blitting and zorder in animations) +* :ghpull:`24137`: Add note about blitting and zorder in animations +* :ghpull:`24134`: Backport PR #24130 on branch v3.6.x (DOC: align contour parameter doc with implementation) +* :ghpull:`24130`: DOC: align contour parameter doc with implementation +* :ghpull:`24081`: TST: force test with shared test image to run in serial + +Issues (21): + +* :ghissue:`20326`: FuncAnimation Named Arguments +* :ghissue:`24332`: [Bug]: backend bug in matplotlib==3.6.1 with python3.11 and PySide6==6.4.0.1 +* :ghissue:`24296`: [Doc]: Axes limits not updated in animate decay +* :ghissue:`24089`: [Bug]: Resizing does not work in WebAgg backend in Safari +* :ghissue:`3657`: matplotlib.pyplot.imsave colormaps some grayscale images before saving them +* :ghissue:`24060`: [TST] Upcoming dependency test failures +* :ghissue:`24264`: [Bug]: Setting matplotlib.pyplot.style.library['seaborn-colorblind'] result in key error on matplotlib v3.6.1 +* :ghissue:`23900`: [Doc]: Adding some 3D plots to plot gallery +* :ghissue:`24199`: [Bug]: pick events do not forward mouseevent-key on Linux +* :ghissue:`23969`: [ENH]: Make rubber band more visible +* :ghissue:`23132`: [Bug]: call cmap object on torch.tensor will output first element all 0 +* :ghissue:`21349`: [Bug]: Hexbin gridsize interpreted differently for x and y +* :ghissue:`22905`: [Doc]: Duplicated toc entries +* :ghissue:`24094`: [Bug]: macOS: PyPy 3.8 (v7.3.9) threading get_native_id Broken +* :ghissue:`24097`: [Bug]: ax.hist density not auto-scaled when using histtype='step' +* :ghissue:`24148`: remove nose-style test classes +* :ghissue:`24133`: [Bug]: Incorrect crop after constrained layout with equal aspect ratio and bbox_inches = tight +* :ghissue:`24155`: [Bug]: TypeError: int() argument must be a string, a bytes-like object or a number, not 'KeyboardModifier' +* :ghissue:`24127`: [Bug]: ax.bar raises for all-nan data on matplotlib 3.6.1 +* :ghissue:`2959`: artists zorder is ignored during animations +* :ghissue:`24121`: [Doc]: Contour functions: auto-generated levels + + +Previous GitHub statistics +-------------------------- .. toctree:: diff --git a/doc/users/index.rst b/doc/users/index.rst index 852ca4321e90..0ecff14c9a23 100644 --- a/doc/users/index.rst +++ b/doc/users/index.rst @@ -1,24 +1,41 @@ .. _users-guide-index: -############ -User's Guide -############ +.. redirect-from:: /contents -.. only:: html - :Release: |version| - :Date: |today| +########### +Users guide +########### + +General +####### + +.. toctree:: + :maxdepth: 2 + + getting_started/index.rst + installing/index.rst + explain/index.rst + faq/index.rst + resources/index.rst + +Tutorials and examples +###################### .. toctree:: - :maxdepth: 2 - - installing.rst - ../tutorials/index.rst - interactive.rst - whats_new.rst - history.rst - github_stats.rst - whats_new_old.rst - license.rst - ../citing.rst - credits.rst + :maxdepth: 1 + + ../plot_types/index.rst + ../tutorials/index.rst + ../gallery/index.rst + +Reference +######### + +.. toctree:: + :maxdepth: 2 + + ../api/index.rst + ../devel/index.rst + project/index.rst + release_notes.rst diff --git a/doc/users/installing.rst b/doc/users/installing.rst deleted file mode 100644 index 545ae4fa153e..000000000000 --- a/doc/users/installing.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../../INSTALL.rst diff --git a/doc/users/installing/index.rst b/doc/users/installing/index.rst new file mode 100644 index 000000000000..1b6fdf65055f --- /dev/null +++ b/doc/users/installing/index.rst @@ -0,0 +1,328 @@ +.. redirect-from:: /users/installing + +############ +Installation +############ + +============================== +Installing an official release +============================== + +Matplotlib releases are available as wheel packages for macOS, Windows and +Linux on `PyPI `_. Install it using +``pip``: + +.. code-block:: sh + + python -m pip install -U pip + python -m pip install -U matplotlib + +If this command results in Matplotlib being compiled from source and +there's trouble with the compilation, you can add ``--prefer-binary`` to +select the newest version of Matplotlib for which there is a +precompiled wheel for your OS and Python. + +.. note:: + + The following backends work out of the box: Agg, ps, pdf, svg + + Python is typically shipped with tk bindings which are used by + TkAgg. + + For support of other GUI frameworks, LaTeX rendering, saving + animations and a larger selection of file formats, you can + install :ref:`optional_dependencies`. + +========================= +Third-party distributions +========================= + +Various third-parties provide Matplotlib for their environments. + +Conda packages +============== +Matplotlib is available both via the *anaconda main channel* + +.. code-block:: sh + + conda install matplotlib + +as well as via the *conda-forge community channel* + +.. code-block:: sh + + conda install -c conda-forge matplotlib + +Python distributions +==================== + +Matplotlib is part of major Python distributions: + +- `Anaconda `_ + +- `ActiveState ActivePython + `_ + +- `WinPython `_ + +Linux package manager +===================== + +If you are using the Python version that comes with your Linux distribution, +you can install Matplotlib via your package manager, e.g.: + +* Debian / Ubuntu: ``sudo apt-get install python3-matplotlib`` +* Fedora: ``sudo dnf install python3-matplotlib`` +* Red Hat: ``sudo yum install python3-matplotlib`` +* Arch: ``sudo pacman -S python-matplotlib`` + +.. redirect-from:: /users/installing/installing_source + +.. _install_from_source: + +========================== +Installing a nightly build +========================== + +Matplotlib makes nightly development build wheels available on the +`scipy-wheels-nightly Anaconda Cloud organization +`_. +These wheels can be installed with ``pip`` by specifying scipy-wheels-nightly +as the package index to query: + +.. code-block:: sh + + python -m pip install \ + --upgrade \ + --pre \ + --index-url https://pypi.anaconda.org/scipy-wheels-nightly/simple \ + --extra-index-url https://pypi.org/simple \ + matplotlib + +====================== +Installing from source +====================== + +If you are interested in contributing to Matplotlib development, +running the latest source code, or just like to build everything +yourself, it is not difficult to build Matplotlib from source. + +First you need to install the :ref:`dependencies`. + +A C compiler is required. Typically, on Linux, you will need ``gcc``, which +should be installed using your distribution's package manager; on macOS, you +will need xcode_; on Windows, you will need `Visual Studio`_ 2015 or later. + +For those using Visual Studio, make sure "Desktop development with C++" is +selected, and that the latest MSVC, "C++ CMake tools for Windows," and a +Windows SDK compatible with your version of Windows are selected and installed. +They should be selected by default under the "Optional" subheading, but are +required to build matplotlib from source. + +.. _xcode: https://guide.macports.org/chunked/installing.html#installing.xcode + +.. _Visual Studio: https://visualstudio.microsoft.com/downloads/ + +The easiest way to get the latest development version to start contributing +is to go to the git `repository `_ +and run:: + + git clone https://github.com/matplotlib/matplotlib.git + +or:: + + git clone git@github.com:matplotlib/matplotlib.git + +If you're developing, it's better to do it in editable mode. The reason why +is that pytest's test discovery only works for Matplotlib +if installation is done this way. Also, editable mode allows your code changes +to be instantly propagated to your library code without reinstalling (though +you will have to restart your python process / kernel):: + + cd matplotlib + python -m pip install -e . + +If you're not developing, it can be installed from the source directory with +a simple (just replace the last step):: + + python -m pip install . + +To run the tests you will need to install some additional dependencies:: + + python -m pip install -r requirements/dev/dev-requirements.txt + +Then, if you want to update your Matplotlib at any time, just do:: + + git pull + +When you run ``git pull``, if the output shows that only Python files have +been updated, you are all set. If C files have changed, you need to run ``pip +install -e .`` again to compile them. + +There is more information on :ref:`using git ` in the developer +docs. + +.. warning:: + + The following instructions in this section are for very custom + installations of Matplotlib. Proceed with caution because these instructions + may result in your build producing unexpected behavior and/or causing + local testing to fail. + +If you would like to build from a tarball, grab the latest *tar.gz* release +file from `the PyPI files page `_. + +We provide a `mplsetup.cfg`_ file which you can use to customize the build +process. For example, which default backend to use, whether some of the +optional libraries that Matplotlib ships with are installed, and so on. This +file will be particularly useful to those packaging Matplotlib. + +.. _mplsetup.cfg: https://raw.githubusercontent.com/matplotlib/matplotlib/main/mplsetup.cfg.template + +If you are building your own Matplotlib wheels (or sdists) on Windows, note +that any DLLs that you copy into the source tree will be packaged too. + +========================== +Installing for development +========================== +See :ref:`installing_for_devs`. + +.. redirect-from:: /faq/installing_faq +.. redirect-from:: /users/faq/installing_faq + +.. _installing-faq: + +========================== +Frequently asked questions +========================== + +.. contents:: + :backlinks: none + :local: + +Report a compilation problem +============================ + +See :ref:`reporting-problems`. + +Matplotlib compiled fine, but nothing shows up when I use it +============================================================ + +The first thing to try is a :ref:`clean install ` and see if +that helps. If not, the best way to test your install is by running a script, +rather than working interactively from a python shell or an integrated +development environment such as :program:`IDLE` which add additional +complexities. Open up a UNIX shell or a DOS command prompt and run, for +example:: + + python -c "from pylab import *; set_loglevel('debug'); plot(); show()" + +This will give you additional information about which backends Matplotlib is +loading, version information, and more. At this point you might want to make +sure you understand Matplotlib's :doc:`configuration ` +process, governed by the :file:`matplotlibrc` configuration file which contains +instructions within and the concept of the Matplotlib backend. + +If you are still having trouble, see :ref:`reporting-problems`. + +.. _clean-install: + +How to completely remove Matplotlib +=================================== + +Occasionally, problems with Matplotlib can be solved with a clean +installation of the package. In order to fully remove an installed Matplotlib: + +1. Delete the caches from your :ref:`Matplotlib configuration directory + `. + +2. Delete any Matplotlib directories or eggs from your :ref:`installation + directory `. + +OSX Notes +========= + +.. _which-python-for-osx: + +Which python for OSX? +--------------------- + +Apple ships OSX with its own Python, in ``/usr/bin/python``, and its own copy +of Matplotlib. Unfortunately, the way Apple currently installs its own copies +of NumPy, Scipy and Matplotlib means that these packages are difficult to +upgrade (see `system python packages`_). For that reason we strongly suggest +that you install a fresh version of Python and use that as the basis for +installing libraries such as NumPy and Matplotlib. One convenient way to +install Matplotlib with other useful Python software is to use the Anaconda_ +Python scientific software collection, which includes Python itself and a +wide range of libraries; if you need a library that is not available from the +collection, you can install it yourself using standard methods such as *pip*. +See the Anaconda web page for installation support. + +.. _system python packages: + https://github.com/MacPython/wiki/wiki/Which-Python#system-python-and-extra-python-packages +.. _Anaconda: https://www.anaconda.com/ + +Other options for a fresh Python install are the standard installer from +`python.org `_, or installing +Python using a general OSX package management system such as `homebrew +`_ or `macports `_. Power users on +OSX will likely want one of homebrew or macports on their system to install +open source software packages, but it is perfectly possible to use these +systems with another source for your Python binary, such as Anaconda +or Python.org Python. + +.. _install_osx_binaries: + +Installing OSX binary wheels +---------------------------- + +If you are using Python from https://www.python.org, Homebrew, or Macports, +then you can use the standard pip installer to install Matplotlib binaries in +the form of wheels. + +pip is installed by default with python.org and Homebrew Python, but needs to +be manually installed on Macports with :: + + sudo port install py38-pip + +Once pip is installed, you can install Matplotlib and all its dependencies with +from the Terminal.app command line:: + + python3 -m pip install matplotlib + +You might also want to install IPython or the Jupyter notebook (``python3 -m pip +install ipython notebook``). + +Checking your installation +-------------------------- + +The new version of Matplotlib should now be on your Python "path". Check this +at the Terminal.app command line:: + + python3 -c 'import matplotlib; print(matplotlib.__version__, matplotlib.__file__)' + +You should see something like :: + + 3.6.0 /Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/matplotlib/__init__.py + +where ``3.6.0`` is the Matplotlib version you just installed, and the path +following depends on whether you are using Python.org Python, Homebrew or +Macports. If you see another version, or you get an error like :: + + Traceback (most recent call last): + File "", line 1, in + ImportError: No module named matplotlib + +then check that the Python binary is the one you expected by running :: + + which python3 + +If you get a result like ``/usr/bin/python...``, then you are getting the +Python installed with OSX, which is probably not what you want. Try closing +and restarting Terminal.app before running the check again. If that doesn't fix +the problem, depending on which Python you wanted to use, consider reinstalling +Python.org Python, or check your homebrew or macports setup. Remember that +the disk image installer only works for Python.org Python, and will not get +picked up by other Pythons. If all these fail, please :ref:`let us know +`. diff --git a/doc/users/license.rst b/doc/users/license.rst deleted file mode 100644 index 4dcb0798712f..000000000000 --- a/doc/users/license.rst +++ /dev/null @@ -1,141 +0,0 @@ -.. _license: - -*********************************************** -License -*********************************************** - - -Matplotlib only uses BSD compatible code, and its license is based on -the `PSF `_ license. See the Open -Source Initiative `licenses page -`_ for details on individual -licenses. Non-BSD compatible licenses (e.g., LGPL) are acceptable in -matplotlib toolkits. For a discussion of the motivations behind the -licencing choice, see :ref:`license-discussion`. - -Copyright Policy -================ - -John Hunter began matplotlib around 2003. Since shortly before his -passing in 2012, Michael Droettboom has been the lead maintainer of -matplotlib, but, as has always been the case, matplotlib is the work -of many. - -Prior to July of 2013, and the 1.3.0 release, the copyright of the -source code was held by John Hunter. As of July 2013, and the 1.3.0 -release, matplotlib has moved to a shared copyright model. - -matplotlib uses a shared copyright model. Each contributor maintains -copyright over their contributions to matplotlib. But, it is important to -note that these contributions are typically only changes to the -repositories. Thus, the matplotlib source code, in its entirety, is not -the copyright of any single person or institution. Instead, it is the -collective copyright of the entire matplotlib Development Team. If -individual contributors want to maintain a record of what -changes/contributions they have specific copyright on, they should -indicate their copyright in the commit message of the change, when -they commit the change to one of the matplotlib repositories. - -The Matplotlib Development Team is the set of all contributors to the -matplotlib project. A full list can be obtained from the git version -control logs. - -License agreement for matplotlib |version| -============================================== - -1. This LICENSE AGREEMENT is between the Matplotlib Development Team -("MDT"), and the Individual or Organization ("Licensee") accessing and -otherwise using matplotlib software in source or binary form and its -associated documentation. - -2. Subject to the terms and conditions of this License Agreement, MDT -hereby grants Licensee a nonexclusive, royalty-free, world-wide license -to reproduce, analyze, test, perform and/or display publicly, prepare -derivative works, distribute, and otherwise use matplotlib |version| -alone or in any derivative version, provided, however, that MDT's -License Agreement and MDT's notice of copyright, i.e., "Copyright (c) -2012-2013 Matplotlib Development Team; All Rights Reserved" are retained in -matplotlib |version| alone or in any derivative version prepared by -Licensee. - -3. In the event Licensee prepares a derivative work that is based on or -incorporates matplotlib |version| or any part thereof, and wants to -make the derivative work available to others as provided herein, then -Licensee hereby agrees to include in any such work a brief summary of -the changes made to matplotlib |version|. - -4. MDT is making matplotlib |version| available to Licensee on an "AS -IS" basis. MDT MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, MDT MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB |version| -WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. - -5. MDT SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB -|version| FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR -LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING -MATPLOTLIB |version|, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF -THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -7. Nothing in this License Agreement shall be deemed to create any -relationship of agency, partnership, or joint venture between MDT and -Licensee. This License Agreement does not grant permission to use MDT -trademarks or trade name in a trademark sense to endorse or promote -products or services of Licensee, or any third party. - -8. By copying, installing or otherwise using matplotlib |version|, -Licensee agrees to be bound by the terms and conditions of this License -Agreement. - -License agreement for matplotlib versions prior to 1.3.0 -======================================================== - -1. This LICENSE AGREEMENT is between John D. Hunter ("JDH"), and the -Individual or Organization ("Licensee") accessing and otherwise using -matplotlib software in source or binary form and its associated -documentation. - -2. Subject to the terms and conditions of this License Agreement, JDH -hereby grants Licensee a nonexclusive, royalty-free, world-wide license -to reproduce, analyze, test, perform and/or display publicly, prepare -derivative works, distribute, and otherwise use matplotlib |version| -alone or in any derivative version, provided, however, that JDH's -License Agreement and JDH's notice of copyright, i.e., "Copyright (c) -2002-2009 John D. Hunter; All Rights Reserved" are retained in -matplotlib |version| alone or in any derivative version prepared by -Licensee. - -3. In the event Licensee prepares a derivative work that is based on or -incorporates matplotlib |version| or any part thereof, and wants to -make the derivative work available to others as provided herein, then -Licensee hereby agrees to include in any such work a brief summary of -the changes made to matplotlib |version|. - -4. JDH is making matplotlib |version| available to Licensee on an "AS -IS" basis. JDH MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, JDH MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB |version| -WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. - -5. JDH SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB -|version| FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR -LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING -MATPLOTLIB |version|, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF -THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -7. Nothing in this License Agreement shall be deemed to create any -relationship of agency, partnership, or joint venture between JDH and -Licensee. This License Agreement does not grant permission to use JDH -trademarks or trade name in a trademark sense to endorse or promote -products or services of Licensee, or any third party. - -8. By copying, installing or otherwise using matplotlib |version|, -Licensee agrees to be bound by the terms and conditions of this License -Agreement. diff --git a/doc/users/next_whats_new.rst b/doc/users/next_whats_new.rst new file mode 100644 index 000000000000..ddd82faf6731 --- /dev/null +++ b/doc/users/next_whats_new.rst @@ -0,0 +1,13 @@ +.. _whats-new: + +================ +Next what's new? +================ + +.. ifconfig:: releaselevel == 'dev' + + .. toctree:: + :glob: + :maxdepth: 1 + + next_whats_new/* diff --git a/doc/users/next_whats_new/README.rst b/doc/users/next_whats_new/README.rst index 6555d229a1b5..e1b27ef97f1e 100644 --- a/doc/users/next_whats_new/README.rst +++ b/doc/users/next_whats_new/README.rst @@ -15,7 +15,7 @@ Please avoid using references in section titles, as it causes links to be confusing in the table of contents. Instead, ensure that a reference is included in the descriptive text. Include contents of the form: :: - Section Title for Feature + Section title for feature ------------------------- A bunch of text about how awesome the new feature is and examples of how diff --git a/doc/users/prev_whats_new/changelog.rst b/doc/users/prev_whats_new/changelog.rst index e35878e9935e..f1f3cca3225e 100644 --- a/doc/users/prev_whats_new/changelog.rst +++ b/doc/users/prev_whats_new/changelog.rst @@ -4,4032 +4,4745 @@ List of changes to Matplotlib prior to 2015 =========================================== This is a list of the changes made to Matplotlib from 2003 to 2015. For more -recent changes, please refer to the `what's new <../whats_new.html>`_ or -the `API changes <../../api/api_changes.html>`_. +recent changes, please refer to the :doc:`/users/release_notes`. -2015-11-16 Levels passed to contour(f) and tricontour(f) must be in increasing - order. +2015-11-16 + Levels passed to contour(f) and tricontour(f) must be in increasing order. -2015-10-21 Added TextBox widget +2015-10-21 + Added TextBox widget + +2015-10-21 + Added get_ticks_direction() +2015-02-27 + Added the rcParam 'image.composite_image' to permit users to decide whether + they want the vector graphics backends to combine all images within a set + of axes into a single composite image. (If images do not get combined, + users can open vector graphics files in Adobe Illustrator or Inkscape and + edit each image individually.) -2015-10-21 Added get_ticks_direction() +2015-02-19 + Rewrite of C++ code that calculates contours to add support for corner + masking. This is controlled by the 'corner_mask' keyword in plotting + commands 'contour' and 'contourf'. - IMT -2015-02-27 Added the rcParam 'image.composite_image' to permit users - to decide whether they want the vector graphics backends to combine - all images within a set of axes into a single composite image. - (If images do not get combined, users can open vector graphics files - in Adobe Illustrator or Inkscape and edit each image individually.) +2015-01-23 + Text bounding boxes are now computed with advance width rather than ink + area. This may result in slightly different placement of text. + +2014-10-27 + Allowed selection of the backend using the :envvar:`MPLBACKEND` environment + variable. Added documentation on backend selection methods. + +2014-09-27 + Overhauled `.colors.LightSource`. Added `.LightSource.hillshade` to allow + the independent generation of illumination maps. Added new types of + blending for creating more visually appealing shaded relief plots (e.g. + ``blend_mode="overlay"``, etc, in addition to the legacy "hsv" mode). + +2014-06-10 + Added Colorbar.remove() + +2014-06-07 + Fixed bug so radial plots can be saved as ps in py3k. -2015-02-19 Rewrite of C++ code that calculates contours to add support for - corner masking. This is controlled by the 'corner_mask' keyword - in plotting commands 'contour' and 'contourf'. - IMT +2014-06-01 + Changed the fmt kwarg of errorbar to support the mpl convention that + "none" means "don't draw it", and to default to the empty string, so that + plotting of data points is done with the plot() function defaults. + Deprecated use of the None object in place "none". + +2014-05-22 + Allow the linscale keyword parameter of symlog scale to be smaller than + one. + +2014-05-20 + Added logic to in FontManager to invalidate font-cache if if font-family + rcparams have changed. + +2014-05-16 + Fixed the positioning of multi-line text in the PGF backend. + +2014-05-14 + Added Axes.add_image() as the standard way to add AxesImage instances to + Axes. This improves the consistency with add_artist(), add_collection(), + add_container(), add_line(), add_patch(), and add_table(). + +2014-05-02 + Added colorblind-friendly colormap, named 'Wistia'. + +2014-04-27 + Improved input clean up in Axes.{h|v}lines + Coerce input into a 1D ndarrays (after dealing with units). + +2014-04-27 + removed un-needed cast to float in stem + +2014-04-23 + Updated references to "ipython -pylab" The preferred method for invoking + pylab is now using the "%pylab" magic. + -Chris G. + +2014-04-22 + Added (re-)generate a simple automatic legend to "Figure Options" dialog of + the Qt4Agg backend. + +2014-04-22 + Added an example showing the difference between interpolation = 'none' and + interpolation = 'nearest' in `~.Axes.imshow` when saving vector graphics + files. + +2014-04-22 + Added violin plotting functions. See `.Axes.violinplot`, `.Axes.violin`, + `.cbook.violin_stats` and `.mlab.GaussianKDE` for details. + +2014-04-10 + Fixed the triangular marker rendering error. The "Up" triangle was rendered + instead of "Right" triangle and vice-versa. + +2014-04-08 + Fixed a bug in parasite_axes.py by making a list out of a generator at line + 263. + +2014-04-02 + Added ``clipon=False`` to patch creation of wedges and shadows in + `~.Axes.pie`. + +2014-02-25 + In backend_qt4agg changed from using update -> repaint under windows. See + comment in source near ``self._priv_update`` for longer explanation. + +2014-03-27 + Added tests for pie ccw parameter. Removed pdf and svg images from tests + for pie linewidth parameter. + +2014-03-24 + Changed the behaviour of axes to not ignore leading or trailing patches of + height 0 (or width 0) while calculating the x and y axis limits. Patches + having both height == 0 and width == 0 are ignored. + +2014-03-24 + Added bool kwarg (manage_xticks) to boxplot to enable/disable the + management of the xlimits and ticks when making a boxplot. Default in True + which maintains current behavior by default. + +2014-03-23 + Fixed a bug in projections/polar.py by making sure that the theta value + being calculated when given the mouse coordinates stays within the range of + 0 and 2 * pi. + +2014-03-22 + Added the keyword arguments wedgeprops and textprops to pie. Users can + control the wedge and text properties of the pie in more detail, if they + choose. + +2014-03-17 + Bug was fixed in append_axes from the AxesDivider class would not append + axes in the right location with respect to the reference locator axes + +2014-03-13 + Add parameter 'clockwise' to function pie, True by default. + +2014-02-28 + Added 'origin' kwarg to `~.Axes.spy` + +2014-02-27 + Implemented separate horizontal/vertical axes padding to the ImageGrid in + the AxesGrid toolkit + +2014-02-27 + Allowed markevery property of matplotlib.lines.Line2D to be, an int numpy + fancy index, slice object, or float. The float behaviour turns on markers + at approximately equal display-coordinate-distances along the line. + +2014-02-25 + In backend_qt4agg changed from using update -> repaint under windows. See + comment in source near ``self._priv_update`` for longer explanation. + +2014-01-02 + `~.Axes.triplot` now returns the artist it adds and support of line and + marker kwargs has been improved. GBY + +2013-12-30 + Made streamplot grid size consistent for different types of density + argument. A 30x30 grid is now used for both density=1 and density=(1, 1). + +2013-12-03 + Added a pure boxplot-drawing method that allow a more complete + customization of boxplots. It takes a list of dicts contains stats. Also + created a function (`.cbook.boxplot_stats`) that generates the stats + needed. + +2013-11-28 + Added qhull extension module to perform Delaunay triangulation more + robustly than before. It is used by tri.Triangulation (and hence all + pyplot.tri* methods) and mlab.griddata. Deprecated matplotlib.delaunay + module. - IMT + +2013-11-05 + Add power-law normalization method. This is useful for, e.g., showing small + populations in a "hist2d" histogram. + +2013-10-27 + Added get_rlabel_position and set_rlabel_position methods to PolarAxes to + control angular position of radial tick labels. + +2013-10-06 + Add stride-based functions to mlab for easy creation of 2D arrays with less + memory. + +2013-10-06 + Improve window and detrend functions in mlab, particular support for 2D + arrays. + +2013-10-06 + Improve performance of all spectrum-related mlab functions and plots. + +2013-10-06 + Added support for magnitude, phase, and angle spectrums to axes.specgram, + and support for magnitude, phase, angle, and complex spectrums to + mlab-specgram. + +2013-10-06 + Added magnitude_spectrum, angle_spectrum, and phase_spectrum plots, as well + as magnitude_spectrum, angle_spectrum, phase_spectrum, and complex_spectrum + functions to mlab + +2013-07-12 + Added support for datetime axes to 2d plots. Axis values are passed through + Axes.convert_xunits/Axes.convert_yunits before being used by + contour/contourf, pcolormesh and pcolor. + +2013-07-12 + Allowed matplotlib.dates.date2num, matplotlib.dates.num2date, and + matplotlib.dates.datestr2num to accept n-d inputs. Also factored in support + for n-d arrays to matplotlib.dates.DateConverter and + matplotlib.units.Registry. + +2013-06-26 + Refactored the axes module: the axes module is now a folder, containing the + following submodule: + + - _subplots.py, containing all the subplots helper methods + - _base.py, containing several private methods and a new _AxesBase class. + This _AxesBase class contains all the methods that are not directly + linked to plots of the "old" Axes + - _axes.py contains the Axes class. This class now inherits from _AxesBase: + it contains all "plotting" methods and labelling methods. + + This refactoring should not affect the API. Only private methods are not + importable from the axes module anymore. + +2013-05-18 + Added support for arbitrary rasterization resolutions to the SVG backend. + Previously the resolution was hard coded to 72 dpi. Now the backend class + takes a image_dpi argument for its constructor, adjusts the image bounding + box accordingly and forwards a magnification factor to the image renderer. + The code and results now resemble those of the PDF backend. + - MW + +2013-05-08 + Changed behavior of hist when given stacked=True and normed=True. + Histograms are now stacked first, then the sum is normalized. Previously, + each histogram was normalized, then they were stacked. + +2013-04-25 + Changed all instances of:: + + from matplotlib import MatplotlibDeprecationWarning as mplDeprecation + + to:: + + from cbook import mplDeprecation + + and removed the import into the matplotlib namespace in __init__.py + - Thomas Caswell + +2013-04-15 + Added 'axes.xmargin' and 'axes.ymargin' to rpParams to set default margins + on auto-scaling. - TAC + +2013-04-16 + Added patheffect support for Line2D objects. -JJL + +2013-03-31 + Added support for arbitrary unstructured user-specified triangulations to + Axes3D.tricontour[f] - Damon McDougall + +2013-03-19 + Added support for passing *linestyle* kwarg to `~.Axes.step` so all + `~.Axes.plot` kwargs are passed to the underlying `~.Axes.plot` call. -TAC + +2013-02-25 + Added classes CubicTriInterpolator, UniformTriRefiner, TriAnalyzer to + matplotlib.tri module. - GBy + +2013-01-23 + Add 'savefig.directory' to rcParams to remember and fill in the last + directory saved to for figure save dialogs - Martin Spacek + +2013-01-13 + Add eventplot method to axes and pyplot and EventCollection class to + collections. + +2013-01-08 + Added two extra titles to axes which are flush with the left and right + edges of the plot respectively. Andrew Dawson + +2013-01-07 + Add framealpha keyword argument to legend - PO + +2013-01-16 + Till Stensitzki added a baseline feature to stackplot + +2012-12-22 + Added classes for interpolation within triangular grids + (LinearTriInterpolator) and to find the triangles in which points lie + (TrapezoidMapTriFinder) to matplotlib.tri module. - IMT + +2012-12-05 + Added MatplotlibDeprecationWarning class for signaling deprecation. + Matplotlib developers can use this class as follows:: + + from matplotlib import MatplotlibDeprecationWarning as mplDeprecation + + In light of the fact that Python builtin DeprecationWarnings are ignored by + default as of Python 2.7, this class was put in to allow for the signaling + of deprecation, but via UserWarnings which are not ignored by default. - PI + +2012-11-27 + Added the *mtext* parameter for supplying matplotlib.text.Text instances to + RendererBase.draw_tex and RendererBase.draw_text. This allows backends to + utilize additional text attributes, like the alignment of text elements. - + pwuertz + +2012-11-26 + deprecate matplotlib/mpl.py, which was used only in pylab.py and is now + replaced by the more suitable ``import matplotlib as mpl``. - PI + +2012-11-25 + Make rc_context available via pyplot interface - PI + +2012-11-16 + plt.set_cmap no longer throws errors if there is not already an active + colorable artist, such as an image, and just sets up the colormap to use + from that point forward. - PI + +2012-11-16 + Added the function _get_rbga_face, which is identical to _get_rbg_face + except it return a (r,g,b,a) tuble, to line2D. Modified Line2D.draw to use + _get_rbga_face to get the markerface color so that any alpha set by + markerfacecolor will respected. - Thomas Caswell + +2012-11-13 + Add a symmetric log normalization class to colors.py. Also added some + tests for the normalization class. Till Stensitzki -2015-01-23 Text bounding boxes are now computed with advance width rather than - ink area. This may result in slightly different placement of text. +2012-11-12 + Make axes.stem take at least one argument. Uses a default range(n) when + the first arg not provided. Damon McDougall -2014-10-27 Allowed selection of the backend using the :envvar:`MPLBACKEND` environment - variable. Added documentation on backend selection methods. +2012-11-09 + Make plt.subplot() without arguments act as subplot(111) - PI -2014-09-27 Overhauled `.colors.LightSource`. Added `.LightSource.hillshade` to - allow the independent generation of illumination maps. Added new - types of blending for creating more visually appealing shaded relief - plots (e.g. ``blend_mode="overlay"``, etc, in addition to the legacy - "hsv" mode). +2012-11-08 + Replaced plt.figure and plt.subplot calls by the newer, more convenient + single call to plt.subplots() in the documentation examples - PI -2014-06-10 Added Colorbar.remove() +2012-10-05 + Add support for saving animations as animated GIFs. - JVDP + +2012-08-11 + Fix path-closing bug in patches.Polygon, so that regardless of whether the + path is the initial one or was subsequently set by set_xy(), get_xy() will + return a closed path if and only if get_closed() is True. Thanks to Jacob + Vanderplas. - EF + +2012-08-05 + When a norm is passed to contourf, either or both of the vmin, vmax + attributes of that norm are now respected. Formerly they were respected + only if both were specified. In addition, vmin and/or vmax can now be + passed to contourf directly as kwargs. - EF + +2012-07-24 + Contourf handles the extend kwarg by mapping the extended ranges outside + the normed 0-1 range so that they are handled by colormap colors determined + by the set_under and set_over methods. Previously the extended ranges were + mapped to 0 or 1 so that the "under" and "over" colormap colors were + ignored. This change also increases slightly the color contrast for a given + set of contour levels. - EF + +2012-06-24 + Make use of mathtext in tick labels configurable - DSD + +2012-06-05 + Images loaded through PIL are now ordered correctly - CG + +2012-06-02 + Add new Axes method and pyplot function, hist2d. - PO + +2012-05-31 + Remove support for 'cairo.' style of backend specification. + Deprecate 'cairo.format' and 'savefig.extension' rcParams and replace with + 'savefig.format'. - Martin Spacek + +2012-05-29 + pcolormesh now obeys the passed in "edgecolor" kwarg. To support this, the + "shading" argument to pcolormesh now only takes "flat" or "gouraud". To + achieve the old "faceted" behavior, pass "edgecolors='k'". - MGD + +2012-05-22 + Added radius kwarg to pie charts. - HH + +2012-05-22 + Collections now have a setting "offset_position" to select whether the + offsets are given in "screen" coordinates (default, following the old + behavior) or "data" coordinates. This is currently used internally to + improve the performance of hexbin. + + As a result, the "draw_path_collection" backend methods have grown a new + argument "offset_position". - MGD + +2012-05-04 + Add a new argument to pie charts - startingangle - that allows one to + specify the angle offset for the first wedge of the chart. - EP + +2012-05-03 + symlog scale now obeys the logarithmic base. Previously, it was completely + ignored and always treated as base e. - MGD + +2012-05-03 + Allow linscalex/y keyword to symlog scale that allows the size of the + linear portion relative to the logarithmic portion to be adjusted. - MGD + +2012-04-14 + Added new plot style: stackplot. This new feature supports stacked area + plots. - Damon McDougall + +2012-04-06 + When path clipping changes a LINETO to a MOVETO, it also changes any + CLOSEPOLY command to a LINETO to the initial point. This fixes a problem + with pdf and svg where the CLOSEPOLY would then draw a line to the latest + MOVETO position instead of the intended initial position. - JKS + +2012-03-27 + Add support to ImageGrid for placing colorbars only at one edge of each + column/row. - RMM + +2012-03-07 + Refactor movie writing into useful classes that make use of pipes to write + image data to ffmpeg or mencoder. Also improve settings for these and the + ability to pass custom options. - RMM + +2012-02-29 + errorevery keyword added to errorbar to enable errorbar subsampling. fixes + issue #600. + +2012-02-28 + Added plot_trisurf to the mplot3d toolkit. This supports plotting three + dimensional surfaces on an irregular grid. - Damon McDougall + +2012-01-23 + The radius labels in polar plots no longer use a fixed padding, but use a + different alignment depending on the quadrant they are in. This fixes + numerical problems when (rmax - rmin) gets too small. - MGD + +2012-01-08 + Add axes.streamplot to plot streamlines of a velocity field. Adapted from + Tom Flannaghan streamplot implementation. -TSY + +2011-12-29 + ps and pdf markers are now stroked only if the line width is nonzero for + consistency with agg, fixes issue #621. - JKS + +2011-12-27 + Work around an EINTR bug in some versions of subprocess. - JKS + +2011-10-25 + added support for \operatorname to mathtext, including the ability to + insert spaces, such as $\operatorname{arg\,max}$ - PI + +2011-08-18 + Change api of Axes.get_tightbbox and add an optional keyword parameter + *call_axes_locator*. - JJL + +2011-07-29 + A new rcParam "axes.formatter.use_locale" was added, that, when True, will + use the current locale to format tick labels. This means that, for + example, in the fr_FR locale, ',' will be used as a decimal separator. - + MGD -2014-06-07 Fixed bug so radial plots can be saved as ps in py3k. +2011-07-15 + The set of markers available in the plot() and scatter() commands has been + unified. In general, this gives more options to both than were previously + available, however, there is one backward-incompatible change to the + markers in scatter: + + "d" used to mean "diamond", it now means "narrow diamond". "D" can be + used for a "diamond". + + -MGD + +2011-07-13 + Fix numerical problems in symlog scale, particularly when linthresh <= 1.0. + Symlog plots may look different if one was depending on the old broken + behavior - MGD + +2011-07-10 + Fixed argument handling error in tripcolor/triplot/tricontour, issue #203. + - IMT + +2011-07-08 + Many functions added to mplot3d.axes3d to bring Axes3D objects more + feature-parity with regular Axes objects. Significant revisions to the + documentation as well. - BVR + +2011-07-07 + Added compatibility with IPython strategy for picking a version of Qt4 + support, and an rcParam for making the choice explicitly: backend.qt4. - EF + +2011-07-07 + Modified AutoMinorLocator to improve automatic choice of the number of + minor intervals per major interval, and to allow one to specify this number + via a kwarg. - EF -2014-06-01 Changed the fmt kwarg of errorbar to support the - the mpl convention that "none" means "don't draw it", - and to default to the empty string, so that plotting - of data points is done with the plot() function - defaults. Deprecated use of the None object in place - "none". +2011-06-28 + 3D versions of scatter, plot, plot_wireframe, plot_surface, bar3d, and some + other functions now support empty inputs. - BVR -2014-05-22 Allow the linscale keyword parameter of symlog scale to be - smaller than one. +2011-06-22 + Add set_theta_offset, set_theta_direction and set_theta_zero_location to + polar axes to control the location of 0 and directionality of theta. - MGD -2014-05-20 Added logic to in FontManager to invalidate font-cache if - if font-family rcparams have changed. +2011-06-22 + Add axes.labelweight parameter to set font weight to axis labels - MGD. -2014-05-16 Fixed the positioning of multi-line text in the PGF backend. +2011-06-20 + Add pause function to pyplot. - EF -2014-05-14 Added Axes.add_image() as the standard way to add AxesImage - instances to Axes. This improves the consistency with - add_artist(), add_collection(), add_container(), add_line(), - add_patch(), and add_table(). +2011-06-16 + Added *bottom* keyword parameter for the stem command. Also, implemented a + legend handler for the stem plot. - JJL -2014-05-02 Added colorblind-friendly colormap, named 'Wistia'. +2011-06-16 + Added legend.frameon rcParams. - Mike Kaufman -2014-04-27 Improved input clean up in Axes.{h|v}lines - Coerce input into a 1D ndarrays (after dealing with units). +2011-05-31 + Made backend_qt4 compatible with PySide . - Gerald Storer -2014-04-27 removed un-needed cast to float in stem +2011-04-17 + Disable keyboard auto-repeat in qt4 backend by ignoring key events + resulting from auto-repeat. This makes constrained zoom/pan work. - EF -2014-04-23 Updated references to "ipython -pylab" - The preferred method for invoking pylab is now using the - "%pylab" magic. - -Chris G. +2011-04-14 + interpolation="nearest" always interpolate images. A new mode "none" is + introduced for no interpolation - JJL -2014-04-22 Added (re-)generate a simple automatic legend to "Figure Options" - dialog of the Qt4Agg backend. +2011-04-03 + Fixed broken pick interface to AsteriskCollection objects used by scatter. + - EF -2014-04-22 Added an example showing the difference between - interpolation = 'none' and interpolation = 'nearest' in - `~.Axes.imshow` when saving vector graphics files. +2011-04-01 + The plot directive Sphinx extension now supports all of the features in the + Numpy fork of that extension. These include doctest formatting, an + 'include-source' option, and a number of new configuration options. - MGD -2014-04-22 Added violin plotting functions. See `.Axes.violinplot`, - `.Axes.violin`, `.cbook.violin_stats` and `.mlab.GaussianKDE` for - details. +2011-03-29 + Wrapped ViewVCCachedServer definition in a factory function. This class + now inherits from urllib2.HTTPSHandler in order to fetch data from github, + but HTTPSHandler is not defined if python was built without SSL support. - + DSD -2014-04-10 Fixed the triangular marker rendering error. The "Up" triangle was - rendered instead of "Right" triangle and vice-versa. +2011-03-10 + Update pytz version to 2011c, thanks to Simon Cross. - JKS -2014-04-08 Fixed a bug in parasite_axes.py by making a list out - of a generator at line 263. +2011-03-06 + Add standalone tests.py test runner script. - JKS + +2011-03-06 + Set edgecolor to 'face' for scatter asterisk-type symbols; this fixes a bug + in which these symbols were not responding to the c kwarg. The symbols + have no face area, so only the edgecolor is visible. - EF -2014-04-02 Added ``clipon=False`` to patch creation of wedges and shadows - in `~.Axes.pie`. +2011-02-27 + Support libpng version 1.5.x; suggestion by Michael Albert. Changed + installation specification to a minimum of libpng version 1.2. - EF -2014-02-25 In backend_qt4agg changed from using update -> repaint under - windows. See comment in source near ``self._priv_update`` for - longer explanation. +2011-02-20 + clabel accepts a callable as an fmt kwarg; modified patch by Daniel Hyams. + - EF -2014-03-27 Added tests for pie ccw parameter. Removed pdf and svg images - from tests for pie linewidth parameter. +2011-02-18 + scatter([], []) is now valid. Also fixed issues with empty collections - + BVR -2014-03-24 Changed the behaviour of axes to not ignore leading or trailing - patches of height 0 (or width 0) while calculating the x and y - axis limits. Patches having both height == 0 and width == 0 are - ignored. +2011-02-07 + Quick workaround for dviread bug #3175113 - JKS -2014-03-24 Added bool kwarg (manage_xticks) to boxplot to enable/disable - the managemnet of the xlimits and ticks when making a boxplot. - Default in True which maintains current behavior by default. +2011-02-05 + Add cbook memory monitoring for Windows, using tasklist. - EF -2014-03-23 Fixed a bug in projections/polar.py by making sure that the theta - value being calculated when given the mouse coordinates stays within - the range of 0 and 2 * pi. +2011-02-05 + Speed up Normalize and LogNorm by using in-place operations and by using + float32 for float32 inputs and for ints of 2 bytes or shorter; based on + patch by Christoph Gohlke. - EF -2014-03-22 Added the keyword arguments wedgeprops and textprops to pie. - Users can control the wedge and text properties of the pie - in more detail, if they choose. +2011-02-04 + Changed imshow to use rgba as uint8 from start to finish, instead of going + through an intermediate step as double precision; thanks to Christoph + Gohlke. - EF -2014-03-17 Bug was fixed in append_axes from the AxesDivider class would not - append axes in the right location with respect to the reference - locator axes +2011-01-13 + Added zdir and offset arguments to contourf3d to bring contourf3d in + feature parity with contour3d. - BVR -2014-03-13 Add parameter 'clockwise' to function pie, True by default. +2011-01-04 + Tag 1.0.1 for release at r8896 -2014-02-28 Added 'origin' kwarg to `~.Axes.spy` +2011-01-03 + Added display of ticker offset to 3d plots. - BVR + +2011-01-03 + Turn off tick labeling on interior subplots for pyplots.subplots when + sharex/sharey is True. - JDH -2014-02-27 Implemented separate horizontal/vertical axes padding to the - ImageGrid in the AxesGrid toolkit +2010-12-29 + Implement axes_divider.HBox and VBox. -JJL -2014-02-27 Allowed markevery property of matplotlib.lines.Line2D to be, an int - numpy fancy index, slice object, or float. The float behaviour - turns on markers at approximately equal display-coordinate-distances - along the line. +2010-11-22 + Fixed error with Hammer projection. - BVR + +2010-11-12 + Fixed the placement and angle of axis labels in 3D plots. - BVR + +2010-11-07 + New rc parameters examples.download and examples.directory allow bypassing + the download mechanism in get_sample_data. - JKS + +2010-10-04 + Fix JPEG saving bug: only accept the kwargs documented by PIL for JPEG + files. - JKS + +2010-09-15 + Remove unused _wxagg extension and numerix.h. - EF + +2010-08-25 + Add new framework for doing animations with examples.- RM + +2010-08-21 + Remove unused and inappropriate methods from Tick classes: + set_view_interval, get_minpos, and get_data_interval are properly found in + the Axis class and don't need to be duplicated in XTick and YTick. - EF + +2010-08-21 + Change Axis.set_view_interval() so that when updating an existing interval, + it respects the orientation of that interval, and can enlarge but not + reduce the interval. This fixes a bug in which Axis.set_ticks would change + the view limits of an inverted axis. Whether set_ticks should be affecting + the viewLim at all remains an open question. - EF + +2010-08-16 + Handle NaN's correctly in path analysis routines. Fixes a bug where the + best location for a legend was not calculated correctly when the line + contains NaNs. - MGD -2014-02-25 In backend_qt4agg changed from using update -> repaint under - windows. See comment in source near ``self._priv_update`` for - longer explanation. +2010-08-14 + Fix bug in patch alpha handling, and in bar color kwarg - EF + +2010-08-12 + Removed all traces of numerix module after 17 months of deprecation + warnings. - EF -2014-01-02 `~.Axes.triplot` now returns the artist it adds and support of line and - marker kwargs has been improved. GBY +2010-08-05 + Added keyword arguments 'thetaunits' and 'runits' for polar plots. Fixed + PolarAxes so that when it set default Formatters, it marked them as such. + Fixed semilogx and semilogy to no longer blindly reset the ticker + information on the non-log axis. Axes.arrow can now accept unitized data. + - JRE -2013-12-30 Made streamplot grid size consistent for different types of density - argument. A 30x30 grid is now used for both density=1 and - density=(1, 1). +2010-08-03 + Add support for MPLSETUPCFG variable for custom setup.cfg filename. Used + by sage buildbot to build an mpl w/ no gui support - JDH -2013-12-03 Added a pure boxplot-drawing method that allow a more complete - customization of boxplots. It takes a list of dicts contains stats. - Also created a function (`.cbook.boxplot_stats`) that generates the - stats needed. +2010-08-01 + Create directory specified by MPLCONFIGDIR if it does not exist. - ADS -2013-11-28 Added qhull extension module to perform Delaunay triangulation more - robustly than before. It is used by tri.Triangulation (and hence - all pyplot.tri* methods) and mlab.griddata. Deprecated - matplotlib.delaunay module. - IMT +2010-07-20 + Return Qt4's default cursor when leaving the canvas - DSD -2013-11-05 Add power-law normalization method. This is useful for, - e.g., showing small populations in a "hist2d" histogram. +2010-07-06 + Tagging for mpl 1.0 at r8502 -2013-10-27 Added get_rlabel_position and set_rlabel_position methods to - PolarAxes to control angular position of radial tick labels. +2010-07-05 + Added Ben Root's patch to put 3D plots in arbitrary axes, allowing you to + mix 3d and 2d in different axes/subplots or to have multiple 3D plots in + one figure. See examples/mplot3d/subplot3d_demo.py - JDH -2013-10-06 Add stride-based functions to mlab for easy creation of 2D arrays - with less memory. +2010-07-05 + Preferred kwarg names in set_xlim are now 'left' and 'right'; in set_ylim, + 'bottom' and 'top'; original kwargs are still accepted without complaint. - + EF -2013-10-06 Improve window and detrend functions in mlab, particulart support for - 2D arrays. +2010-07-05 + TkAgg and FltkAgg backends are now consistent with other interactive + backends: when used in scripts from the command line (not from ipython + -pylab), show blocks, and can be called more than once. - EF -2013-10-06 Improve performance of all spectrum-related mlab functions and plots. +2010-07-02 + Modified CXX/WrapPython.h to fix "swab bug" on solaris so mpl can compile + on Solaris with CXX6 in the trunk. Closes tracker bug 3022815 - JDH -2013-10-06 Added support for magnitude, phase, and angle spectrums to - axes.specgram, and support for magnitude, phase, angle, and complex - spectrums to mlab-specgram. +2010-06-30 + Added autoscale convenience method and corresponding pyplot function for + simplified control of autoscaling; and changed axis, set_xlim, and set_ylim + so that by default, they turn off the autoscaling on the relevant axis or + axes. Therefore one can call set_xlim before plotting a line, for example, + and the limits will be retained. - EF -2013-10-06 Added magnitude_spectrum, angle_spectrum, and phase_spectrum plots, - as well as magnitude_spectrum, angle_spectrum, phase_spectrum, - and complex_spectrum functions to mlab +2010-06-20 + Added Axes.tick_params and corresponding pyplot function to control tick + and tick label appearance after an Axes has been created. - EF -2013-07-12 Added support for datetime axes to 2d plots. Axis values are passed - through Axes.convert_xunits/Axes.convert_yunits before being used by - contour/contourf, pcolormesh and pcolor. +2010-06-09 + Allow Axes.grid to control minor gridlines; allow Axes.grid and Axis.grid + to control major and minor gridlines in the same method call. - EF -2013-07-12 Allowed matplotlib.dates.date2num, matplotlib.dates.num2date, - and matplotlib.dates.datestr2num to accept n-d inputs. Also - factored in support for n-d arrays to matplotlib.dates.DateConverter - and matplotlib.units.Registry. +2010-06-06 + Change the way we do split/dividend adjustments in finance.py to handle + dividends and fix the zero division bug reported in sf bug 2949906 and + 2123566. Note that volume is not adjusted because the Yahoo CSV does not + distinguish between share split and dividend adjustments making it near + impossible to get volume adjustment right (unless we want to guess based on + the size of the adjustment or scrape the html tables, which we don't) - JDH -2013-06-26 Refactored the axes module: the axes module is now a folder, - containing the following submodule: - - _subplots.py, containing all the subplots helper methods - - _base.py, containing several private methods and a new - _AxesBase class. This _AxesBase class contains all the methods - that are not directly linked to plots of the "old" Axes - - _axes.py contains the Axes class. This class now inherits from - _AxesBase: it contains all "plotting" methods and labelling - methods. +2010-06-06 + Updated dateutil to 1.5 and pytz to 2010h. - This refactoring should not affect the API. Only private methods - are not importable from the axes module anymore. +2010-06-02 + Add error_kw kwarg to Axes.bar(). - EF -2013-05-18 Added support for arbitrary rasterization resolutions to the - SVG backend. Previously the resolution was hard coded to 72 - dpi. Now the backend class takes a image_dpi argument for - its constructor, adjusts the image bounding box accordingly - and forwards a magnification factor to the image renderer. - The code and results now resemble those of the PDF backend. - - MW +2010-06-01 + Fix pcolormesh() and QuadMesh to pass on kwargs as appropriate. - RM -2013-05-08 Changed behavior of hist when given stacked=True and normed=True. - Histograms are now stacked first, then the sum is normalized. - Previously, each histogram was normalized, then they were stacked. +2010-05-18 + Merge mpl_toolkits.gridspec into the main tree. - JJL + +2010-05-04 + Improve backend_qt4 so it displays figures with the correct size - DSD + +2010-04-20 + Added generic support for connecting to a timer for events. This adds + TimerBase, TimerGTK, TimerQT, TimerWx, and TimerTk to the backends and a + new_timer() method to each backend's canvas to allow ease of creating a new + timer. - RM + +2010-04-20 + Added margins() Axes method and pyplot function. - EF -2013-04-25 Changed all instances of: +2010-04-18 + update the axes_grid documentation. -JJL - from matplotlib import MatplotlibDeprecationWarning as mplDeprecation - to: +2010-04-18 + Control MaxNLocator parameters after instantiation, and via + Axes.locator_params method, with corresponding pyplot function. -EF - from cbook import mplDeprecation +2010-04-18 + Control ScalarFormatter offsets directly and via the + Axes.ticklabel_format() method, and add that to pyplot. -EF + +2010-04-16 + Add a close_event to the backends. -RM - and removed the import into the matplotlib namespace in __init__.py - Thomas Caswell +2010-04-06 + modify axes_grid examples to use axes_grid1 and axisartist. -JJL -2013-04-15 Added 'axes.xmargin' and 'axes.ymargin' to rpParams to set default - margins on auto-scaleing. - TAC +2010-04-06 + rebase axes_grid using axes_grid1 and axisartist modules. -JJL -2013-04-16 Added patheffect support for Line2D objects. -JJL +2010-04-06 + axes_grid toolkit is split into two separate modules, axes_grid1 and + axisartist. -JJL -2013-03-31 Added support for arbitrary unstructured user-specified - triangulations to Axes3D.tricontour[f] - Damon McDougall +2010-04-05 + Speed up import: import pytz only if and when it is needed. It is not + needed if the rc timezone is UTC. - EF -2013-03-19 Added support for passing *linestyle* kwarg to `~.Axes.step` so all `~.Axes.plot` - kwargs are passed to the underlying `~.Axes.plot` call. -TAC +2010-04-03 + Added color kwarg to Axes.hist(), based on work by Jeff Klukas. - EF -2013-02-25 Added classes CubicTriInterpolator, UniformTriRefiner, TriAnalyzer - to matplotlib.tri module. - GBy +2010-03-24 + refactor colorbar code so that no cla() is necessary when mappable is + changed. -JJL + +2010-03-22 + fix incorrect rubber band during the zoom mode when mouse leaves the axes. + -JJL -2013-01-23 Add 'savefig.directory' to rcParams to remember and fill in the last - directory saved to for figure save dialogs - Martin Spacek +2010-03-21 + x/y key during the zoom mode only changes the x/y limits. -JJL -2013-01-13 Add eventplot method to axes and pyplot and EventCollection class - to collections. +2010-03-20 + Added pyplot.sca() function suggested by JJL. - EF -2013-01-08 Added two extra titles to axes which are flush with the left and - right edges of the plot respectively. - Andrew Dawson +2010-03-20 + Added conditional support for new Tooltip API in gtk backend. - EF -2013-01-07 Add framealpha keyword argument to legend - PO +2010-03-20 + Changed plt.fig_subplot() to plt.subplots() after discussion on list, and + changed its API to return axes as a numpy object array (with control of + dimensions via squeeze keyword). FP. -2013-01-16 Till Stensitzki added a baseline feature to stackplot +2010-03-13 + Manually brought in commits from branch:: -2012-12-22 Added classes for interpolation within triangular grids - (LinearTriInterpolator) and to find the triangles in which points - lie (TrapezoidMapTriFinder) to matplotlib.tri module. - IMT + ------------------------------------------------------------------------ + r8191 | leejjoon | 2010-03-13 + 17:27:57 -0500 (Sat, 13 Mar 2010) | 1 line -2012-12-05 Added MatplotlibDeprecationWarning class for signaling deprecation. - Matplotlib developers can use this class as follows: + fix the bug that handles for scatter are incorrectly set when dpi!=72. + Thanks to Ray Speth for the bug report. - from matplotlib import MatplotlibDeprecationWarning as mplDeprecation +2010-03-03 + Manually brought in commits from branch via diff/patch (svnmerge is broken):: - In light of the fact that Python builtin DeprecationWarnings are - ignored by default as of Python 2.7, this class was put in to allow - for the signaling of deprecation, but via UserWarnings which are - not ignored by default. - PI + ------------------------------------------------------------------------ + r8175 | leejjoon | 2010-03-03 + 10:03:30 -0800 (Wed, 03 Mar 2010) | 1 line -2012-11-27 Added the *mtext* parameter for supplying matplotlib.text.Text - instances to RendererBase.draw_tex and RendererBase.draw_text. - This allows backends to utilize additional text attributes, like - the alignment of text elements. - pwuertz + fix arguments of allow_rasterization.draw_wrapper + ------------------------------------------------------------------------ + r8174 | jdh2358 | 2010-03-03 + 09:15:58 -0800 (Wed, 03 Mar 2010) | 1 line -2012-11-26 deprecate matplotlib/mpl.py, which was used only in pylab.py and is - now replaced by the more suitable ``import matplotlib as mpl``. - PI + added support for favicon in docs build + ------------------------------------------------------------------------ + r8173 | jdh2358 | 2010-03-03 + 08:56:16 -0800 (Wed, 03 Mar 2010) | 1 line -2012-11-25 Make rc_context available via pyplot interface - PI + applied Mattias get_bounds patch + ------------------------------------------------------------------------ + r8172 | jdh2358 | 2010-03-03 + 08:31:42 -0800 (Wed, 03 Mar 2010) | 1 line -2012-11-16 plt.set_cmap no longer throws errors if there is not already - an active colorable artist, such as an image, and just sets - up the colormap to use from that point forward. - PI + fix svnmerge download instructions + ------------------------------------------------------------------------ + r8171 | jdh2358 | 2010-03-03 + 07:47:48 -0800 (Wed, 03 Mar 2010) | 1 line -2012-11-16 Added the funcction _get_rbga_face, which is identical to - _get_rbg_face except it return a (r,g,b,a) tuble, to line2D. - Modified Line2D.draw to use _get_rbga_face to get the markerface - color so that any alpha set by markerfacecolor will respected. - - Thomas Caswell +2010-02-25 + add annotation_demo3.py that demonstrates new functionality. -JJL -2012-11-13 Add a symmetric log normalization class to colors.py. - Also added some tests for the normalization class. - Till Stensitzki +2010-02-25 + refactor Annotation to support arbitrary Transform as xycoords or + textcoords. Also, if a tuple of two coordinates is provided, they are + interpreted as coordinates for each x and y position. -JJL -2012-11-12 Make axes.stem take at least one argument. - Uses a default range(n) when the first arg not provided. - Damon McDougall +2010-02-24 + Added pyplot.fig_subplot(), to create a figure and a group of subplots in a + single call. This offers an easier pattern than manually making figures + and calling add_subplot() multiple times. FP -2012-11-09 Make plt.subplot() without arguments act as subplot(111) - PI +2010-02-17 + Added Gokhan's and Mattias' customizable keybindings patch for the toolbar. + You can now set the keymap.* properties in the matplotlibrc file. + Newbindings were added for toggling log scaling on the x-axis. JDH -2012-11-08 Replaced plt.figure and plt.subplot calls by the newer, more - convenient single call to plt.subplots() in the documentation - examples - PI +2010-02-16 + Committed TJ's filled marker patch for left|right|bottom|top|full filled + markers. See examples/pylab_examples/filledmarker_demo.py. JDH -2012-10-05 Add support for saving animations as animated GIFs. - JVDP +2010-02-11 + Added 'bootstrap' option to boxplot. This allows bootstrap estimates of + median confidence intervals. Based on an initial patch by Paul Hobson. - + ADS -2012-08-11 Fix path-closing bug in patches.Polygon, so that regardless - of whether the path is the initial one or was subsequently - set by set_xy(), get_xy() will return a closed path if and - only if get_closed() is True. Thanks to Jacob Vanderplas. - EF +2010-02-06 + Added setup.cfg "basedirlist" option to override setting in setupext.py + "basedir" dictionary; added "gnu0" platform requested by Benjamin Drung. - + EF -2012-08-05 When a norm is passed to contourf, either or both of the - vmin, vmax attributes of that norm are now respected. - Formerly they were respected only if both were - specified. In addition, vmin and/or vmax can now - be passed to contourf directly as kwargs. - EF +2010-02-06 + Added 'xy' scaling option to EllipseCollection. - EF -2012-07-24 Contourf handles the extend kwarg by mapping the extended - ranges outside the normed 0-1 range so that they are - handled by colormap colors determined by the set_under - and set_over methods. Previously the extended ranges - were mapped to 0 or 1 so that the "under" and "over" - colormap colors were ignored. This change also increases - slightly the color contrast for a given set of contour - levels. - EF +2010-02-03 + Made plot_directive use a custom PlotWarning category, so that warnings can + be turned into fatal errors easily if desired. - FP -2012-06-24 Make use of mathtext in tick labels configurable - DSD +2010-01-29 + Added draggable method to Legend to allow mouse drag placement. Thanks + Adam Fraser. JDH -2012-06-05 Images loaded through PIL are now ordered correctly - CG +2010-01-25 + Fixed a bug reported by Olle Engdegard, when using histograms with + stepfilled and log=True - MM -2012-06-02 Add new Axes method and pyplot function, hist2d. - PO +2010-01-16 + Upgraded CXX to 6.1.1 - JDH -2012-05-31 Remove support for 'cairo.' style of backend specification. - Deprecate 'cairo.format' and 'savefig.extension' rcParams and - replace with 'savefig.format'. - Martin Spacek +2009-01-16 + Don't create minor ticks on top of existing major ticks. Patch by Neil + Crighton. -ADS -2012-05-29 pcolormesh now obeys the passed in "edgecolor" kwarg. - To support this, the "shading" argument to pcolormesh now only - takes "flat" or "gouraud". To achieve the old "faceted" behavior, - pass "edgecolors='k'". - MGD +2009-01-16 + Ensure three minor ticks always drawn (SF# 2924245). Patch by Neil + Crighton. -ADS -2012-05-22 Added radius kwarg to pie charts. - HH +2010-01-16 + Applied patch by Ian Thomas to fix two contouring problems: now contourf + handles interior masked regions, and the boundaries of line and filled + contours coincide. - EF -2012-05-22 Collections now have a setting "offset_position" to select whether - the offsets are given in "screen" coordinates (default, - following the old behavior) or "data" coordinates. This is currently - used internally to improve the performance of hexbin. +2009-01-11 + The color of legend patch follows the rc parameters axes.facecolor and + axes.edgecolor. -JJL - As a result, the "draw_path_collection" backend methods have grown - a new argument "offset_position". - MGD +2009-01-11 + adjustable of Axes can be "box-forced" which allow sharing axes. -JJL -2012-05-04 Add a new argument to pie charts - startingangle - that - allows one to specify the angle offset for the first wedge - of the chart. - EP +2009-01-11 + Add add_click and pop_click methods in BlockingContourLabeler. -JJL -2012-05-03 symlog scale now obeys the logarithmic base. Previously, it was - completely ignored and always treated as base e. - MGD +2010-01-03 + Added rcParams['axes.color_cycle'] - EF -2012-05-03 Allow linscalex/y keyword to symlog scale that allows the size of - the linear portion relative to the logarithmic portion to be - adjusted. - MGD +2010-01-03 + Added Pierre's qt4 formlayout editor and toolbar button - JDH -2012-04-14 Added new plot style: stackplot. This new feature supports stacked - area plots. - Damon McDougall +2009-12-31 + Add support for using math text as marker symbols (Thanks to tcb) - MGD + +2009-12-31 + Commit a workaround for a regression in PyQt4-4.6.{0,1} - DSD -2012-04-06 When path clipping changes a LINETO to a MOVETO, it also - changes any CLOSEPOLY command to a LINETO to the initial - point. This fixes a problem with pdf and svg where the - CLOSEPOLY would then draw a line to the latest MOVETO - position instead of the intended initial position. - JKS +2009-12-22 + Fix cmap data for gist_earth_r, etc. -JJL -2012-03-27 Add support to ImageGrid for placing colorbars only at - one edge of each column/row. - RMM +2009-12-20 + spines: put spines in data coordinates, add set_bounds() call. -ADS -2012-03-07 Refactor movie writing into useful classes that make use - of pipes to write image data to ffmpeg or mencoder. Also - improve settings for these and the ability to pass custom - options. - RMM +2009-12-18 + Don't limit notch size in boxplot to q1-q3 range, as this is effectively + making the data look better than it is. - ADS -2012-02-29 errorevery keyword added to errorbar to enable errorbar - subsampling. fixes issue #600. +2009-12-18 + mlab.prctile handles even-length data, such that the median is the mean of + the two middle values. - ADS -2012-02-28 Added plot_trisurf to the mplot3d toolkit. This supports plotting - three dimensional surfaces on an irregular grid. - Damon McDougall +2009-12-15 + Add raw-image (unsampled) support for the ps backend. - JJL -2012-01-23 The radius labels in polar plots no longer use a fixed - padding, but use a different alignment depending on the - quadrant they are in. This fixes numerical problems when - (rmax - rmin) gets too small. - MGD +2009-12-14 + Add patch_artist kwarg to boxplot, but keep old default. Convert + boxplot_demo2.py to use the new patch_artist. - ADS -2012-01-08 Add axes.streamplot to plot streamlines of a velocity field. - Adapted from Tom Flannaghan streamplot implementation. -TSY +2009-12-06 + axes_grid: reimplemented AxisArtist with FloatingAxes support. Added new + examples. - JJL -2011-12-29 ps and pdf markers are now stroked only if the line width - is nonzero for consistency with agg, fixes issue #621. - JKS +2009-12-01 + Applied Laurent Dufrechou's patch to improve blitting with the qt4 backend + - DSD -2011-12-27 Work around an EINTR bug in some versions of subprocess. - JKS +2009-11-13 + The pdf backend now allows changing the contents of a pdf file's + information dictionary via PdfPages.infodict. - JKS -2011-10-25 added support for \operatorname to mathtext, - including the ability to insert spaces, such as - $\operatorname{arg\,max}$ - PI +2009-11-12 + font_manager.py should no longer cause EINTR on Python 2.6 (but will on the + 2.5 version of subprocess). Also the fc-list command in that file was fixed + so now it should actually find the list of fontconfig fonts. - JKS -2011-08-18 Change api of Axes.get_tightbbox and add an optional - keyword parameter *call_axes_locator*. - JJL +2009-11-10 + Single images, and all images in renderers with option_image_nocomposite + (i.e. agg, macosx and the svg backend when rcParams['svg.image_noscale'] is + True), are now drawn respecting the zorder relative to other artists. (Note + that there may now be inconsistencies across backends when more than one + image is drawn at varying zorders, but this change introduces correct + behavior for the backends in which it's easy to do so.) -2011-07-29 A new rcParam "axes.formatter.use_locale" was added, that, - when True, will use the current locale to format tick - labels. This means that, for example, in the fr_FR locale, - ',' will be used as a decimal separator. - MGD +2009-10-21 + Make AutoDateLocator more configurable by adding options to control the + maximum and minimum number of ticks. Also add control of the intervals to + be used for ticking. This does not change behavior but opens previously + hard-coded behavior to runtime modification`. - RMM -2011-07-15 The set of markers available in the plot() and scatter() - commands has been unified. In general, this gives more - options to both than were previously available, however, - there is one backward-incompatible change to the markers in - scatter: +2009-10-19 + Add "path_effects" support for Text and Patch. See + examples/pylab_examples/patheffect_demo.py -JJL - "d" used to mean "diamond", it now means "narrow - diamond". "D" can be used for a "diamond". +2009-10-19 + Add "use_clabeltext" option to clabel. If True, clabels will be created + with ClabelText class, which recalculates rotation angle of the label + during the drawing time. -JJL - -MGD +2009-10-16 + Make AutoDateFormatter actually use any specified timezone setting.This was + only working correctly when no timezone was specified. - RMM -2011-07-13 Fix numerical problems in symlog scale, particularly when - linthresh <= 1.0. Symlog plots may look different if one - was depending on the old broken behavior - MGD +2009-09-27 + Beginnings of a capability to test the pdf backend. - JKS -2011-07-10 Fixed argument handling error in tripcolor/triplot/tricontour, - issue #203. - IMT - -2011-07-08 Many functions added to mplot3d.axes3d to bring Axes3D - objects more feature-parity with regular Axes objects. - Significant revisions to the documentation as well. - - BVR - -2011-07-07 Added compatibility with IPython strategy for picking - a version of Qt4 support, and an rcParam for making - the choice explicitly: backend.qt4. - EF - -2011-07-07 Modified AutoMinorLocator to improve automatic choice of - the number of minor intervals per major interval, and - to allow one to specify this number via a kwarg. - EF - -2011-06-28 3D versions of scatter, plot, plot_wireframe, plot_surface, - bar3d, and some other functions now support empty inputs. - BVR - -2011-06-22 Add set_theta_offset, set_theta_direction and - set_theta_zero_location to polar axes to control the - location of 0 and directionality of theta. - MGD - -2011-06-22 Add axes.labelweight parameter to set font weight to axis - labels - MGD. - -2011-06-20 Add pause function to pyplot. - EF - -2011-06-16 Added *bottom* keyword parameter for the stem command. - Also, implemented a legend handler for the stem plot. - - JJL - -2011-06-16 Added legend.frameon rcParams. - Mike Kaufman - -2011-05-31 Made backend_qt4 compatible with PySide . - Gerald Storer - -2011-04-17 Disable keyboard auto-repeat in qt4 backend by ignoring - key events resulting from auto-repeat. This makes - constrained zoom/pan work. - EF - -2011-04-14 interpolation="nearest" always interpolate images. A new - mode "none" is introduced for no interpolation - JJL - -2011-04-03 Fixed broken pick interface to AsteriskCollection objects - used by scatter. - EF - -2011-04-01 The plot directive Sphinx extension now supports all of the - features in the Numpy fork of that extension. These - include doctest formatting, an 'include-source' option, and - a number of new configuration options. - MGD - -2011-03-29 Wrapped ViewVCCachedServer definition in a factory function. - This class now inherits from urllib2.HTTPSHandler in order - to fetch data from github, but HTTPSHandler is not defined - if python was built without SSL support. - DSD - -2011-03-10 Update pytz version to 2011c, thanks to Simon Cross. - JKS - -2011-03-06 Add standalone tests.py test runner script. - JKS - -2011-03-06 Set edgecolor to 'face' for scatter asterisk-type - symbols; this fixes a bug in which these symbols were - not responding to the c kwarg. The symbols have no - face area, so only the edgecolor is visible. - EF - -2011-02-27 Support libpng version 1.5.x; suggestion by Michael - Albert. Changed installation specification to a - minimum of libpng version 1.2. - EF - -2011-02-20 clabel accepts a callable as an fmt kwarg; modified - patch by Daniel Hyams. - EF - -2011-02-18 scatter([], []) is now valid. Also fixed issues - with empty collections - BVR - -2011-02-07 Quick workaround for dviread bug #3175113 - JKS - -2011-02-05 Add cbook memory monitoring for Windows, using - tasklist. - EF - -2011-02-05 Speed up Normalize and LogNorm by using in-place - operations and by using float32 for float32 inputs - and for ints of 2 bytes or shorter; based on - patch by Christoph Gohlke. - EF - -2011-02-04 Changed imshow to use rgba as uint8 from start to - finish, instead of going through an intermediate - step as double precision; thanks to Christoph Gohlke. - EF - -2011-01-13 Added zdir and offset arguments to contourf3d to - bring contourf3d in feature parity with contour3d. - BVR - -2011-01-04 Tag 1.0.1 for release at r8896 - -2011-01-03 Added display of ticker offset to 3d plots. - BVR - -2011-01-03 Turn off tick labeling on interior subplots for - pyplots.subplots when sharex/sharey is True. - JDH - -2010-12-29 Implement axes_divider.HBox and VBox. -JJL - - -2010-11-22 Fixed error with Hammer projection. - BVR - -2010-11-12 Fixed the placement and angle of axis labels in 3D plots. - BVR - -2010-11-07 New rc parameters examples.download and examples.directory - allow bypassing the download mechanism in get_sample_data. - - JKS - -2010-10-04 Fix JPEG saving bug: only accept the kwargs documented - by PIL for JPEG files. - JKS - -2010-09-15 Remove unused _wxagg extension and numerix.h. - EF - -2010-08-25 Add new framework for doing animations with examples.- RM - -2010-08-21 Remove unused and inappropriate methods from Tick classes: - set_view_interval, get_minpos, and get_data_interval are - properly found in the Axis class and don't need to be - duplicated in XTick and YTick. - EF - -2010-08-21 Change Axis.set_view_interval() so that when updating an - existing interval, it respects the orientation of that - interval, and can enlarge but not reduce the interval. - This fixes a bug in which Axis.set_ticks would - change the view limits of an inverted axis. Whether - set_ticks should be affecting the viewLim at all remains - an open question. - EF - -2010-08-16 Handle NaN's correctly in path analysis routines. Fixes a - bug where the best location for a legend was not calculated - correctly when the line contains NaNs. - MGD - -2010-08-14 Fix bug in patch alpha handling, and in bar color kwarg - EF - -2010-08-12 Removed all traces of numerix module after 17 months of - deprecation warnings. - EF - -2010-08-05 Added keyword arguments 'thetaunits' and 'runits' for polar - plots. Fixed PolarAxes so that when it set default - Formatters, it marked them as such. Fixed semilogx and - semilogy to no longer blindly reset the ticker information - on the non-log axis. Axes.arrow can now accept unitized - data. - JRE - -2010-08-03 Add support for MPLSETUPCFG variable for custom setup.cfg - filename. Used by sage buildbot to build an mpl w/ no gui - support - JDH - -2010-08-01 Create directory specified by MPLCONFIGDIR if it does - not exist. - ADS - -2010-07-20 Return Qt4's default cursor when leaving the canvas - DSD - -2010-07-06 Tagging for mpl 1.0 at r8502 - - -2010-07-05 Added Ben Root's patch to put 3D plots in arbitrary axes, - allowing you to mix 3d and 2d in different axes/subplots or - to have multiple 3D plots in one figure. See - examples/mplot3d/subplot3d_demo.py - JDH - -2010-07-05 Preferred kwarg names in set_xlim are now 'left' and - 'right'; in set_ylim, 'bottom' and 'top'; original - kwargs are still accepted without complaint. - EF - -2010-07-05 TkAgg and FltkAgg backends are now consistent with other - interactive backends: when used in scripts from the - command line (not from ipython -pylab), show blocks, - and can be called more than once. - EF - -2010-07-02 Modified CXX/WrapPython.h to fix "swab bug" on solaris so - mpl can compile on Solaris with CXX6 in the trunk. Closes - tracker bug 3022815 - JDH - -2010-06-30 Added autoscale convenience method and corresponding - pyplot function for simplified control of autoscaling; - and changed axis, set_xlim, and set_ylim so that by - default, they turn off the autoscaling on the relevant - axis or axes. Therefore one can call set_xlim before - plotting a line, for example, and the limits will be - retained. - EF - -2010-06-20 Added Axes.tick_params and corresponding pyplot function - to control tick and tick label appearance after an Axes - has been created. - EF - -2010-06-09 Allow Axes.grid to control minor gridlines; allow - Axes.grid and Axis.grid to control major and minor - gridlines in the same method call. - EF - -2010-06-06 Change the way we do split/dividend adjustments in - finance.py to handle dividends and fix the zero division bug reported - in sf bug 2949906 and 2123566. Note that volume is not adjusted - because the Yahoo CSV does not distinguish between share - split and dividend adjustments making it near impossible to - get volume adjustment right (unless we want to guess based - on the size of the adjustment or scrape the html tables, - which we don't) - JDH - -2010-06-06 Updated dateutil to 1.5 and pytz to 2010h. - -2010-06-02 Add error_kw kwarg to Axes.bar(). - EF - -2010-06-01 Fix pcolormesh() and QuadMesh to pass on kwargs as - appropriate. - RM - -2010-05-18 Merge mpl_toolkits.gridspec into the main tree. - JJL - -2010-05-04 Improve backend_qt4 so it displays figures with the - correct size - DSD - -2010-04-20 Added generic support for connecting to a timer for events. This - adds TimerBase, TimerGTK, TimerQT, TimerWx, and TimerTk to - the backends and a new_timer() method to each backend's - canvas to allow ease of creating a new timer. - RM - -2010-04-20 Added margins() Axes method and pyplot function. - EF - -2010-04-18 update the axes_grid documentation. -JJL - -2010-04-18 Control MaxNLocator parameters after instantiation, - and via Axes.locator_params method, with corresponding - pyplot function. -EF - -2010-04-18 Control ScalarFormatter offsets directly and via the - Axes.ticklabel_format() method, and add that to pyplot. -EF - -2010-04-16 Add a close_event to the backends. -RM - -2010-04-06 modify axes_grid examples to use axes_grid1 and axisartist. -JJL - -2010-04-06 rebase axes_grid using axes_grid1 and axisartist modules. -JJL - -2010-04-06 axes_grid toolkit is split into two separate modules, - axes_grid1 and axisartist. -JJL - -2010-04-05 Speed up import: import pytz only if and when it is - needed. It is not needed if the rc timezone is UTC. - EF - -2010-04-03 Added color kwarg to Axes.hist(), based on work by - Jeff Klukas. - EF - -2010-03-24 refactor colorbar code so that no cla() is necessary when - mappable is changed. -JJL - -2010-03-22 fix incorrect rubber band during the zoom mode when mouse - leaves the axes. -JJL - -2010-03-21 x/y key during the zoom mode only changes the x/y limits. -JJL - -2010-03-20 Added pyplot.sca() function suggested by JJL. - EF - -2010-03-20 Added conditional support for new Tooltip API in gtk backend. - EF - -2010-03-20 Changed plt.fig_subplot() to plt.subplots() after discussion on - list, and changed its API to return axes as a numpy object array - (with control of dimensions via squeeze keyword). FP. - -2010-03-13 Manually brought in commits from branch:: - - ------------------------------------------------------------------------ - r8191 | leejjoon | 2010-03-13 17:27:57 -0500 (Sat, 13 Mar 2010) | 1 line - - fix the bug that handles for scatter are incorrectly set when dpi!=72. - Thanks to Ray Speth for the bug report. - - -2010-03-03 Manually brought in commits from branch via diff/patch (svnmerge is broken):: - - ------------------------------------------------------------------------ - r8175 | leejjoon | 2010-03-03 10:03:30 -0800 (Wed, 03 Mar 2010) | 1 line - - fix arguments of allow_rasterization.draw_wrapper - ------------------------------------------------------------------------ - r8174 | jdh2358 | 2010-03-03 09:15:58 -0800 (Wed, 03 Mar 2010) | 1 line - - added support for favicon in docs build - ------------------------------------------------------------------------ - r8173 | jdh2358 | 2010-03-03 08:56:16 -0800 (Wed, 03 Mar 2010) | 1 line - - applied Mattias get_bounds patch - ------------------------------------------------------------------------ - r8172 | jdh2358 | 2010-03-03 08:31:42 -0800 (Wed, 03 Mar 2010) | 1 line - - fix svnmerge download instructions - ------------------------------------------------------------------------ - r8171 | jdh2358 | 2010-03-03 07:47:48 -0800 (Wed, 03 Mar 2010) | 1 line - - - -2010-02-25 add annotation_demo3.py that demonstrates new functionality. -JJL - -2010-02-25 refactor Annotation to support arbitrary Transform as xycoords - or textcoords. Also, if a tuple of two coordinates is provided, - they are interpreted as coordinates for each x and y position. - -JJL - -2010-02-24 Added pyplot.fig_subplot(), to create a figure and a group of - subplots in a single call. This offers an easier pattern than - manually making figures and calling add_subplot() multiple times. FP - -2010-02-17 Added Gokhan's and Mattias' customizable keybindings patch - for the toolbar. You can now set the keymap.* properties - in the matplotlibrc file. Newbindings were added for - toggling log scaling on the x-axis. JDH - -2010-02-16 Committed TJ's filled marker patch for - left|right|bottom|top|full filled markers. See - examples/pylab_examples/filledmarker_demo.py. JDH - -2010-02-11 Added 'bootstrap' option to boxplot. This allows bootstrap - estimates of median confidence intervals. Based on an - initial patch by Paul Hobson. - ADS - -2010-02-06 Added setup.cfg "basedirlist" option to override setting - in setupext.py "basedir" dictionary; added "gnu0" - platform requested by Benjamin Drung. - EF - -2010-02-06 Added 'xy' scaling option to EllipseCollection. - EF - -2010-02-03 Made plot_directive use a custom PlotWarning category, so that - warnings can be turned into fatal errors easily if desired. - FP - -2010-01-29 Added draggable method to Legend to allow mouse drag - placement. Thanks Adam Fraser. JDH - -2010-01-25 Fixed a bug reported by Olle Engdegard, when using - histograms with stepfilled and log=True - MM - -2010-01-16 Upgraded CXX to 6.1.1 - JDH - -2009-01-16 Don't create minor ticks on top of existing major - ticks. Patch by Neil Crighton. -ADS - -2009-01-16 Ensure three minor ticks always drawn (SF# 2924245). Patch - by Neil Crighton. -ADS - -2010-01-16 Applied patch by Ian Thomas to fix two contouring - problems: now contourf handles interior masked regions, - and the boundaries of line and filled contours coincide. - EF - -2009-01-11 The color of legend patch follows the rc parameters - axes.facecolor and axes.edgecolor. -JJL - -2009-01-11 adjustable of Axes can be "box-forced" which allow - sharing axes. -JJL - -2009-01-11 Add add_click and pop_click methods in - BlockingContourLabeler. -JJL - - -2010-01-03 Added rcParams['axes.color_cycle'] - EF - -2010-01-03 Added Pierre's qt4 formlayout editor and toolbar button - JDH - -2009-12-31 Add support for using math text as marker symbols (Thanks to tcb) - - MGD - -2009-12-31 Commit a workaround for a regression in PyQt4-4.6.{0,1} - DSD - -2009-12-22 Fix cmap data for gist_earth_r, etc. -JJL - -2009-12-20 spines: put spines in data coordinates, add set_bounds() - call. -ADS - -2009-12-18 Don't limit notch size in boxplot to q1-q3 range, as this - is effectively making the data look better than it is. - ADS - -2009-12-18 mlab.prctile handles even-length data, such that the median - is the mean of the two middle values. - ADS - -2009-12-15 Add raw-image (unsampled) support for the ps backend. - JJL - -2009-12-14 Add patch_artist kwarg to boxplot, but keep old default. - Convert boxplot_demo2.py to use the new patch_artist. - ADS - -2009-12-06 axes_grid: reimplemented AxisArtist with FloatingAxes support. - Added new examples. - JJL - -2009-12-01 Applied Laurent Dufrechou's patch to improve blitting with - the qt4 backend - DSD - -2009-11-13 The pdf backend now allows changing the contents of - a pdf file's information dictionary via PdfPages.infodict. - JKS - -2009-11-12 font_manager.py should no longer cause EINTR on Python 2.6 - (but will on the 2.5 version of subprocess). Also the - fc-list command in that file was fixed so now it should - actually find the list of fontconfig fonts. - JKS - -2009-11-10 Single images, and all images in renderers with - option_image_nocomposite (i.e. agg, macosx and the svg - backend when rcParams['svg.image_noscale'] is True), are - now drawn respecting the zorder relative to other - artists. (Note that there may now be inconsistencies across - backends when more than one image is drawn at varying - zorders, but this change introduces correct behavior for - the backends in which it's easy to do so.) - -2009-10-21 Make AutoDateLocator more configurable by adding options - to control the maximum and minimum number of ticks. Also - add control of the intervals to be used for ticking. This - does not change behavior but opens previously hard-coded - behavior to runtime modification`. - RMM - -2009-10-19 Add "path_effects" support for Text and Patch. See - examples/pylab_examples/patheffect_demo.py -JJL - -2009-10-19 Add "use_clabeltext" option to clabel. If True, clabels - will be created with ClabelText class, which recalculates - rotation angle of the label during the drawing time. -JJL - -2009-10-16 Make AutoDateFormatter actually use any specified - timezone setting.This was only working correctly - when no timezone was specified. - RMM - -2009-09-27 Beginnings of a capability to test the pdf backend. - JKS - -2009-09-27 Add a savefig.extension rcparam to control the default - filename extension used by savefig. - JKS +2009-09-27 + Add a savefig.extension rcparam to control the default filename extension + used by savefig. - JKS =============================================== -2009-09-21 Tagged for release 0.99.1 - -2009-09-20 Fix usetex spacing errors in pdf backend. - JKS +2009-09-21 + Tagged for release 0.99.1 -2009-09-20 Add Sphinx extension to highlight IPython console sessions, - originally authored (I think) by Michael Droetboom. - FP +2009-09-20 + Fix usetex spacing errors in pdf backend. - JKS -2009-09-20 Fix off-by-one error in dviread.Tfm, and additionally protect - against exceptions in case a dvi font is missing some metrics. - JKS +2009-09-20 + Add Sphinx extension to highlight IPython console sessions, originally + authored (I think) by Michael Droetboom. - FP -2009-09-15 Implement draw_text and draw_tex method of backend_base using - the textpath module. Implement draw_tex method of the svg - backend. - JJL +2009-09-20 + Fix off-by-one error in dviread.Tfm, and additionally protect against + exceptions in case a dvi font is missing some metrics. - JKS -2009-09-15 Don't fail on AFM files containing floating-point bounding boxes - JKS +2009-09-15 + Implement draw_text and draw_tex method of backend_base using the textpath + module. Implement draw_tex method of the svg backend. - JJL -2009-09-13 AxesGrid : add modified version of colorbar. Add colorbar - location howto. - JJL +2009-09-15 + Don't fail on AFM files containing floating-point bounding boxes - JKS -2009-09-07 AxesGrid : implemented axisline style. - Added a demo examples/axes_grid/demo_axisline_style.py- JJL +2009-09-13 + AxesGrid : add modified version of colorbar. Add colorbar location howto. - + JJL -2009-09-04 Make the textpath class as a separate module - (textpath.py). Add support for mathtext and tex.- JJL +2009-09-07 + AxesGrid : implemented axisline style. Added a demo + examples/axes_grid/demo_axisline_style.py- JJL -2009-09-01 Added support for Gouraud interpolated triangles. - pcolormesh now accepts shading='gouraud' as an option. - MGD +2009-09-04 + Make the textpath class as a separate module (textpath.py). Add support for + mathtext and tex.- JJL -2009-08-29 Added matplotlib.testing package, which contains a Nose - plugin and a decorator that lets tests be marked as - KnownFailures - ADS +2009-09-01 + Added support for Gouraud interpolated triangles. pcolormesh now accepts + shading='gouraud' as an option. - MGD -2009-08-20 Added scaled dict to AutoDateFormatter for customized - scales - JDH +2009-08-29 + Added matplotlib.testing package, which contains a Nose plugin and a + decorator that lets tests be marked as KnownFailures - ADS -2009-08-15 Pyplot interface: the current image is now tracked at the - figure and axes level, addressing tracker item 1656374. - EF +2009-08-20 + Added scaled dict to AutoDateFormatter for customized scales - JDH -2009-08-15 Docstrings are now manipulated with decorators defined - in a new module, docstring.py, thanks to Jason Coombs. - EF +2009-08-15 + Pyplot interface: the current image is now tracked at the figure and axes + level, addressing tracker item 1656374. - EF -2009-08-14 Add support for image filtering for agg back end. See the example - demo_agg_filter.py. -JJL +2009-08-15 + Docstrings are now manipulated with decorators defined in a new module, + docstring.py, thanks to Jason Coombs. - EF -2009-08-09 AnnotationBbox added. Similar to Annotation, but works with - OffsetBox instead of Text. See the example - demo_annotation_box.py. -JJL +2009-08-14 + Add support for image filtering for agg back end. See the example + demo_agg_filter.py. -JJL -2009-08-07 BboxImage implemented. Two examples, demo_bboximage.py and - demo_ribbon_box.py added. - JJL +2009-08-09 + AnnotationBbox added. Similar to Annotation, but works with OffsetBox + instead of Text. See the example demo_annotation_box.py. -JJL -2009-08-07 In an effort to simplify the backend API, all clipping rectangles - and paths are now passed in using GraphicsContext objects, even - on collections and images. Therefore: +2009-08-07 + BboxImage implemented. Two examples, demo_bboximage.py and + demo_ribbon_box.py added. - JJL - draw_path_collection(self, master_transform, cliprect, clippath, - clippath_trans, paths, all_transforms, offsets, - offsetTrans, facecolors, edgecolors, linewidths, - linestyles, antialiaseds, urls) +2009-08-07 + In an effort to simplify the backend API, all clipping rectangles and paths + are now passed in using GraphicsContext objects, even on collections and + images. Therefore:: - becomes: + draw_path_collection(self, master_transform, cliprect, clippath, + clippath_trans, paths, all_transforms, offsets, + offsetTrans, facecolors, edgecolors, linewidths, + linestyles, antialiaseds, urls) - draw_path_collection(self, gc, master_transform, paths, all_transforms, - offsets, offsetTrans, facecolors, edgecolors, - linewidths, linestyles, antialiaseds, urls) + becomes:: + draw_path_collection(self, gc, master_transform, paths, all_transforms, + offsets, offsetTrans, facecolors, edgecolors, + linewidths, linestyles, antialiaseds, urls) + :: - draw_quad_mesh(self, master_transform, cliprect, clippath, - clippath_trans, meshWidth, meshHeight, coordinates, - offsets, offsetTrans, facecolors, antialiased, - showedges) + draw_quad_mesh(self, master_transform, cliprect, clippath, + clippath_trans, meshWidth, meshHeight, coordinates, + offsets, offsetTrans, facecolors, antialiased, + showedges) - becomes: + becomes:: - draw_quad_mesh(self, gc, master_transform, meshWidth, meshHeight, - coordinates, offsets, offsetTrans, facecolors, - antialiased, showedges) + draw_quad_mesh(self, gc, master_transform, meshWidth, meshHeight, + coordinates, offsets, offsetTrans, facecolors, + antialiased, showedges) + :: + draw_image(self, x, y, im, bbox, clippath=None, clippath_trans=None) - draw_image(self, x, y, im, bbox, clippath=None, clippath_trans=None) + becomes:: - becomes: + draw_image(self, gc, x, y, im) - draw_image(self, gc, x, y, im) + - MGD - - MGD +2009-08-06 + Tagging the 0.99.0 release at svn r7397 - JDH -2009-08-06 Tagging the 0.99.0 release at svn r7397 - JDH + * fixed an alpha colormapping bug posted on sf 2832575 - * fixed an alpha colormapping bug posted on sf 2832575 + * fix typo in axes_divider.py. use nanmin, nanmax in angle_helper.py (patch + by Christoph Gohlke) - * fix typo in axes_divider.py. use nanmin, nanmax in angle_helper.py - (patch by Christoph Gohlke) + * remove dup gui event in enter/leave events in gtk - * remove dup gui event in enter/leave events in gtk + * lots of fixes for os x binaries (Thanks Russell Owen) - * lots of fixes for os x binaries (Thanks Russell Owen) + * attach gtk events to mpl events -- fixes sf bug 2816580 - * attach gtk events to mpl events -- fixes sf bug 2816580 + * applied sf patch 2815064 (middle button events for wx) and patch 2818092 + (resize events for wx) - * applied sf patch 2815064 (middle button events for wx) and - patch 2818092 (resize events for wx) + * fixed boilerplate.py so it doesn't break the ReST docs. - * fixed boilerplate.py so it doesn't break the ReST docs. + * removed a couple of cases of mlab.load - * removed a couple of cases of mlab.load + * fixed rec2csv win32 file handle bug from sf patch 2831018 - * fixed rec2csv win32 file handle bug from sf patch 2831018 + * added two examples from Josh Hemann: + examples/pylab_examples/barchart_demo2.py and + examples/pylab_examples/boxplot_demo2.py - * added two examples from Josh Hemann: examples/pylab_examples/barchart_demo2.py - and examples/pylab_examples/boxplot_demo2.py + * handled sf bugs 2831556 and 2830525; better bar error messages and + backend driver configs - * handled sf bugs 2831556 and 2830525; better bar error messages and - backend driver configs + * added miktex win32 patch from sf patch 2820194 - * added miktex win32 patch from sf patch 2820194 + * apply sf patches 2830233 and 2823885 for osx setup and 64 bit; thanks + Michiel - * apply sf patches 2830233 and 2823885 for osx setup and 64 bit; thanks Michiel +2009-08-04 + Made cbook.get_sample_data make use of the ETag and Last-Modified headers + of mod_dav_svn. - JKS -2009-08-04 Made cbook.get_sample_data make use of the ETag and Last-Modified - headers of mod_dav_svn. - JKS +2009-08-03 + Add PathCollection; modify contourf to use complex paths instead of simple + paths with cuts. - EF -2009-08-03 Add PathCollection; modify contourf to use complex - paths instead of simple paths with cuts. - EF +2009-08-03 + Fixed boilerplate.py so it doesn't break the ReST docs. - JKS +2009-08-03 + pylab no longer provides a load and save function. These are available in + matplotlib.mlab, or you can use numpy.loadtxt and numpy.savetxt for text + files, or np.save and np.load for binary numpy arrays. - JDH -2009-08-03 Fixed boilerplate.py so it doesn't break the ReST docs. - JKS +2009-07-31 + Added cbook.get_sample_data for urllib enabled fetching and caching of data + needed for examples. See examples/misc/sample_data_demo.py - JDH -2009-08-03 pylab no longer provides a load and save function. These - are available in matplotlib.mlab, or you can use - numpy.loadtxt and numpy.savetxt for text files, or np.save - and np.load for binary numpy arrays. - JDH +2009-07-31 + Tagging 0.99.0.rc1 at 7314 - MGD -2009-07-31 Added cbook.get_sample_data for urllib enabled fetching and - caching of data needed for examples. See - examples/misc/sample_data_demo.py - JDH +2009-07-30 + Add set_cmap and register_cmap, and improve get_cmap, to provide convenient + handling of user-generated colormaps. Reorganized _cm and cm modules. - EF -2009-07-31 Tagging 0.99.0.rc1 at 7314 - MGD +2009-07-28 + Quiver speed improved, thanks to tip by Ray Speth. -EF -2009-07-30 Add set_cmap and register_cmap, and improve get_cmap, - to provide convenient handling of user-generated - colormaps. Reorganized _cm and cm modules. - EF +2009-07-27 + Simplify argument handling code for plot method. -EF -2009-07-28 Quiver speed improved, thanks to tip by Ray Speth. -EF +2009-07-25 + Allow "plot(1, 2, 'r*')" to work. - EF -2009-07-27 Simplify argument handling code for plot method. -EF +2009-07-22 + Added an 'interp' keyword to griddata so the faster linear interpolation + method can be chosen. Default is 'nn', so default behavior (using natural + neighbor method) is unchanged (JSW) -2009-07-25 Allow "plot(1, 2, 'r*')" to work. - EF +2009-07-22 + Improved boilerplate.py so that it generates the correct signatures for + pyplot functions. - JKS -2009-07-22 Added an 'interp' keyword to griddata so the faster linear - interpolation method can be chosen. Default is 'nn', so - default behavior (using natural neighbor method) is unchanged (JSW) +2009-07-19 + Fixed the docstring of Axes.step to reflect the correct meaning of the + kwargs "pre" and "post" - See SF bug + \https://sourceforge.net/tracker/index.php?func=detail&aid=2823304&group_id=80706&atid=560720 + - JDH -2009-07-22 Improved boilerplate.py so that it generates the correct - signatures for pyplot functions. - JKS +2009-07-18 + Fix support for hatches without color fills to pdf and svg backends. Add an + example of that to hatch_demo.py. - JKS -2009-07-19 Fixed the docstring of Axes.step to reflect the correct - meaning of the kwargs "pre" and "post" - See SF bug - \https://sourceforge.net/tracker/index.php?func=detail&aid=2823304&group_id=80706&atid=560720 - - JDH +2009-07-17 + Removed fossils from swig version of agg backend. - EF -2009-07-18 Fix support for hatches without color fills to pdf and svg - backends. Add an example of that to hatch_demo.py. - JKS +2009-07-14 + initial submission of the annotation guide. -JJL -2009-07-17 Removed fossils from swig version of agg backend. - EF +2009-07-14 + axes_grid : minor improvements in anchored_artists and inset_locator. -JJL -2009-07-14 initial submission of the annotation guide. -JJL +2009-07-14 + Fix a few bugs in ConnectionStyle algorithms. Add ConnectionPatch class. + -JJL -2009-07-14 axes_grid : minor improvements in anchored_artists and - inset_locator. -JJL +2009-07-11 + Added a fillstyle Line2D property for half filled markers -- see + examples/pylab_examples/fillstyle_demo.py JDH -2009-07-14 Fix a few bugs in ConnectionStyle algorithms. Add - ConnectionPatch class. -JJL +2009-07-08 + Attempt to improve performance of qt4 backend, do not call + qApp.processEvents while processing an event. Thanks Ole Streicher for + tracking this down - DSD -2009-07-11 Added a fillstyle Line2D property for half filled markers - -- see examples/pylab_examples/fillstyle_demo.py JDH +2009-06-24 + Add withheader option to mlab.rec2csv and changed use_mrecords default to + False in mlab.csv2rec since this is partially broken - JDH -2009-07-08 Attempt to improve performance of qt4 backend, do not call - qApp.processEvents while processing an event. Thanks Ole - Streicher for tracking this down - DSD +2009-06-24 + backend_agg.draw_marker quantizes the main path (as in the draw_path). - + JJL -2009-06-24 Add withheader option to mlab.rec2csv and changed - use_mrecords default to False in mlab.csv2rec since this is - partially broken - JDH +2009-06-24 + axes_grid: floating axis support added. - JJL -2009-06-24 backend_agg.draw_marker quantizes the main path (as in the - draw_path). - JJL +2009-06-14 + Add new command line options to backend_driver.py to support running only + some directories of tests - JKS -2009-06-24 axes_grid: floating axis support added. - JJL +2009-06-13 + partial cleanup of mlab and its importation in pylab - EF -2009-06-14 Add new command line options to backend_driver.py to support - running only some directories of tests - JKS +2009-06-13 + Introduce a rotation_mode property for the Text artist. See + examples/pylab_examples/demo_text_rotation_mode.py -JJL -2009-06-13 partial cleanup of mlab and its importation in pylab - EF +2009-06-07 + add support for bz2 files per sf support request 2794556 - JDH -2009-06-13 Introduce a rotation_mode property for the Text artist. See - examples/pylab_examples/demo_text_rotation_mode.py -JJL +2009-06-06 + added a properties method to the artist and inspector to return a dict + mapping property name -> value; see sf feature request 2792183 - JDH -2009-06-07 add support for bz2 files per sf support request 2794556 - - JDH +2009-06-06 + added Neil's auto minor tick patch; sf patch #2789713 - JDH -2009-06-06 added a properties method to the artist and inspector to - return a dict mapping property name -> value; see sf - feature request 2792183 - JDH +2009-06-06 + do not apply alpha to rgba color conversion if input is already rgba - JDH -2009-06-06 added Neil's auto minor tick patch; sf patch #2789713 - JDH +2009-06-03 + axes_grid : Initial check-in of curvelinear grid support. See + examples/axes_grid/demo_curvelinear_grid.py - JJL -2009-06-06 do not apply alpha to rgba color conversion if input is - already rgba - JDH +2009-06-01 + Add set_color method to Patch - EF -2009-06-03 axes_grid : Initial check-in of curvelinear grid support. See - examples/axes_grid/demo_curvelinear_grid.py - JJL +2009-06-01 + Spine is now derived from Patch - ADS -2009-06-01 Add set_color method to Patch - EF +2009-06-01 + use cbook.is_string_like() instead of isinstance() for spines - ADS -2009-06-01 Spine is now derived from Patch - ADS +2009-06-01 + cla() support for spines - ADS -2009-06-01 use cbook.is_string_like() instead of isinstance() for spines - ADS +2009-06-01 + Removed support for gtk < 2.4. - EF -2009-06-01 cla() support for spines - ADS +2009-05-29 + Improved the animation_blit_qt4 example, which was a mix of the + object-oriented and pylab interfaces. It is now strictly object-oriented - + DSD -2009-06-01 Removed support for gtk < 2.4. - EF +2009-05-28 + Fix axes_grid toolkit to work with spine patch by ADS. - JJL -2009-05-29 Improved the animation_blit_qt4 example, which was a mix - of the object-oriented and pylab interfaces. It is now - strictly object-oriented - DSD +2009-05-28 + Applied fbianco's patch to handle scroll wheel events in the qt4 backend - + DSD -2009-05-28 Fix axes_grid toolkit to work with spine patch by ADS. - JJL +2009-05-26 + Add support for "axis spines" to have arbitrary location. -ADS -2009-05-28 Applied fbianco's patch to handle scroll wheel events in - the qt4 backend - DSD +2009-05-20 + Add an empty matplotlibrc to the tests/ directory so that running tests + will use the default set of rcparams rather than the user's config. - RMM -2009-05-26 Add support for "axis spines" to have arbitrary location. -ADS +2009-05-19 + Axis.grid(): allow use of which='major,minor' to have grid on major and + minor ticks. -ADS -2009-05-20 Add an empty matplotlibrc to the tests/ directory so that running - tests will use the default set of rcparams rather than the user's - config. - RMM +2009-05-18 + Make psd(), csd(), and cohere() wrap properly for complex/two-sided + versions, like specgram() (SF #2791686) - RMM -2009-05-19 Axis.grid(): allow use of which='major,minor' to have grid - on major and minor ticks. -ADS +2009-05-18 + Fix the linespacing bug of multiline text (#1239682). See + examples/pylab_examples/multiline.py -JJL -2009-05-18 Make psd(), csd(), and cohere() wrap properly for complex/two-sided - versions, like specgram() (SF #2791686) - RMM +2009-05-18 + Add *annotation_clip* attr. for text.Annotation class. If True, annotation + is only drawn when the annotated point is inside the axes area. -JJL -2009-05-18 Fix the linespacing bug of multiline text (#1239682). See - examples/pylab_examples/multiline.py -JJL +2009-05-17 + Fix bug(#2749174) that some properties of minor ticks are not conserved + -JJL -2009-05-18 Add *annotation_clip* attr. for text.Annotation class. - If True, annotation is only drawn when the annotated point is - inside the axes area. -JJL +2009-05-17 + applied Michiel's sf patch 2790638 to turn off gtk event loop in setupext + for pygtk>=2.15.10 - JDH -2009-05-17 Fix bug(#2749174) that some properties of minor ticks are - not conserved -JJL - -2009-05-17 applied Michiel's sf patch 2790638 to turn off gtk event - loop in setupext for pygtk>=2.15.10 - JDH - -2009-05-17 applied Michiel's sf patch 2792742 to speed up Cairo and - macosx collections; speedups can be 20x. Also fixes some - bugs in which gc got into inconsistent state +2009-05-17 + applied Michiel's sf patch 2792742 to speed up Cairo and macosx + collections; speedups can be 20x. Also fixes some bugs in which gc got + into inconsistent state ----------------------- -2008-05-17 Release 0.98.5.3 at r7107 from the branch - JDH - -2009-05-13 An optional offset and bbox support in restore_bbox. - Add animation_blit_gtk2.py. -JJL +2008-05-17 + Release 0.98.5.3 at r7107 from the branch - JDH -2009-05-13 psfrag in backend_ps now uses baseline-alignment - when preview.sty is used ((default is - bottom-alignment). Also, a small API improvement - in OffsetBox-JJL +2009-05-13 + An optional offset and bbox support in restore_bbox. Add + animation_blit_gtk2.py. -JJL -2009-05-13 When the x-coordinate of a line is monotonically - increasing, it is now automatically clipped at - the stage of generating the transformed path in - the draw method; this greatly speeds up zooming and - panning when one is looking at a short segment of - a long time series, for example. - EF +2009-05-13 + psfrag in backend_ps now uses baseline-alignment when preview.sty is used + ((default is bottom-alignment). Also, a small API improvement in + OffsetBox-JJL -2009-05-11 aspect=1 in log-log plot gives square decades. -JJL +2009-05-13 + When the x-coordinate of a line is monotonically increasing, it is now + automatically clipped at the stage of generating the transformed path in + the draw method; this greatly speeds up zooming and panning when one is + looking at a short segment of a long time series, for example. - EF -2009-05-08 clabel takes new kwarg, rightside_up; if False, labels - will not be flipped to keep them rightside-up. This - allows the use of clabel to make streamfunction arrows, - as requested by Evan Mason. - EF +2009-05-11 + aspect=1 in log-log plot gives square decades. -JJL -2009-05-07 'labelpad' can now be passed when setting x/y labels. This - allows controlling the spacing between the label and its - axis. - RMM +2009-05-08 + clabel takes new kwarg, rightside_up; if False, labels will not be flipped + to keep them rightside-up. This allows the use of clabel to make + streamfunction arrows, as requested by Evan Mason. - EF -2009-05-06 print_ps now uses mixed-mode renderer. Axes.draw rasterize - artists whose zorder smaller than rasterization_zorder. - -JJL +2009-05-07 + 'labelpad' can now be passed when setting x/y labels. This allows + controlling the spacing between the label and its axis. - RMM -2009-05-06 Per-artist Rasterization, originally by Eric Bruning. -JJ +2009-05-06 + print_ps now uses mixed-mode renderer. Axes.draw rasterize artists whose + zorder smaller than rasterization_zorder. -JJL -2009-05-05 Add an example that shows how to make a plot that updates - using data from another process. Thanks to Robert - Cimrman - RMM +2009-05-06 + Per-artist Rasterization, originally by Eric Bruning. -JJ -2009-05-05 Add Axes.get_legend_handles_labels method. - JJL +2009-05-05 + Add an example that shows how to make a plot that updates using data from + another process. Thanks to Robert Cimrman - RMM -2009-05-04 Fix bug that Text.Annotation is still drawn while set to - not visible. - JJL +2009-05-05 + Add Axes.get_legend_handles_labels method. - JJL -2009-05-04 Added TJ's fill_betweenx patch - JDH +2009-05-04 + Fix bug that Text.Annotation is still drawn while set to not visible. - JJL -2009-05-02 Added options to plotfile based on question from - Joseph Smidt and patch by Matthias Michler. - EF +2009-05-04 + Added TJ's fill_betweenx patch - JDH +2009-05-02 + Added options to plotfile based on question from Joseph Smidt and patch by + Matthias Michler. - EF -2009-05-01 Changed add_artist and similar Axes methods to - return their argument. - EF +2009-05-01 + Changed add_artist and similar Axes methods to return their argument. - EF -2009-04-30 Incorrect eps bbox for landscape mode fixed - JJL +2009-04-30 + Incorrect eps bbox for landscape mode fixed - JJL -2009-04-28 Fixed incorrect bbox of eps output when usetex=True. - JJL +2009-04-28 + Fixed incorrect bbox of eps output when usetex=True. - JJL -2009-04-24 Changed use of os.open* to instead use subprocess.Popen. - os.popen* are deprecated in 2.6 and are removed in 3.0. - RMM +2009-04-24 + Changed use of os.open* to instead use subprocess.Popen. os.popen* are + deprecated in 2.6 and are removed in 3.0. - RMM -2009-04-20 Worked on axes_grid documentation. Added - axes_grid.inset_locator. - JJL +2009-04-20 + Worked on axes_grid documentation. Added axes_grid.inset_locator. - JJL -2009-04-17 Initial check-in of the axes_grid toolkit. - JJL +2009-04-17 + Initial check-in of the axes_grid toolkit. - JJL -2009-04-17 Added a support for bbox_to_anchor in - offsetbox.AnchoredOffsetbox. Improved a documentation. - - JJL +2009-04-17 + Added a support for bbox_to_anchor in offsetbox.AnchoredOffsetbox. Improved + a documentation. - JJL -2009-04-16 Fixed a offsetbox bug that multiline texts are not - correctly aligned. - JJL +2009-04-16 + Fixed a offsetbox bug that multiline texts are not correctly aligned. - + JJL -2009-04-16 Fixed a bug in mixed mode renderer that images produced by - an rasterizing backend are placed with incorrect size. - - JJL +2009-04-16 + Fixed a bug in mixed mode renderer that images produced by an rasterizing + backend are placed with incorrect size. - JJL -2009-04-14 Added Jonathan Taylor's Reinier Heeres' port of John - Porters' mplot3d to svn trunk. Package in - mpl_toolkits.mplot3d and demo is examples/mplot3d/demo.py. - Thanks Reiner +2009-04-14 + Added Jonathan Taylor's Reinier Heeres' port of John Porters' mplot3d to + svn trunk. Package in mpl_toolkits.mplot3d and demo is + examples/mplot3d/demo.py. Thanks Reiner -2009-04-06 The pdf backend now escapes newlines and linefeeds in strings. - Fixes sf bug #2708559; thanks to Tiago Pereira for the report. +2009-04-06 + The pdf backend now escapes newlines and linefeeds in strings. Fixes sf + bug #2708559; thanks to Tiago Pereira for the report. -2009-04-06 texmanager.make_dvi now raises an error if LaTeX failed to - create an output file. Thanks to Joao Luis Silva for reporting - this. - JKS +2009-04-06 + texmanager.make_dvi now raises an error if LaTeX failed to create an output + file. Thanks to Joao Luis Silva for reporting this. - JKS -2009-04-05 _png.read_png() reads 12 bit PNGs (patch from - Tobias Wood) - ADS +2009-04-05 + _png.read_png() reads 12 bit PNGs (patch from Tobias Wood) - ADS -2009-04-04 Allow log axis scale to clip non-positive values to - small positive value; this is useful for errorbars. - EF +2009-04-04 + Allow log axis scale to clip non-positive values to small positive value; + this is useful for errorbars. - EF -2009-03-28 Make images handle nan in their array argument. - A helper, cbook.safe_masked_invalid() was added. - EF +2009-03-28 + Make images handle nan in their array argument. A helper, + cbook.safe_masked_invalid() was added. - EF -2009-03-25 Make contour and contourf handle nan in their Z argument. - EF +2009-03-25 + Make contour and contourf handle nan in their Z argument. - EF -2009-03-20 Add AuxTransformBox in offsetbox.py to support some transformation. - anchored_text.py example is enhanced and renamed - (anchored_artists.py). - JJL +2009-03-20 + Add AuxTransformBox in offsetbox.py to support some transformation. + anchored_text.py example is enhanced and renamed (anchored_artists.py). - + JJL -2009-03-20 Add "bar" connection style for annotation - JJL +2009-03-20 + Add "bar" connection style for annotation - JJL -2009-03-17 Fix bugs in edge color handling by contourf, found - by Jae-Joon Lee. - EF +2009-03-17 + Fix bugs in edge color handling by contourf, found by Jae-Joon Lee. - EF -2009-03-14 Added 'LightSource' class to colors module for - creating shaded relief maps. shading_example.py - added to illustrate usage. - JSW +2009-03-14 + Added 'LightSource' class to colors module for creating shaded relief maps. + shading_example.py added to illustrate usage. - JSW -2009-03-11 Ensure wx version >= 2.8; thanks to Sandro Tosi and - Chris Barker. - EF +2009-03-11 + Ensure wx version >= 2.8; thanks to Sandro Tosi and Chris Barker. - EF -2009-03-10 Fix join style bug in pdf. - JKS +2009-03-10 + Fix join style bug in pdf. - JKS -2009-03-07 Add pyplot access to figure number list - EF +2009-03-07 + Add pyplot access to figure number list - EF -2009-02-28 hashing of FontProperties accounts current rcParams - JJL +2009-02-28 + hashing of FontProperties accounts current rcParams - JJL -2009-02-28 Prevent double-rendering of shared axis in twinx, twiny - EF +2009-02-28 + Prevent double-rendering of shared axis in twinx, twiny - EF -2009-02-26 Add optional bbox_to_anchor argument for legend class - JJL +2009-02-26 + Add optional bbox_to_anchor argument for legend class - JJL -2009-02-26 Support image clipping in pdf backend. - JKS +2009-02-26 + Support image clipping in pdf backend. - JKS -2009-02-25 Improve tick location subset choice in FixedLocator. - EF +2009-02-25 + Improve tick location subset choice in FixedLocator. - EF -2009-02-24 Deprecate numerix, and strip out all but the numpy - part of the code. - EF +2009-02-24 + Deprecate numerix, and strip out all but the numpy part of the code. - EF -2009-02-21 Improve scatter argument handling; add an early error - message, allow inputs to have more than one dimension. - EF +2009-02-21 + Improve scatter argument handling; add an early error message, allow inputs + to have more than one dimension. - EF -2009-02-16 Move plot_directive.py to the installed source tree. Add - support for inline code content - MGD +2009-02-16 + Move plot_directive.py to the installed source tree. Add support for + inline code content - MGD -2009-02-16 Move mathmpl.py to the installed source tree so it is - available to other projects. - MGD +2009-02-16 + Move mathmpl.py to the installed source tree so it is available to other + projects. - MGD -2009-02-14 Added the legend title support - JJL +2009-02-14 + Added the legend title support - JJL -2009-02-10 Fixed a bug in backend_pdf so it doesn't break when the setting - pdf.use14corefonts=True is used. Added test case in - unit/test_pdf_use14corefonts.py. - NGR +2009-02-10 + Fixed a bug in backend_pdf so it doesn't break when the setting + pdf.use14corefonts=True is used. Added test case in + unit/test_pdf_use14corefonts.py. - NGR -2009-02-08 Added a new imsave function to image.py and exposed it in - the pyplot interface - GR +2009-02-08 + Added a new imsave function to image.py and exposed it in the pyplot + interface - GR -2009-02-04 Some reorgnization of the legend code. anchored_text.py - added as an example. - JJL +2009-02-04 + Some reorganization of the legend code. anchored_text.py added as an + example. - JJL -2009-02-04 Add extent keyword arg to hexbin - ADS +2009-02-04 + Add extent keyword arg to hexbin - ADS -2009-02-04 Fix bug in mathtext related to \dots and \ldots - MGD +2009-02-04 + Fix bug in mathtext related to \dots and \ldots - MGD -2009-02-03 Change default joinstyle to round - MGD +2009-02-03 + Change default joinstyle to round - MGD -2009-02-02 Reduce number of marker XObjects in pdf output - JKS +2009-02-02 + Reduce number of marker XObjects in pdf output - JKS -2009-02-02 Change default resolution on polar plot to 1 - MGD +2009-02-02 + Change default resolution on polar plot to 1 - MGD -2009-02-02 Avoid malloc errors in ttconv for fonts that don't have - e.g., PostName (a version of Tahoma triggered this) - JKS +2009-02-02 + Avoid malloc errors in ttconv for fonts that don't have e.g., PostName (a + version of Tahoma triggered this) - JKS -2009-01-30 Remove support for pyExcelerator in exceltools -- use xlwt - instead - JDH +2009-01-30 + Remove support for pyExcelerator in exceltools -- use xlwt instead - JDH -2009-01-29 Document 'resolution' kwarg for polar plots. Support it - when using pyplot.polar, not just Figure.add_axes. - MGD +2009-01-29 + Document 'resolution' kwarg for polar plots. Support it when using + pyplot.polar, not just Figure.add_axes. - MGD -2009-01-29 Rework the nan-handling/clipping/quantizing/simplification - framework so each is an independent part of a pipeline. - Expose the C++-implementation of all of this so it can be - used from all Python backends. Add rcParam - "path.simplify_threshold" to control the threshold of - similarity below which vertices will be removed. +2009-01-29 + Rework the nan-handling/clipping/quantizing/simplification framework so + each is an independent part of a pipeline. Expose the C++-implementation + of all of this so it can be used from all Python backends. Add rcParam + "path.simplify_threshold" to control the threshold of similarity below + which vertices will be removed. -2009-01-26 Improved tight bbox option of the savefig. - JJL +2009-01-26 + Improved tight bbox option of the savefig. - JJL -2009-01-26 Make curves and NaNs play nice together - MGD +2009-01-26 + Make curves and NaNs play nice together - MGD -2009-01-21 Changed the defaults of acorr and xcorr to use - usevlines=True, maxlags=10 and normed=True since these are - the best defaults +2009-01-21 + Changed the defaults of acorr and xcorr to use usevlines=True, maxlags=10 + and normed=True since these are the best defaults -2009-01-19 Fix bug in quiver argument handling. - EF +2009-01-19 + Fix bug in quiver argument handling. - EF -2009-01-19 Fix bug in backend_gtk: don't delete nonexistent toolbar. - EF +2009-01-19 + Fix bug in backend_gtk: don't delete nonexistent toolbar. - EF -2009-01-16 Implement bbox_inches option for savefig. If bbox_inches is - "tight", try to determine the tight bounding box. - JJL +2009-01-16 + Implement bbox_inches option for savefig. If bbox_inches is "tight", try to + determine the tight bounding box. - JJL -2009-01-16 Fix bug in is_string_like so it doesn't raise an - unnecessary exception. - EF +2009-01-16 + Fix bug in is_string_like so it doesn't raise an unnecessary exception. - + EF -2009-01-16 Fix an infinite recursion in the unit registry when searching - for a converter for a sequence of strings. Add a corresponding - test. - RM +2009-01-16 + Fix an infinite recursion in the unit registry when searching for a + converter for a sequence of strings. Add a corresponding test. - RM -2009-01-16 Bugfix of C typedef of MPL_Int64 that was failing on - Windows XP 64 bit, as reported by George Goussard on numpy - mailing list. - ADS +2009-01-16 + Bugfix of C typedef of MPL_Int64 that was failing on Windows XP 64 bit, as + reported by George Goussard on numpy mailing list. - ADS -2009-01-16 Added helper function LinearSegmentedColormap.from_list to - facilitate building simple custom colomaps. See - examples/pylab_examples/custom_cmap_fromlist.py - JDH +2009-01-16 + Added helper function LinearSegmentedColormap.from_list to facilitate + building simple custom colomaps. See + examples/pylab_examples/custom_cmap_fromlist.py - JDH -2009-01-16 Applied Michiel's patch for macosx backend to fix rounding - bug. Closed sf bug 2508440 - JSW +2009-01-16 + Applied Michiel's patch for macosx backend to fix rounding bug. Closed sf + bug 2508440 - JSW -2009-01-10 Applied Michiel's hatch patch for macosx backend and - draw_idle patch for qt. Closes sf patched 2497785 and - 2468809 - JDH +2009-01-10 + Applied Michiel's hatch patch for macosx backend and draw_idle patch for + qt. Closes sf patched 2497785 and 2468809 - JDH -2009-01-10 Fix bug in pan/zoom with log coordinates. - EF +2009-01-10 + Fix bug in pan/zoom with log coordinates. - EF -2009-01-06 Fix bug in setting of dashed negative contours. - EF +2009-01-06 + Fix bug in setting of dashed negative contours. - EF -2009-01-06 Be fault tolerant when len(linestyles)>NLev in contour. - MM +2009-01-06 + Be fault tolerant when len(linestyles)>NLev in contour. - MM -2009-01-06 Added marginals kwarg to hexbin to plot marginal densities - JDH +2009-01-06 + Added marginals kwarg to hexbin to plot marginal densities JDH -2009-01-06 Change user-visible multipage pdf object to PdfPages to - avoid accidents with the file-like PdfFile. - JKS +2009-01-06 + Change user-visible multipage pdf object to PdfPages to avoid accidents + with the file-like PdfFile. - JKS -2009-01-05 Fix a bug in pdf usetex: allow using non-embedded fonts. - JKS +2009-01-05 + Fix a bug in pdf usetex: allow using non-embedded fonts. - JKS -2009-01-05 optional use of preview.sty in usetex mode. - JJL +2009-01-05 + optional use of preview.sty in usetex mode. - JJL -2009-01-02 Allow multipage pdf files. - JKS +2009-01-02 + Allow multipage pdf files. - JKS -2008-12-31 Improve pdf usetex by adding support for font effects - (slanting and extending). - JKS +2008-12-31 + Improve pdf usetex by adding support for font effects (slanting and + extending). - JKS -2008-12-29 Fix a bug in pdf usetex support, which occurred if the same - Type-1 font was used with different encodings, e.g., with - Minion Pro and MnSymbol. - JKS +2008-12-29 + Fix a bug in pdf usetex support, which occurred if the same Type-1 font was + used with different encodings, e.g., with Minion Pro and MnSymbol. - JKS -2008-12-20 fix the dpi-dependent offset of Shadow. - JJL +2008-12-20 + fix the dpi-dependent offset of Shadow. - JJL -2008-12-20 fix the hatch bug in the pdf backend. minor update - in docs and example - JJL +2008-12-20 + fix the hatch bug in the pdf backend. minor update in docs and example - + JJL -2008-12-19 Add axes_locator attribute in Axes. Two examples are added. - - JJL +2008-12-19 + Add axes_locator attribute in Axes. Two examples are added. - JJL -2008-12-19 Update Axes.legend documentation. /api/api_changes.rst is also - updated to describe changes in keyword parameters. - Issue a warning if old keyword parameters are used. - JJL +2008-12-19 + Update Axes.legend documentation. /api/api_changes.rst is also updated to + describe changes in keyword parameters. Issue a warning if old keyword + parameters are used. - JJL -2008-12-18 add new arrow style, a line + filled triangles. -JJL +2008-12-18 + add new arrow style, a line + filled triangles. -JJL ---------------- -2008-12-18 Re-Released 0.98.5.2 from v0_98_5_maint at r6679 - Released 0.98.5.2 from v0_98_5_maint at r6667 - -2008-12-18 Removed configobj, experimental traits and doc/mpl_data link - JDH - -2008-12-18 Fix bug where a line with NULL data limits prevents - subsequent data limits from calculating correctly - MGD +2008-12-18 + Re-Released 0.98.5.2 from v0_98_5_maint at r6679 Released 0.98.5.2 from + v0_98_5_maint at r6667 -2008-12-17 Major documentation generator changes - MGD +2008-12-18 + Removed configobj, experimental traits and doc/mpl_data link - JDH -2008-12-17 Applied macosx backend patch with support for path - collections, quadmesh, etc... - JDH +2008-12-18 + Fix bug where a line with NULL data limits prevents subsequent data limits + from calculating correctly - MGD -2008-12-17 fix dpi-dependent behavior of text bbox and arrow in annotate - -JJL +2008-12-17 + Major documentation generator changes - MGD -2008-12-17 Add group id support in artist. Two examples which - demonstrate svg filter are added. -JJL +2008-12-17 + Applied macosx backend patch with support for path collections, quadmesh, + etc... - JDH -2008-12-16 Another attempt to fix dpi-dependent behavior of Legend. -JJL +2008-12-17 + fix dpi-dependent behavior of text bbox and arrow in annotate -JJL -2008-12-16 Fixed dpi-dependent behavior of Legend and fancybox in Text. +2008-12-17 + Add group id support in artist. Two examples which demonstrate svg filter + are added. -JJL -2008-12-16 Added markevery property to Line2D to support subsampling - of markers - JDH -2008-12-15 Removed mpl_data symlink in docs. On platforms that do not - support symlinks, these become copies, and the font files - are large, so the distro becomes unnecessarily bloated. - Keeping the mpl_examples dir because relative links are - harder for the plot directive and the \*.py files are not so - large. - JDH +2008-12-16 + Another attempt to fix dpi-dependent behavior of Legend. -JJL -2008-12-15 Fix \$ in non-math text with usetex off. Document - differences between usetex on/off - MGD +2008-12-16 + Fixed dpi-dependent behavior of Legend and fancybox in Text. -2008-12-15 Fix anti-aliasing when auto-snapping - MGD +2008-12-16 + Added markevery property to Line2D to support subsampling of markers - JDH -2008-12-15 Fix grid lines not moving correctly during pan and zoom - MGD +2008-12-15 + Removed mpl_data symlink in docs. On platforms that do not support + symlinks, these become copies, and the font files are large, so the distro + becomes unnecessarily bloated. Keeping the mpl_examples dir because + relative links are harder for the plot directive and the \*.py files are + not so large. - JDH -2008-12-12 Preparations to eliminate maskedarray rcParams key: its - use will now generate a warning. Similarly, importing - the obsolote numerix.npyma will generate a warning. - EF +2008-12-15 + Fix \$ in non-math text with usetex off. Document differences between + usetex on/off - MGD -2008-12-12 Added support for the numpy.histogram() weights parameter - to the axes hist() method. Docs taken from numpy - MM +2008-12-15 + Fix anti-aliasing when auto-snapping - MGD -2008-12-12 Fixed warning in hist() with numpy 1.2 - MM +2008-12-15 + Fix grid lines not moving correctly during pan and zoom - MGD -2008-12-12 Removed external packages: configobj and enthought.traits - which are only required by the experimental traited config - and are somewhat out of date. If needed, install them - independently, see: +2008-12-12 + Preparations to eliminate maskedarray rcParams key: its use will now + generate a warning. Similarly, importing the obsolete numerix.npyma will + generate a warning. - EF - http://code.enthought.com/pages/traits.html +2008-12-12 + Added support for the numpy.histogram() weights parameter to the axes + hist() method. Docs taken from numpy - MM - and: +2008-12-12 + Fixed warning in hist() with numpy 1.2 - MM - http://www.voidspace.org.uk/python/configobj.html +2008-12-12 + Removed external packages: configobj and enthought.traits which are only + required by the experimental traited config and are somewhat out of date. + If needed, install them independently, see + http://code.enthought.com/pages/traits.html and + http://www.voidspace.org.uk/python/configobj.html -2008-12-12 Added support to assign labels to histograms of multiple - data. - MM +2008-12-12 + Added support to assign labels to histograms of multiple data. - MM ------------------------- -2008-12-11 Released 0.98.5 at svn r6573 +2008-12-11 + Released 0.98.5 at svn r6573 -2008-12-11 Use subprocess.Popen instead of os.popen in dviread - (Windows problem reported by Jorgen Stenarson) - JKS +2008-12-11 + Use subprocess.Popen instead of os.popen in dviread (Windows problem + reported by Jorgen Stenarson) - JKS -2008-12-10 Added Michael's font_manager fix and Jae-Joon's - figure/subplot fix. Bumped version number to 0.98.5 - JDH +2008-12-10 + Added Michael's font_manager fix and Jae-Joon's figure/subplot fix. Bumped + version number to 0.98.5 - JDH ---------------------------- -2008-12-09 Released 0.98.4 at svn r6536 - -2008-12-08 Added mdehoon's native macosx backend from sf patch 2179017 - JDH - -2008-12-08 Removed the prints in the set_*style commands. Return the - list of pprinted strings instead - JDH - -2008-12-08 Some of the changes Michael made to improve the output of - the property tables in the rest docs broke of made - difficult to use some of the interactive doc helpers, e.g., - setp and getp. Having all the rest markup in the ipython - shell also confused the docstrings. I added a new rc param - docstring.hardcopy, to format the docstrings differently for - hard copy and other use. The ArtistInspector could use a - little refactoring now since there is duplication of effort - between the rest out put and the non-rest output - JDH - -2008-12-08 Updated spectral methods (psd, csd, etc.) to scale one-sided - densities by a factor of 2 and, optionally, scale all densities - by the sampling frequency. This gives better MatLab - compatibility. -RM - -2008-12-08 Fixed alignment of ticks in colorbars. -MGD - -2008-12-07 drop the deprecated "new" keyword of np.histogram() for - numpy 1.2 or later. -JJL - -2008-12-06 Fixed a bug in svg backend that new_figure_manager() - ignores keywords arguments such as figsize, etc. -JJL - -2008-12-05 Fixed a bug that the handlelength of the new legend class - set too short when numpoints=1 -JJL - -2008-12-04 Added support for data with units (e.g., dates) to - Axes.fill_between. -RM - -2008-12-04 Added fancybox keyword to legend. Also applied some changes - for better look, including baseline adjustment of the - multiline texts so that it is center aligned. -JJL +2008-12-09 + Released 0.98.4 at svn r6536 + +2008-12-08 + Added mdehoon's native macosx backend from sf patch 2179017 - JDH + +2008-12-08 + Removed the prints in the set_*style commands. Return the list of pprinted + strings instead - JDH -2008-12-02 The transmuter classes in the patches.py are reorganized as - subclasses of the Style classes. A few more box and arrow - styles are added. -JJL +2008-12-08 + Some of the changes Michael made to improve the output of the property + tables in the rest docs broke of made difficult to use some of the + interactive doc helpers, e.g., setp and getp. Having all the rest markup + in the ipython shell also confused the docstrings. I added a new rc param + docstring.hardcopy, to format the docstrings differently for hard copy and + other use. The ArtistInspector could use a little refactoring now since + there is duplication of effort between the rest out put and the non-rest + output - JDH -2008-12-02 Fixed a bug in the new legend class that didn't allowed - a tuple of coordinate values as loc. -JJL +2008-12-08 + Updated spectral methods (psd, csd, etc.) to scale one-sided densities by a + factor of 2 and, optionally, scale all densities by the sampling frequency. + This gives better MatLab compatibility. -RM -2008-12-02 Improve checks for external dependencies, using subprocess - (instead of deprecated popen*) and distutils (for version - checking) - DSD +2008-12-08 + Fixed alignment of ticks in colorbars. -MGD -2008-11-30 Reimplementation of the legend which supports baseline alignment, - multi-column, and expand mode. - JJL +2008-12-07 + drop the deprecated "new" keyword of np.histogram() for numpy 1.2 or later. + -JJL -2008-12-01 Fixed histogram autoscaling bug when bins or range are given - explicitly (fixes Debian bug 503148) - MM +2008-12-06 + Fixed a bug in svg backend that new_figure_manager() ignores keywords + arguments such as figsize, etc. -JJL -2008-11-25 Added rcParam axes.unicode_minus which allows plain hyphen - for minus when False - JDH +2008-12-05 + Fixed a bug that the handlelength of the new legend class set too short + when numpoints=1 -JJL -2008-11-25 Added scatterpoints support in Legend. patch by Erik - Tollerud - JJL +2008-12-04 + Added support for data with units (e.g., dates) to Axes.fill_between. -RM -2008-11-24 Fix crash in log ticking. - MGD +2008-12-04 + Added fancybox keyword to legend. Also applied some changes for better + look, including baseline adjustment of the multiline texts so that it is + center aligned. -JJL -2008-11-20 Added static helper method BrokenHBarCollection.span_where - and Axes/pyplot method fill_between. See - examples/pylab/fill_between.py - JDH +2008-12-02 + The transmuter classes in the patches.py are reorganized as subclasses of + the Style classes. A few more box and arrow styles are added. -JJL -2008-11-12 Add x_isdata and y_isdata attributes to Artist instances, - and use them to determine whether either or both - coordinates are used when updating dataLim. This is - used to fix autoscaling problems that had been triggered - by axhline, axhspan, axvline, axvspan. - EF +2008-12-02 + Fixed a bug in the new legend class that didn't allowed a tuple of + coordinate values as loc. -JJL -2008-11-11 Update the psd(), csd(), cohere(), and specgram() methods - of Axes and the csd() cohere(), and specgram() functions - in mlab to be in sync with the changes to psd(). - In fact, under the hood, these all call the same core - to do computations. - RM +2008-12-02 + Improve checks for external dependencies, using subprocess (instead of + deprecated popen*) and distutils (for version checking) - DSD + +2008-11-30 + Reimplementation of the legend which supports baseline alignment, + multi-column, and expand mode. - JJL + +2008-12-01 + Fixed histogram autoscaling bug when bins or range are given explicitly + (fixes Debian bug 503148) - MM -2008-11-11 Add 'pad_to' and 'sides' parameters to mlab.psd() to - allow controlling of zero padding and returning of - negative frequency components, respecitively. These are - added in a way that does not change the API. - RM +2008-11-25 + Added rcParam axes.unicode_minus which allows plain hyphen for minus when + False - JDH + +2008-11-25 + Added scatterpoints support in Legend. patch by Erik Tollerud - JJL + +2008-11-24 + Fix crash in log ticking. - MGD -2008-11-10 Fix handling of c kwarg by scatter; generalize - is_string_like to accept numpy and numpy.ma string - array scalars. - RM and EF +2008-11-20 + Added static helper method BrokenHBarCollection.span_where and Axes/pyplot + method fill_between. See examples/pylab/fill_between.py - JDH + +2008-11-12 + Add x_isdata and y_isdata attributes to Artist instances, and use them to + determine whether either or both coordinates are used when updating + dataLim. This is used to fix autoscaling problems that had been triggered + by axhline, axhspan, axvline, axvspan. - EF + +2008-11-11 + Update the psd(), csd(), cohere(), and specgram() methods of Axes and the + csd() cohere(), and specgram() functions in mlab to be in sync with the + changes to psd(). In fact, under the hood, these all call the same core to + do computations. - RM + +2008-11-11 + Add 'pad_to' and 'sides' parameters to mlab.psd() to allow controlling of + zero padding and returning of negative frequency components, respectively. + These are added in a way that does not change the API. - RM + +2008-11-10 + Fix handling of c kwarg by scatter; generalize is_string_like to accept + numpy and numpy.ma string array scalars. - RM and EF + +2008-11-09 + Fix a possible EINTR problem in dviread, which might help when saving pdf + files from the qt backend. - JKS + +2008-11-05 + Fix bug with zoom to rectangle and twin axes - MGD + +2008-10-24 + Added Jae Joon's fancy arrow, box and annotation enhancements -- see + examples/pylab_examples/annotation_demo2.py + +2008-10-23 + Autoscaling is now supported with shared axes - EF -2008-11-09 Fix a possible EINTR problem in dviread, which might help - when saving pdf files from the qt backend. - JKS +2008-10-23 + Fixed exception in dviread that happened with Minion - JKS -2008-11-05 Fix bug with zoom to rectangle and twin axes - MGD +2008-10-21 + set_xlim, ylim now return a copy of the viewlim array to avoid modify + inplace surprises -2008-10-24 Added Jae Joon's fancy arrow, box and annotation - enhancements -- see - examples/pylab_examples/annotation_demo2.py +2008-10-20 + Added image thumbnail generating function matplotlib.image.thumbnail. See + examples/misc/image_thumbnail.py - JDH -2008-10-23 Autoscaling is now supported with shared axes - EF +2008-10-20 + Applied scatleg patch based on ideas and work by Erik Tollerud and Jae-Joon + Lee. - MM -2008-10-23 Fixed exception in dviread that happened with Minion - JKS +2008-10-11 + Fixed bug in pdf backend: if you pass a file object for output instead of a + filename, e.g., in a wep app, we now flush the object at the end. - JKS -2008-10-21 set_xlim, ylim now return a copy of the viewlim array to - avoid modify inplace surprises +2008-10-08 + Add path simplification support to paths with gaps. - EF -2008-10-20 Added image thumbnail generating function - matplotlib.image.thumbnail. See - examples/misc/image_thumbnail.py - JDH +2008-10-05 + Fix problem with AFM files that don't specify the font's full name or + family name. - JKS -2008-10-20 Applied scatleg patch based on ideas and work by Erik - Tollerud and Jae-Joon Lee. - MM +2008-10-04 + Added 'scilimits' kwarg to Axes.ticklabel_format() method, for easy access + to the set_powerlimits method of the major ScalarFormatter. - EF -2008-10-11 Fixed bug in pdf backend: if you pass a file object for - output instead of a filename, e.g., in a wep app, we now - flush the object at the end. - JKS +2008-10-04 + Experimental new kwarg borderpad to replace pad in legend, based on + suggestion by Jae-Joon Lee. - EF -2008-10-08 Add path simplification support to paths with gaps. - EF +2008-09-27 + Allow spy to ignore zero values in sparse arrays, based on patch by Tony + Yu. Also fixed plot to handle empty data arrays, and fixed handling of + markers in figlegend. - EF -2008-10-05 Fix problem with AFM files that don't specify the font's - full name or family name. - JKS +2008-09-24 + Introduce drawstyles for lines. Transparently split linestyles like + 'steps--' into drawstyle 'steps' and linestyle '--'. Legends always use + drawstyle 'default'. - MM -2008-10-04 Added 'scilimits' kwarg to Axes.ticklabel_format() method, - for easy access to the set_powerlimits method of the - major ScalarFormatter. - EF +2008-09-18 + Fixed quiver and quiverkey bugs (failure to scale properly when resizing) + and added additional methods for determining the arrow angles - EF -2008-10-04 Experimental new kwarg borderpad to replace pad in legend, - based on suggestion by Jae-Joon Lee. - EF +2008-09-18 + Fix polar interpolation to handle negative values of theta - MGD -2008-09-27 Allow spy to ignore zero values in sparse arrays, based - on patch by Tony Yu. Also fixed plot to handle empty - data arrays, and fixed handling of markers in figlegend. - EF +2008-09-14 + Reorganized cbook and mlab methods related to numerical calculations that + have little to do with the goals of those two modules into a separate + module numerical_methods.py Also, added ability to select points and stop + point selection with keyboard in ginput and manual contour labeling code. + Finally, fixed contour labeling bug. - DMK -2008-09-24 Introduce drawstyles for lines. Transparently split linestyles - like 'steps--' into drawstyle 'steps' and linestyle '--'. - Legends always use drawstyle 'default'. - MM +2008-09-11 + Fix backtick in Postscript output. - MGD -2008-09-18 Fixed quiver and quiverkey bugs (failure to scale properly - when resizing) and added additional methods for determining - the arrow angles - EF +2008-09-10 + [ 2089958 ] Path simplification for vector output backends Leverage the + simplification code exposed through path_to_polygons to simplify certain + well-behaved paths in the vector backends (PDF, PS and SVG). + "path.simplify" must be set to True in matplotlibrc for this to work. + - MGD -2008-09-18 Fix polar interpolation to handle negative values of theta - MGD +2008-09-10 + Add "filled" kwarg to Path.intersects_path and Path.intersects_bbox. - MGD -2008-09-14 Reorganized cbook and mlab methods related to numerical - calculations that have little to do with the goals of those two - modules into a separate module numerical_methods.py - Also, added ability to select points and stop point selection - with keyboard in ginput and manual contour labeling code. - Finally, fixed contour labeling bug. - DMK +2008-09-07 + Changed full arrows slightly to avoid an xpdf rendering problem reported by + Friedrich Hagedorn. - JKS -2008-09-11 Fix backtick in Postscript output. - MGD +2008-09-07 + Fix conversion of quadratic to cubic Bezier curves in PDF and PS backends. + Patch by Jae-Joon Lee. - JKS -2008-09-10 [ 2089958 ] Path simplification for vector output backends - Leverage the simplification code exposed through - path_to_polygons to simplify certain well-behaved paths in - the vector backends (PDF, PS and SVG). "path.simplify" - must be set to True in matplotlibrc for this to work. - - MGD +2008-09-06 + Added 5-point star marker to plot command - EF -2008-09-10 Add "filled" kwarg to Path.intersects_path and - Path.intersects_bbox. - MGD +2008-09-05 + Fix hatching in PS backend - MGD -2008-09-07 Changed full arrows slightly to avoid an xpdf rendering - problem reported by Friedrich Hagedorn. - JKS +2008-09-03 + Fix log with base 2 - MGD -2008-09-07 Fix conversion of quadratic to cubic Bezier curves in PDF - and PS backends. Patch by Jae-Joon Lee. - JKS +2008-09-01 + Added support for bilinear interpolation in NonUniformImage; patch by + Gregory Lielens. - EF -2008-09-06 Added 5-point star marker to plot command - EF +2008-08-28 + Added support for multiple histograms with data of different length - MM -2008-09-05 Fix hatching in PS backend - MGD +2008-08-28 + Fix step plots with log scale - MGD -2008-09-03 Fix log with base 2 - MGD +2008-08-28 + Fix masked arrays with markers in non-Agg backends - MGD -2008-09-01 Added support for bilinear interpolation in - NonUniformImage; patch by Gregory Lielens. - EF +2008-08-28 + Fix clip_on kwarg so it actually works correctly - MGD -2008-08-28 Added support for multiple histograms with data of - different length - MM +2008-08-25 + Fix locale problems in SVG backend - MGD -2008-08-28 Fix step plots with log scale - MGD +2008-08-22 + fix quiver so masked values are not plotted - JSW -2008-08-28 Fix masked arrays with markers in non-Agg backends - MGD +2008-08-18 + improve interactive pan/zoom in qt4 backend on windows - DSD -2008-08-28 Fix clip_on kwarg so it actually works correctly - MGD - -2008-08-25 Fix locale problems in SVG backend - MGD - -2008-08-22 fix quiver so masked values are not plotted - JSW - -2008-08-18 improve interactive pan/zoom in qt4 backend on windows - DSD - -2008-08-11 Fix more bugs in NaN/inf handling. In particular, path simplification - (which does not handle NaNs or infs) will be turned off automatically - when infs or NaNs are present. Also masked arrays are now converted - to arrays with NaNs for consistent handling of masks and NaNs - - MGD and EF +2008-08-11 + Fix more bugs in NaN/inf handling. In particular, path simplification + (which does not handle NaNs or infs) will be turned off automatically when + infs or NaNs are present. Also masked arrays are now converted to arrays + with NaNs for consistent handling of masks and NaNs - MGD and EF ------------------------ -2008-08-03 Released 0.98.3 at svn r5947 +2008-08-03 + Released 0.98.3 at svn r5947 -2008-08-01 Backported memory leak fixes in _ttconv.cpp - MGD +2008-08-01 + Backported memory leak fixes in _ttconv.cpp - MGD -2008-07-31 Added masked array support to griddata. - JSW +2008-07-31 + Added masked array support to griddata. - JSW -2008-07-26 Added optional C and reduce_C_function arguments to - axes.hexbin(). This allows hexbin to accumulate the values - of C based on the x,y coordinates and display in hexagonal - bins. - ADS +2008-07-26 + Added optional C and reduce_C_function arguments to axes.hexbin(). This + allows hexbin to accumulate the values of C based on the x,y coordinates + and display in hexagonal bins. - ADS -2008-07-24 Deprecated (raise NotImplementedError) all the mlab2 - functions from matplotlib.mlab out of concern that some of - them were not clean room implementations. JDH +2008-07-24 + Deprecated (raise NotImplementedError) all the mlab2 functions from + matplotlib.mlab out of concern that some of them were not clean room + implementations. JDH -2008-07-24 Rewrite of a significant portion of the clabel code (class - ContourLabeler) to improve inlining. - DMK +2008-07-24 + Rewrite of a significant portion of the clabel code (class ContourLabeler) + to improve inlining. - DMK -2008-07-22 Added Barbs polygon collection (similar to Quiver) for plotting - wind barbs. Added corresponding helpers to Axes and pyplot as - well. (examples/pylab_examples/barb_demo.py shows it off.) - RMM +2008-07-22 + Added Barbs polygon collection (similar to Quiver) for plotting wind barbs. + Added corresponding helpers to Axes and pyplot as well. + (examples/pylab_examples/barb_demo.py shows it off.) - RMM -2008-07-21 Added scikits.delaunay as matplotlib.delaunay. Added griddata - function in matplotlib.mlab, with example (griddata_demo.py) in - pylab_examples. griddata function will use mpl_toolkits._natgrid - if installed. - JSW +2008-07-21 + Added scikits.delaunay as matplotlib.delaunay. Added griddata function in + matplotlib.mlab, with example (griddata_demo.py) in pylab_examples. + griddata function will use mpl_toolkits._natgrid if installed. - JSW -2008-07-21 Re-introduced offset_copy that works in the context of the - new transforms. - MGD +2008-07-21 + Re-introduced offset_copy that works in the context of the new transforms. + - MGD -2008-07-21 Committed patch by Ryan May to add get_offsets and - set_offsets to Collections base class - EF +2008-07-21 + Committed patch by Ryan May to add get_offsets and set_offsets to + Collections base class - EF -2008-07-21 Changed the "asarray" strategy in image.py so that - colormapping of masked input should work for all - image types (thanks Klaus Zimmerman) - EF +2008-07-21 + Changed the "asarray" strategy in image.py so that colormapping of masked + input should work for all image types (thanks Klaus Zimmerman) - EF -2008-07-20 Rewrote cbook.delete_masked_points and corresponding - unit test to support rgb color array inputs, datetime - inputs, etc. - EF +2008-07-20 + Rewrote cbook.delete_masked_points and corresponding unit test to support + rgb color array inputs, datetime inputs, etc. - EF -2008-07-20 Renamed unit/axes_unit.py to cbook_unit.py and modified - in accord with Ryan's move of delete_masked_points from - axes to cbook. - EF +2008-07-20 + Renamed unit/axes_unit.py to cbook_unit.py and modified in accord with + Ryan's move of delete_masked_points from axes to cbook. - EF -2008-07-18 Check for nan and inf in axes.delete_masked_points(). - This should help hexbin and scatter deal with nans. - ADS +2008-07-18 + Check for nan and inf in axes.delete_masked_points(). This should help + hexbin and scatter deal with nans. - ADS -2008-07-17 Added ability to manually select contour label locations. - Also added a waitforbuttonpress function. - DMK +2008-07-17 + Added ability to manually select contour label locations. Also added a + waitforbuttonpress function. - DMK -2008-07-17 Fix bug with NaNs at end of path (thanks, Andrew Straw for - the report) - MGD +2008-07-17 + Fix bug with NaNs at end of path (thanks, Andrew Straw for the report) - + MGD -2008-07-16 Improve error handling in texmanager, thanks to Ian Henry - for reporting - DSD +2008-07-16 + Improve error handling in texmanager, thanks to Ian Henry for reporting - + DSD -2008-07-12 Added support for external backends with the - "module://my_backend" syntax - JDH +2008-07-12 + Added support for external backends with the "module://my_backend" syntax - + JDH -2008-07-11 Fix memory leak related to shared axes. Grouper should - store weak references. - MGD +2008-07-11 + Fix memory leak related to shared axes. Grouper should store weak + references. - MGD -2008-07-10 Bugfix: crash displaying fontconfig pattern - MGD +2008-07-10 + Bugfix: crash displaying fontconfig pattern - MGD -2008-07-10 Bugfix: [ 2013963 ] update_datalim_bounds in Axes not works - MGD +2008-07-10 + Bugfix: [ 2013963 ] update_datalim_bounds in Axes not works - MGD -2008-07-10 Bugfix: [ 2014183 ] multiple imshow() causes gray edges - MGD +2008-07-10 + Bugfix: [ 2014183 ] multiple imshow() causes gray edges - MGD -2008-07-09 Fix rectangular axes patch on polar plots bug - MGD +2008-07-09 + Fix rectangular axes patch on polar plots bug - MGD -2008-07-09 Improve mathtext radical rendering - MGD +2008-07-09 + Improve mathtext radical rendering - MGD -2008-07-08 Improve mathtext superscript placement - MGD +2008-07-08 + Improve mathtext superscript placement - MGD -2008-07-07 Fix custom scales in pcolormesh (thanks Matthew Turk) - MGD +2008-07-07 + Fix custom scales in pcolormesh (thanks Matthew Turk) - MGD -2008-07-03 Implemented findobj method for artist and pyplot - see - examples/pylab_examples/findobj_demo.py - JDH +2008-07-03 + Implemented findobj method for artist and pyplot - see + examples/pylab_examples/findobj_demo.py - JDH -2008-06-30 Another attempt to fix TextWithDash - DSD +2008-06-30 + Another attempt to fix TextWithDash - DSD -2008-06-30 Removed Qt4 NavigationToolbar2.destroy -- it appears to - have been unnecessary and caused a bug reported by P. - Raybaut - DSD +2008-06-30 + Removed Qt4 NavigationToolbar2.destroy -- it appears to have been + unnecessary and caused a bug reported by P. Raybaut - DSD -2008-06-27 Fixed tick positioning bug - MM +2008-06-27 + Fixed tick positioning bug - MM -2008-06-27 Fix dashed text bug where text was at the wrong end of the - dash - MGD +2008-06-27 + Fix dashed text bug where text was at the wrong end of the dash - MGD -2008-06-26 Fix mathtext bug for expressions like $x_{\leftarrow}$ - MGD +2008-06-26 + Fix mathtext bug for expressions like $x_{\leftarrow}$ - MGD -2008-06-26 Fix direction of horizontal/vertical hatches - MGD +2008-06-26 + Fix direction of horizontal/vertical hatches - MGD -2008-06-25 Figure.figurePatch renamed Figure.patch, Axes.axesPatch - renamed Axes.patch, Axes.axesFrame renamed Axes.frame, - Axes.get_frame, which returns Axes.patch, is deprecated. - Examples and users guide updated - JDH +2008-06-25 + Figure.figurePatch renamed Figure.patch, Axes.axesPatch renamed Axes.patch, + Axes.axesFrame renamed Axes.frame, Axes.get_frame, which returns + Axes.patch, is deprecated. Examples and users guide updated - JDH -2008-06-25 Fix rendering quality of pcolor - MGD +2008-06-25 + Fix rendering quality of pcolor - MGD ---------------------------- -2008-06-24 Released 0.98.2 at svn r5667 - (source only for debian) JDH +2008-06-24 + Released 0.98.2 at svn r5667 - (source only for debian) JDH -2008-06-24 Added "transparent" kwarg to savefig. - MGD +2008-06-24 + Added "transparent" kwarg to savefig. - MGD -2008-06-24 Applied Stefan's patch to draw a single centered marker over - a line with numpoints==1 - JDH +2008-06-24 + Applied Stefan's patch to draw a single centered marker over a line with + numpoints==1 - JDH -2008-06-23 Use splines to render circles in scatter plots - MGD +2008-06-23 + Use splines to render circles in scatter plots - MGD ---------------------------- -2008-06-22 Released 0.98.1 at revision 5637 +2008-06-22 + Released 0.98.1 at revision 5637 -2008-06-22 Removed axes3d support and replaced it with a - NotImplementedError for one release cycle +2008-06-22 + Removed axes3d support and replaced it with a NotImplementedError for one + release cycle -2008-06-21 fix marker placement bug in backend_ps - DSD +2008-06-21 + fix marker placement bug in backend_ps - DSD -2008-06-20 [ 1978629 ] scale documentation missing/incorrect for log - MGD +2008-06-20 + [ 1978629 ] scale documentation missing/incorrect for log - MGD -2008-06-20 Added closed kwarg to PolyCollection. Fixes bug [ 1994535 - ] still missing lines on graph with svn (r 5548). - MGD +2008-06-20 + Added closed kwarg to PolyCollection. Fixes bug [ 1994535 ] still missing + lines on graph with svn (r 5548). - MGD -2008-06-20 Added set/get_closed method to Polygon; fixes error - in hist - MM +2008-06-20 + Added set/get_closed method to Polygon; fixes error in hist - MM -2008-06-19 Use relative font sizes (e.g., 'medium' and 'large') in - rcsetup.py and matplotlibrc.template so that text will - be scaled by default when changing rcParams['font.size'] - - EF +2008-06-19 + Use relative font sizes (e.g., 'medium' and 'large') in rcsetup.py and + matplotlibrc.template so that text will be scaled by default when changing + rcParams['font.size'] - EF -2008-06-17 Add a generic PatchCollection class that can contain any - kind of patch. - MGD +2008-06-17 + Add a generic PatchCollection class that can contain any kind of patch. - + MGD -2008-06-13 Change pie chart label alignment to avoid having labels - overwrite the pie - MGD +2008-06-13 + Change pie chart label alignment to avoid having labels overwrite the pie - + MGD -2008-06-12 Added some helper functions to the mathtext parser to - return bitmap arrays or write pngs to make it easier to use - mathtext outside the context of an mpl figure. modified - the mathpng sphinxext to use the mathtext png save - functionality - see examples/api/mathtext_asarray.py - JDH +2008-06-12 + Added some helper functions to the mathtext parser to return bitmap arrays + or write pngs to make it easier to use mathtext outside the context of an + mpl figure. modified the mathpng sphinxext to use the mathtext png save + functionality - see examples/api/mathtext_asarray.py - JDH -2008-06-11 Use matplotlib.mathtext to render math expressions in - online docs - MGD +2008-06-11 + Use matplotlib.mathtext to render math expressions in online docs - MGD -2008-06-11 Move PNG loading/saving to its own extension module, and - remove duplicate code in _backend_agg.cpp and _image.cpp - that does the same thing - MGD +2008-06-11 + Move PNG loading/saving to its own extension module, and remove duplicate + code in _backend_agg.cpp and _image.cpp that does the same thing - MGD -2008-06-11 Numerous mathtext bugfixes, primarily related to - dpi-independence - MGD +2008-06-11 + Numerous mathtext bugfixes, primarily related to dpi-independence - MGD -2008-06-10 Bar now applies the label only to the first patch only, and - sets '_nolegend_' for the other patch labels. This lets - autolegend work as expected for hist and bar - see - \https://sourceforge.net/tracker/index.php?func=detail&aid=1986597&group_id=80706&atid=560720 - JDH +2008-06-10 + Bar now applies the label only to the first patch only, and sets + '_nolegend_' for the other patch labels. This lets autolegend work as + expected for hist and bar - see + \https://sourceforge.net/tracker/index.php?func=detail&aid=1986597&group_id=80706&atid=560720 + JDH -2008-06-10 Fix text baseline alignment bug. [ 1985420 ] Repair of - baseline alignment in Text._get_layout. Thanks Stan West - - MGD +2008-06-10 + Fix text baseline alignment bug. [ 1985420 ] Repair of baseline alignment + in Text._get_layout. Thanks Stan West - MGD -2008-06-09 Committed Gregor's image resample patch to downsampling - images with new rcparam image.resample - JDH +2008-06-09 + Committed Gregor's image resample patch to downsampling images with new + rcparam image.resample - JDH -2008-06-09 Don't install Enthought.Traits along with matplotlib. For - matplotlib developers convenience, it can still be - installed by setting an option in setup.cfg while we figure - decide if there is a future for the traited config - DSD +2008-06-09 + Don't install Enthought.Traits along with matplotlib. For matplotlib + developers convenience, it can still be installed by setting an option in + setup.cfg while we figure decide if there is a future for the traited + config - DSD -2008-06-09 Added range keyword arg to hist() - MM +2008-06-09 + Added range keyword arg to hist() - MM -2008-06-07 Moved list of backends to rcsetup.py; made use of lower - case for backend names consistent; use validate_backend - when importing backends subpackage - EF +2008-06-07 + Moved list of backends to rcsetup.py; made use of lower case for backend + names consistent; use validate_backend when importing backends subpackage - + EF -2008-06-06 hist() revision, applied ideas proposed by Erik Tollerud and - Olle Engdegard: make histtype='step' unfilled by default - and introduce histtype='stepfilled'; use default color - cycle; introduce reverse cumulative histogram; new align - keyword - MM +2008-06-06 + hist() revision, applied ideas proposed by Erik Tollerud and Olle + Engdegard: make histtype='step' unfilled by default and introduce + histtype='stepfilled'; use default color cycle; introduce reverse + cumulative histogram; new align keyword - MM -2008-06-06 Fix closed polygon patch and also provide the option to - not close the polygon - MGD +2008-06-06 + Fix closed polygon patch and also provide the option to not close the + polygon - MGD -2008-06-05 Fix some dpi-changing-related problems with PolyCollection, - as called by Axes.scatter() - MGD +2008-06-05 + Fix some dpi-changing-related problems with PolyCollection, as called by + Axes.scatter() - MGD -2008-06-05 Fix image drawing so there is no extra space to the right - or bottom - MGD +2008-06-05 + Fix image drawing so there is no extra space to the right or bottom - MGD -2006-06-04 Added a figure title command suptitle as a Figure method - and pyplot command -- see examples/figure_title.py - JDH +2006-06-04 + Added a figure title command suptitle as a Figure method and pyplot command + -- see examples/figure_title.py - JDH -2008-06-02 Added support for log to hist with histtype='step' and fixed - a bug for log-scale stacked histograms - MM +2008-06-02 + Added support for log to hist with histtype='step' and fixed a bug for + log-scale stacked histograms - MM ----------------------------- -2008-05-29 Released 0.98.0 at revision 5314 +2008-05-29 + Released 0.98.0 at revision 5314 -2008-05-29 matplotlib.image.imread now no longer always returns RGBA - -- if the image is luminance or RGB, it will return a MxN - or MxNx3 array if possible. Also uint8 is no longer always - forced to float. +2008-05-29 + matplotlib.image.imread now no longer always returns RGBA -- if the image + is luminance or RGB, it will return a MxN or MxNx3 array if possible. Also + uint8 is no longer always forced to float. -2008-05-29 Implement path clipping in PS backend - JDH +2008-05-29 + Implement path clipping in PS backend - JDH -2008-05-29 Fixed two bugs in texmanager.py: - improved comparison of dvipng versions - fixed a bug introduced when get_grey method was added - - DSD +2008-05-29 + Fixed two bugs in texmanager.py: improved comparison of dvipng versions + fixed a bug introduced when get_grey method was added - DSD -2008-05-28 Fix crashing of PDFs in xpdf and ghostscript when two-byte - characters are used with Type 3 fonts - MGD +2008-05-28 + Fix crashing of PDFs in xpdf and ghostscript when two-byte characters are + used with Type 3 fonts - MGD -2008-05-28 Allow keyword args to configure widget properties as - requested in - \http://sourceforge.net/tracker/index.php?func=detail&aid=1866207&group_id=80706&atid=560722 - - JDH +2008-05-28 + Allow keyword args to configure widget properties as requested in + \http://sourceforge.net/tracker/index.php?func=detail&aid=1866207&group_id=80706&atid=560722 + - JDH -2008-05-28 Replaced '-' with u'\u2212' for minus sign as requested in - \http://sourceforge.net/tracker/index.php?func=detail&aid=1962574&group_id=80706&atid=560720 +2008-05-28 + Replaced '-' with u'\\u2212' for minus sign as requested in + \http://sourceforge.net/tracker/index.php?func=detail&aid=1962574&group_id=80706&atid=560720 -2008-05-28 zero width/height Rectangles no longer influence the - autoscaler. Useful for log histograms with empty bins - - JDH +2008-05-28 + zero width/height Rectangles no longer influence the autoscaler. Useful + for log histograms with empty bins - JDH -2008-05-28 Fix rendering of composite glyphs in Type 3 conversion - (particularly as evidenced in the Eunjin.ttf Korean font) - Thanks Jae-Joon Lee for finding this! +2008-05-28 + Fix rendering of composite glyphs in Type 3 conversion (particularly as + evidenced in the Eunjin.ttf Korean font) Thanks Jae-Joon Lee for finding + this! -2008-05-27 Rewrote the cm.ScalarMappable callback infrastructure to - use cbook.CallbackRegistry rather than custom callback - handling. Amy users of add_observer/notify of the - cm.ScalarMappable should uae the - cm.ScalarMappable.callbacksSM CallbackRegistry instead. JDH +2008-05-27 + Rewrote the cm.ScalarMappable callback infrastructure to use + cbook.CallbackRegistry rather than custom callback handling. Any users of + add_observer/notify of the cm.ScalarMappable should use the + cm.ScalarMappable.callbacksSM CallbackRegistry instead. JDH -2008-05-27 Fix TkAgg build on Ubuntu 8.04 (and hopefully a more - general solution for other platforms, too.) +2008-05-27 + Fix TkAgg build on Ubuntu 8.04 (and hopefully a more general solution for + other platforms, too.) -2008-05-24 Added PIL support for loading images to imread (if PIL is - available) - JDH +2008-05-24 + Added PIL support for loading images to imread (if PIL is available) - JDH -2008-05-23 Provided a function and a method for controlling the - plot color cycle. - EF +2008-05-23 + Provided a function and a method for controlling the plot color cycle. - EF -2008-05-23 Major revision of hist(). Can handle 2D arrays and create - stacked histogram plots; keyword 'width' deprecated and - rwidth (relative width) introduced; align='edge' changed - to center of bin - MM +2008-05-23 + Major revision of hist(). Can handle 2D arrays and create stacked histogram + plots; keyword 'width' deprecated and rwidth (relative width) introduced; + align='edge' changed to center of bin - MM -2008-05-22 Added support for ReST-based doumentation using Sphinx. - Documents are located in doc/, and are broken up into - a users guide and an API reference. To build, run the - make.py files. Sphinx-0.4 is needed to build generate xml, - which will be useful for rendering equations with mathml, - use sphinx from svn until 0.4 is released - DSD +2008-05-22 + Added support for ReST-based documentation using Sphinx. Documents are + located in doc/, and are broken up into a users guide and an API reference. + To build, run the make.py files. Sphinx-0.4 is needed to build generate + xml, which will be useful for rendering equations with mathml, use sphinx + from svn until 0.4 is released - DSD -2008-05-21 Fix segfault in TkAgg backend - MGD +2008-05-21 + Fix segfault in TkAgg backend - MGD -2008-05-21 Fix a "local variable unreferenced" bug in plotfile - MM +2008-05-21 + Fix a "local variable unreferenced" bug in plotfile - MM -2008-05-19 Fix crash when Windows can not access the registry to - determine font path [Bug 1966974, thanks Patrik Simons] - MGD +2008-05-19 + Fix crash when Windows can not access the registry to determine font path + [Bug 1966974, thanks Patrik Simons] - MGD -2008-05-16 removed some unneeded code w/ the python 2.4 requirement. - cbook no longer provides compatibility for reversed, - enumerate, set or izip. removed lib/subprocess, mpl1, - sandbox/units, and the swig code. This stuff should remain - on the maintenance branch for archival purposes. JDH +2008-05-16 + removed some unneeded code w/ the python 2.4 requirement. cbook no longer + provides compatibility for reversed, enumerate, set or izip. removed + lib/subprocess, mpl1, sandbox/units, and the swig code. This stuff should + remain on the maintenance branch for archival purposes. JDH -2008-05-16 Reorganized examples dir - JDH +2008-05-16 + Reorganized examples dir - JDH -2008-05-16 Added 'elinewidth' keyword arg to errorbar, based on patch - by Christopher Brown - MM +2008-05-16 + Added 'elinewidth' keyword arg to errorbar, based on patch by Christopher + Brown - MM -2008-05-16 Added 'cumulative' keyword arg to hist to plot cumulative - histograms. For normed hists, this is normalized to one - MM +2008-05-16 + Added 'cumulative' keyword arg to hist to plot cumulative histograms. For + normed hists, this is normalized to one - MM -2008-05-15 Fix Tk backend segfault on some machines - MGD +2008-05-15 + Fix Tk backend segfault on some machines - MGD -2008-05-14 Don't use stat on Windows (fixes font embedding problem) - MGD +2008-05-14 + Don't use stat on Windows (fixes font embedding problem) - MGD -2008-05-09 Fix /singlequote (') in Postscript backend - MGD +2008-05-09 + Fix /singlequote (') in Postscript backend - MGD -2008-05-08 Fix kerning in SVG when embedding character outlines - MGD +2008-05-08 + Fix kerning in SVG when embedding character outlines - MGD -2008-05-07 Switched to future numpy histogram semantic in hist - MM +2008-05-07 + Switched to future numpy histogram semantic in hist - MM -2008-05-06 Fix strange colors when blitting in QtAgg and Qt4Agg - MGD +2008-05-06 + Fix strange colors when blitting in QtAgg and Qt4Agg - MGD -2008-05-05 pass notify_axes_change to the figure's add_axobserver - in the qt backends, like we do for the other backends. - Thanks Glenn Jones for the report - DSD +2008-05-05 + pass notify_axes_change to the figure's add_axobserver in the qt backends, + like we do for the other backends. Thanks Glenn Jones for the report - DSD -2008-05-02 Added step histograms, based on patch by Erik Tollerud. - MM +2008-05-02 + Added step histograms, based on patch by Erik Tollerud. - MM -2008-05-02 On PyQt <= 3.14 there is no way to determine the underlying - Qt version. [1851364] - MGD +2008-05-02 + On PyQt <= 3.14 there is no way to determine the underlying Qt version. + [1851364] - MGD -2008-05-02 Don't call sys.exit() when pyemf is not found [1924199] - - MGD +2008-05-02 + Don't call sys.exit() when pyemf is not found [1924199] - MGD -2008-05-02 Update _subprocess.c from upstream Python 2.5.2 to get a - few memory and reference-counting-related bugfixes. See - bug 1949978. - MGD +2008-05-02 + Update _subprocess.c from upstream Python 2.5.2 to get a few memory and + reference-counting-related bugfixes. See bug 1949978. - MGD -2008-04-30 Added some record array editing widgets for gtk -- see - examples/rec_edit*.py - JDH +2008-04-30 + Added some record array editing widgets for gtk -- see + examples/rec_edit*.py - JDH -2008-04-29 Fix bug in mlab.sqrtm - MM +2008-04-29 + Fix bug in mlab.sqrtm - MM -2008-04-28 Fix bug in SVG text with Mozilla-based viewers (the symbol - tag is not supported) - MGD +2008-04-28 + Fix bug in SVG text with Mozilla-based viewers (the symbol tag is not + supported) - MGD -2008-04-27 Applied patch by Michiel de Hoon to add hexbin - axes method and pyplot function - EF +2008-04-27 + Applied patch by Michiel de Hoon to add hexbin axes method and pyplot + function - EF -2008-04-25 Enforce python >= 2.4; remove subprocess build - EF +2008-04-25 + Enforce python >= 2.4; remove subprocess build - EF -2008-04-25 Enforce the numpy requirement at build time - JDH +2008-04-25 + Enforce the numpy requirement at build time - JDH -2008-04-24 Make numpy 1.1 and python 2.3 required when importing - matplotlib - EF +2008-04-24 + Make numpy 1.1 and python 2.3 required when importing matplotlib - EF -2008-04-24 Fix compilation issues on VS2003 (Thanks Martin Spacek for - all the help) - MGD +2008-04-24 + Fix compilation issues on VS2003 (Thanks Martin Spacek for all the help) - + MGD -2008-04-24 Fix sub/superscripts when the size of the font has been - changed - MGD +2008-04-24 + Fix sub/superscripts when the size of the font has been changed - MGD -2008-04-22 Use "svg.embed_char_paths" consistently everywhere - MGD +2008-04-22 + Use "svg.embed_char_paths" consistently everywhere - MGD -2008-04-20 Add support to MaxNLocator for symmetric axis autoscaling. - EF +2008-04-20 + Add support to MaxNLocator for symmetric axis autoscaling. - EF -2008-04-20 Fix double-zoom bug. - MM +2008-04-20 + Fix double-zoom bug. - MM -2008-04-15 Speed up colormapping. - EF +2008-04-15 + Speed up colormapping. - EF -2008-04-12 Speed up zooming and panning of dense images. - EF +2008-04-12 + Speed up zooming and panning of dense images. - EF -2008-04-11 Fix global font rcParam setting after initialization - time. - MGD +2008-04-11 + Fix global font rcParam setting after initialization time. - MGD -2008-04-11 Revert commits 5002 and 5031, which were intended to - avoid an unnecessary call to draw(). 5002 broke saving - figures before show(). 5031 fixed the problem created in - 5002, but broke interactive plotting. Unnecessary call to - draw still needs resolution - DSD +2008-04-11 + Revert commits 5002 and 5031, which were intended to avoid an unnecessary + call to draw(). 5002 broke saving figures before show(). 5031 fixed the + problem created in 5002, but broke interactive plotting. Unnecessary call + to draw still needs resolution - DSD -2008-04-07 Improve color validation in rc handling, suggested - by Lev Givon - EF +2008-04-07 + Improve color validation in rc handling, suggested by Lev Givon - EF -2008-04-02 Allow to use both linestyle definition arguments, '-' and - 'solid' etc. in plots/collections - MM +2008-04-02 + Allow to use both linestyle definition arguments, '-' and 'solid' etc. in + plots/collections - MM -2008-03-27 Fix saving to Unicode filenames with Agg backend - (other backends appear to already work...) - (Thanks, Christopher Barker) - MGD +2008-03-27 + Fix saving to Unicode filenames with Agg backend (other backends appear to + already work...) (Thanks, Christopher Barker) - MGD -2008-03-26 Fix SVG backend bug that prevents copying and pasting in - Inkscape (thanks Kaushik Ghose) - MGD +2008-03-26 + Fix SVG backend bug that prevents copying and pasting in Inkscape (thanks + Kaushik Ghose) - MGD -2008-03-24 Removed an unnecessary call to draw() in the backend_qt* - mouseReleaseEvent. Thanks to Ted Drain - DSD +2008-03-24 + Removed an unnecessary call to draw() in the backend_qt* mouseReleaseEvent. + Thanks to Ted Drain - DSD -2008-03-23 Fix a pdf backend bug which sometimes caused the outermost - gsave to not be balanced with a grestore. - JKS +2008-03-23 + Fix a pdf backend bug which sometimes caused the outermost gsave to not be + balanced with a grestore. - JKS -2008-03-20 Fixed a minor bug in ContourSet._process_linestyles when - len(linestyles)==Nlev - MM +2008-03-20 + Fixed a minor bug in ContourSet._process_linestyles when + len(linestyles)==Nlev - MM -2008-03-19 Changed ma import statements to "from numpy import ma"; - this should work with past and future versions of - numpy, whereas "import numpy.ma as ma" will work only - with numpy >= 1.05, and "import numerix.npyma as ma" - is obsolete now that maskedarray is replacing the - earlier implementation, as of numpy 1.05. +2008-03-19 + Changed ma import statements to "from numpy import ma"; this should work + with past and future versions of numpy, whereas "import numpy.ma as ma" + will work only with numpy >= 1.05, and "import numerix.npyma as ma" is + obsolete now that maskedarray is replacing the earlier implementation, as + of numpy 1.05. -2008-03-14 Removed an apparently unnecessary call to - FigureCanvasAgg.draw in backend_qt*agg. Thanks to Ted - Drain - DSD +2008-03-14 + Removed an apparently unnecessary call to FigureCanvasAgg.draw in + backend_qt*agg. Thanks to Ted Drain - DSD -2008-03-10 Workaround a bug in backend_qt4agg's blitting due to a - buffer width/bbox width mismatch in _backend_agg's - copy_from_bbox - DSD +2008-03-10 + Workaround a bug in backend_qt4agg's blitting due to a buffer width/bbox + width mismatch in _backend_agg's copy_from_bbox - DSD -2008-02-29 Fix class Wx toolbar pan and zoom functions (Thanks Jeff - Peery) - MGD +2008-02-29 + Fix class Wx toolbar pan and zoom functions (Thanks Jeff Peery) - MGD -2008-02-16 Added some new rec array functionality to mlab - (rec_summarize, rec2txt and rec_groupby). See - examples/rec_groupby_demo.py. Thanks to Tim M for rec2txt. +2008-02-16 + Added some new rec array functionality to mlab (rec_summarize, rec2txt and + rec_groupby). See examples/rec_groupby_demo.py. Thanks to Tim M for + rec2txt. -2008-02-12 Applied Erik Tollerud's span selector patch - JDH +2008-02-12 + Applied Erik Tollerud's span selector patch - JDH -2008-02-11 Update plotting() doc string to refer to getp/setp. - JKS +2008-02-11 + Update plotting() doc string to refer to getp/setp. - JKS -2008-02-10 Fixed a problem with square roots in the pdf backend with - usetex. - JKS +2008-02-10 + Fixed a problem with square roots in the pdf backend with usetex. - JKS -2008-02-08 Fixed minor __str__ bugs so getp(gca()) works. - JKS +2008-02-08 + Fixed minor __str__ bugs so getp(gca()) works. - JKS -2008-02-05 Added getters for title, xlabel, ylabel, as requested - by Brandon Kieth - EF +2008-02-05 + Added getters for title, xlabel, ylabel, as requested by Brandon Kieth - EF -2008-02-05 Applied Gael's ginput patch and created - examples/ginput_demo.py - JDH +2008-02-05 + Applied Gael's ginput patch and created examples/ginput_demo.py - JDH -2008-02-03 Expose interpnames, a list of valid interpolation - methods, as an AxesImage class attribute. - EF +2008-02-03 + Expose interpnames, a list of valid interpolation methods, as an AxesImage + class attribute. - EF -2008-02-03 Added BoundaryNorm, with examples in colorbar_only.py - and image_masked.py. - EF +2008-02-03 + Added BoundaryNorm, with examples in colorbar_only.py and image_masked.py. + - EF -2008-02-03 Force dpi=72 in pdf backend to fix picture size bug. - JKS +2008-02-03 + Force dpi=72 in pdf backend to fix picture size bug. - JKS -2008-02-01 Fix doubly-included font problem in Postscript backend - MGD +2008-02-01 + Fix doubly-included font problem in Postscript backend - MGD -2008-02-01 Fix reference leak in ft2font Glyph objects. - MGD +2008-02-01 + Fix reference leak in ft2font Glyph objects. - MGD -2008-01-31 Don't use unicode strings with usetex by default - DSD +2008-01-31 + Don't use unicode strings with usetex by default - DSD -2008-01-31 Fix text spacing problems in PDF backend with *some* fonts, - such as STIXGeneral. +2008-01-31 + Fix text spacing problems in PDF backend with *some* fonts, such as + STIXGeneral. -2008-01-31 Fix \sqrt with radical number (broken by making [ and ] - work below) - MGD +2008-01-31 + Fix \sqrt with radical number (broken by making [ and ] work below) - MGD -2008-01-27 Applied Martin Teichmann's patch to improve the Qt4 - backend. Uses Qt's builtin toolbars and statusbars. - See bug 1828848 - DSD +2008-01-27 + Applied Martin Teichmann's patch to improve the Qt4 backend. Uses Qt's + builtin toolbars and statusbars. See bug 1828848 - DSD -2008-01-10 Moved toolkits to mpl_toolkits, made mpl_toolkits - a namespace package - JSWHIT +2008-01-10 + Moved toolkits to mpl_toolkits, made mpl_toolkits a namespace package - + JSWHIT -2008-01-10 Use setup.cfg to set the default parameters (tkagg, - numpy) when building windows installers - DSD +2008-01-10 + Use setup.cfg to set the default parameters (tkagg, numpy) when building + windows installers - DSD -2008-01-10 Fix bug displaying [ and ] in mathtext - MGD +2008-01-10 + Fix bug displaying [ and ] in mathtext - MGD -2008-01-10 Fix bug when displaying a tick value offset with scientific - notation. (Manifests itself as a warning that the \times - symbol can not be found). - MGD +2008-01-10 + Fix bug when displaying a tick value offset with scientific notation. + (Manifests itself as a warning that the \times symbol can not be found). - + MGD -2008-01-10 Use setup.cfg to set the default parameters (tkagg, - numpy) when building windows installers - DSD +2008-01-10 + Use setup.cfg to set the default parameters (tkagg, numpy) when building + windows installers - DSD -------------------- -2008-01-06 Released 0.91.2 at revision 4802 +2008-01-06 + Released 0.91.2 at revision 4802 -2007-12-26 Reduce too-late use of matplotlib.use() to a warning - instead of an exception, for backwards compatibility - EF +2007-12-26 + Reduce too-late use of matplotlib.use() to a warning instead of an + exception, for backwards compatibility - EF -2007-12-25 Fix bug in errorbar, identified by Noriko Minakawa - EF +2007-12-25 + Fix bug in errorbar, identified by Noriko Minakawa - EF -2007-12-25 Changed masked array importing to work with the upcoming - numpy 1.05 (now the maskedarray branch) as well as with - earlier versions. - EF +2007-12-25 + Changed masked array importing to work with the upcoming numpy 1.05 (now + the maskedarray branch) as well as with earlier versions. - EF -2007-12-16 rec2csv saves doubles without losing precision. Also, it - does not close filehandles passed in open. - JDH,ADS +2007-12-16 + rec2csv saves doubles without losing precision. Also, it does not close + filehandles passed in open. - JDH,ADS -2007-12-13 Moved rec2gtk to matplotlib.toolkits.gtktools and rec2excel - to matplotlib.toolkits.exceltools - JDH +2007-12-13 + Moved rec2gtk to matplotlib.toolkits.gtktools and rec2excel to + matplotlib.toolkits.exceltools - JDH -2007-12-12 Support alpha-blended text in the Agg and Svg backends - - MGD +2007-12-12 + Support alpha-blended text in the Agg and Svg backends - MGD -2007-12-10 Fix SVG text rendering bug. - MGD +2007-12-10 + Fix SVG text rendering bug. - MGD -2007-12-10 Increase accuracy of circle and ellipse drawing by using an - 8-piece bezier approximation, rather than a 4-piece one. - Fix PDF, SVG and Cairo backends so they can draw paths - (meaning ellipses as well). - MGD +2007-12-10 + Increase accuracy of circle and ellipse drawing by using an 8-piece bezier + approximation, rather than a 4-piece one. Fix PDF, SVG and Cairo backends + so they can draw paths (meaning ellipses as well). - MGD -2007-12-07 Issue a warning when drawing an image on a non-linear axis. - MGD +2007-12-07 + Issue a warning when drawing an image on a non-linear axis. - MGD -2007-12-06 let widgets.Cursor initialize to the lower x and y bounds - rather than 0,0, which can cause havoc for dates and other - transforms - DSD +2007-12-06 + let widgets.Cursor initialize to the lower x and y bounds rather than 0,0, + which can cause havoc for dates and other transforms - DSD -2007-12-06 updated references to mpl data directories for py2exe - DSD +2007-12-06 + updated references to mpl data directories for py2exe - DSD -2007-12-06 fixed a bug in rcsetup, see bug 1845057 - DSD +2007-12-06 + fixed a bug in rcsetup, see bug 1845057 - DSD -2007-12-05 Fix how fonts are cached to avoid loading the same one multiple times. - (This was a regression since 0.90 caused by the refactoring of - font_manager.py) - MGD +2007-12-05 + Fix how fonts are cached to avoid loading the same one multiple times. + (This was a regression since 0.90 caused by the refactoring of + font_manager.py) - MGD -2007-12-05 Support arbitrary rotation of usetex text in Agg backend. - MGD +2007-12-05 + Support arbitrary rotation of usetex text in Agg backend. - MGD -2007-12-04 Support '|' as a character in mathtext - MGD +2007-12-04 + Support '|' as a character in mathtext - MGD ----------------------------------------------------- -2007-11-27 Released 0.91.1 at revision 4517 +2007-11-27 + Released 0.91.1 at revision 4517 ----------------------------------------------------- -2007-11-27 Released 0.91.0 at revision 4478 - -2007-11-13 All backends now support writing to a file-like object, not - just a regular file. savefig() can be passed a file-like - object in place of a file path. - MGD - -2007-11-13 Improved the default backend selection at build time: - SVG -> Agg -> TkAgg -> WXAgg -> GTK -> GTKAgg. The last usable - backend in this progression will be chosen in the default - config file. If a backend is defined in setup.cfg, that will - be the default backend - DSD - -2007-11-13 Improved creation of default config files at build time for - traited config package - DSD - -2007-11-12 Exposed all the build options in setup.cfg. These options are - read into a dict called "options" by setupext.py. Also, added - "-mpl" tags to the version strings for packages provided by - matplotlib. Versions provided by mpl will be identified and - updated on subsequent installs - DSD - -2007-11-12 Added support for STIX fonts. A new rcParam, - mathtext.fontset, can be used to choose between: - - 'cm': - The TeX/LaTeX Computer Modern fonts - - 'stix': - The STIX fonts (see stixfonts.org) - - 'stixsans': - The STIX fonts, using sans-serif glyphs by default - - 'custom': - A generic Unicode font, in which case the mathtext font - must be specified using mathtext.bf, mathtext.it, - mathtext.sf etc. - - Added a new example, stix_fonts_demo.py to show how to access - different fonts and unusual symbols. - - - MGD - -2007-11-12 Options to disable building backend extension modules moved - from setup.py to setup.cfg - DSD - -2007-11-09 Applied Martin Teichmann's patch 1828813: a QPainter is used in - paintEvent, which has to be destroyed using the method end(). If - matplotlib raises an exception before the call to end - and it - does if you feed it with bad data - this method end() is never - called and Qt4 will start spitting error messages - -2007-11-09 Moved pyparsing back into matplotlib namespace. Don't use - system pyparsing, API is too variable from one release - to the next - DSD - -2007-11-08 Made pylab use straight numpy instead of oldnumeric - by default - EF - -2007-11-08 Added additional record array utilities to mlab (rec2excel, - rec2gtk, rec_join, rec_append_field, rec_drop_field) - JDH +2007-11-27 + Released 0.91.0 at revision 4478 + +2007-11-13 + All backends now support writing to a file-like object, not just a regular + file. savefig() can be passed a file-like object in place of a file path. + - MGD + +2007-11-13 + Improved the default backend selection at build time: SVG -> Agg -> TkAgg + -> WXAgg -> GTK -> GTKAgg. The last usable backend in this progression will + be chosen in the default config file. If a backend is defined in setup.cfg, + that will be the default backend - DSD + +2007-11-13 + Improved creation of default config files at build time for traited config + package - DSD + +2007-11-12 + Exposed all the build options in setup.cfg. These options are read into a + dict called "options" by setupext.py. Also, added "-mpl" tags to the + version strings for packages provided by matplotlib. Versions provided by + mpl will be identified and updated on subsequent installs - DSD + +2007-11-12 + Added support for STIX fonts. A new rcParam, mathtext.fontset, can be used + to choose between: + + 'cm' + The TeX/LaTeX Computer Modern fonts + 'stix' + The STIX fonts (see stixfonts.org) + 'stixsans' + The STIX fonts, using sans-serif glyphs by default + 'custom' + A generic Unicode font, in which case the mathtext font must be + specified using mathtext.bf, mathtext.it, mathtext.sf etc. + + Added a new example, stix_fonts_demo.py to show how to access different + fonts and unusual symbols. - MGD + +2007-11-12 + Options to disable building backend extension modules moved from setup.py + to setup.cfg - DSD + +2007-11-09 + Applied Martin Teichmann's patch 1828813: a QPainter is used in paintEvent, + which has to be destroyed using the method end(). If matplotlib raises an + exception before the call to end - and it does if you feed it with bad data + - this method end() is never called and Qt4 will start spitting error + messages + +2007-11-09 + Moved pyparsing back into matplotlib namespace. Don't use system pyparsing, + API is too variable from one release to the next - DSD + +2007-11-08 + Made pylab use straight numpy instead of oldnumeric by default - EF + +2007-11-08 + Added additional record array utilities to mlab (rec2excel, rec2gtk, + rec_join, rec_append_field, rec_drop_field) - JDH + +2007-11-08 + Updated pytz to version 2007g - DSD -2007-11-08 Updated pytz to version 2007g - DSD +2007-11-08 + Updated pyparsing to version 1.4.8 - DSD + +2007-11-08 + Moved csv2rec to recutils and added other record array utilities - JDH + +2007-11-08 + If available, use existing pyparsing installation - DSD + +2007-11-07 + Removed old enthought.traits from lib/matplotlib, added Gael Varoquaux's + enthought.traits-2.6b1, which is stripped of setuptools. The package is + installed to site-packages if not already available - DSD + +2007-11-05 + Added easy access to minor tick properties; slight mod of patch by Pierre + G-M - EF + +2007-11-02 + Committed Phil Thompson's patch 1599876, fixes to Qt4Agg backend and qt4 + blitting demo - DSD + +2007-11-02 + Committed Phil Thompson's patch 1599876, fixes to Qt4Agg backend and qt4 + blitting demo - DSD + +2007-10-31 + Made log color scale easier to use with contourf; automatic level + generation now works. - EF + +2007-10-29 + TRANSFORMS REFACTORING + + The primary goal of this refactoring was to make it easier to extend + matplotlib to support new kinds of projections. This is primarily an + internal improvement, and the possible user-visible changes it allows are + yet to come. + + The transformation framework was completely rewritten in Python (with + Numpy). This will make it easier to add news kinds of transformations + without writing C/C++ code. + + Transforms are composed into a 'transform tree', made of transforms whose + value depends on other transforms (their children). When the contents of + children change, their parents are automatically updated to reflect those + changes. To do this an "invalidation" method is used: when children + change, all of their ancestors are marked as "invalid". When the value of + a transform is accessed at a later time, its value is recomputed only if it + is invalid, otherwise a cached value may be used. This prevents + unnecessary recomputations of transforms, and contributes to better + interactive performance. + + The framework can be used for both affine and non-affine transformations. + However, for speed, we want use the backend renderers to perform affine + transformations whenever possible. Therefore, it is possible to perform + just the affine or non-affine part of a transformation on a set of data. + The affine is always assumed to occur after the non-affine. For any + transform:: + + full transform == non-affine + affine + + Much of the drawing has been refactored in terms of compound paths. + Therefore, many methods have been removed from the backend interface and + replaced with a handful to draw compound paths. This will make updating + the backends easier, since there is less to update. It also should make + the backends more consistent in terms of functionality. + + User visible changes: + + - POLAR PLOTS: Polar plots are now interactively zoomable, and the r-axis + labels can be interactively rotated. Straight line segments are now + interpolated to follow the curve of the r-axis. + + - Non-rectangular clipping works in more backends and with more types of + objects. + + - Sharing an axis across figures is now done in exactly the same way as + sharing an axis between two axes in the same figure:: + + fig1 = figure() + fig2 = figure() + + ax1 = fig1.add_subplot(111) + ax2 = fig2.add_subplot(111, sharex=ax1, sharey=ax1) -2007-11-08 Updated pyparsing to version 1.4.8 - DSD + - linestyles now include steps-pre, steps-post and steps-mid. The old step + still works and is equivalent to step-pre. -2007-11-08 Moved csv2rec to recutils and added other record array - utilities - JDH + - Multiple line styles may be provided to a collection. -2007-11-08 If available, use existing pyparsing installation - DSD + See API_CHANGES for more low-level information about this refactoring. -2007-11-07 Removed old enthought.traits from lib/matplotlib, added - Gael Varoquaux's enthought.traits-2.6b1, which is stripped - of setuptools. The package is installed to site-packages - if not already available - DSD +2007-10-24 + Added ax kwarg to Figure.colorbar and pyplot.colorbar - EF -2007-11-05 Added easy access to minor tick properties; slight mod - of patch by Pierre G-M - EF +2007-10-19 + Removed a gsave/grestore pair surrounding _draw_ps, which was causing a + loss graphics state info (see "EPS output problem - scatter & edgecolors" + on mpl-dev, 2007-10-29) - DSD -2007-11-02 Committed Phil Thompson's patch 1599876, fixes to Qt4Agg - backend and qt4 blitting demo - DSD +2007-10-15 + Fixed a bug in patches.Ellipse that was broken for aspect='auto'. Scale + free ellipses now work properly for equal and auto on Agg and PS, and they + fall back on a polygonal approximation for nonlinear transformations until + we convince ourselves that the spline approximation holds for nonlinear + transformations. Added unit/ellipse_compare.py to compare spline with + vertex approx for both aspects. JDH + +2007-10-05 + remove generator expressions from texmanager and mpltraits. generator + expressions are not supported by python-2.3 - DSD + +2007-10-01 + Made matplotlib.use() raise an exception if called after backends has been + imported. - EF + +2007-09-30 + Modified update* methods of Bbox and Interval so they work with reversed + axes. Prior to this, trying to set the ticks on a reversed axis failed + with an uninformative error message. - EF + +2007-09-30 + Applied patches to axes3d to fix index error problem - EF + +2007-09-24 + Applied Eike Welk's patch reported on mpl-dev on 2007-09-22 Fixes a bug + with multiple plot windows in the qt backend, ported the changes to + backend_qt4 as well - DSD + +2007-09-21 + Changed cbook.reversed to yield the same result as the python reversed + builtin - DSD + +2007-09-13 + The usetex support in the pdf backend is more usable now, so I am enabling + it. - JKS + +2007-09-12 + Fixed a Axes.bar unit bug - JDH + +2007-09-10 + Made skiprows=1 the default on csv2rec - JDH + +2007-09-09 + Split out the plotting part of pylab and put it in pyplot.py; removed + numerix from the remaining pylab.py, which imports everything from + pyplot.py. The intention is that apart from cleanups, the result of + importing from pylab is nearly unchanged, but there is the new alternative + of importing from pyplot to get the state-engine graphics without all the + numeric functions. Numpified examples; deleted two that were obsolete; + modified some to use pyplot. - EF -2007-11-02 Committed Phil Thompson's patch 1599876, fixes to Qt4Agg - backend and qt4 blitting demo - DSD +2007-09-08 + Eliminated gd and paint backends - EF -2007-10-31 Made log color scale easier to use with contourf; - automatic level generation now works. - EF +2007-09-06 + .bmp file format is now longer an alias for .raw -2007-10-29 TRANSFORMS REFACTORING +2007-09-07 + Added clip path support to pdf backend. - JKS - The primary goal of this refactoring was to make it easier - to extend matplotlib to support new kinds of projections. - This is primarily an internal improvement, and the possible - user-visible changes it allows are yet to come. +2007-09-06 + Fixed a bug in the embedding of Type 1 fonts in PDF. Now it doesn't crash + Preview.app. - JKS - The transformation framework was completely rewritten in - Python (with Numpy). This will make it easier to add news - kinds of transformations without writing C/C++ code. +2007-09-06 + Refactored image saving code so that all GUI backends can save most image + types. See FILETYPES for a matrix of backends and their supported file + types. Backend canvases should no longer write their own print_figure() + method -- instead they should write a print_xxx method for each filetype + they can output and add an entry to their class-scoped filetypes + dictionary. - MGD - Transforms are composed into a 'transform tree', made of - transforms whose value depends on other transforms (their - children). When the contents of children change, their - parents are automatically updated to reflect those changes. - To do this an "invalidation" method is used: when children - change, all of their ancestors are marked as "invalid". - When the value of a transform is accessed at a later time, - its value is recomputed only if it is invalid, otherwise a - cached value may be used. This prevents unnecessary - recomputations of transforms, and contributes to better - interactive performance. +2007-09-05 + Fixed Qt version reporting in setupext.py - DSD - The framework can be used for both affine and non-affine - transformations. However, for speed, we want use the - backend renderers to perform affine transformations - whenever possible. Therefore, it is possible to perform - just the affine or non-affine part of a transformation on a - set of data. The affine is always assumed to occur after - the non-affine. For any transform: +2007-09-04 + Embedding Type 1 fonts in PDF, and thus usetex support via dviread, sort of + works. To test, enable it by renaming _draw_tex to draw_tex. - JKS - full transform == non-affine + affine +2007-09-03 + Added ability of errorbar show limits via caret or arrowhead ends on the + bars; patch by Manual Metz. - EF - Much of the drawing has been refactored in terms of - compound paths. Therefore, many methods have been removed - from the backend interface and replaced with a a handful to - draw compound paths. This will make updating the backends - easier, since there is less to update. It also should make - the backends more consistent in terms of functionality. +2007-09-03 + Created type1font.py, added features to AFM and FT2Font (see API_CHANGES), + started work on embedding Type 1 fonts in pdf files. - JKS - User visible changes: +2007-09-02 + Continued work on dviread.py. - JKS - - POLAR PLOTS: Polar plots are now interactively zoomable, - and the r-axis labels can be interactively rotated. - Straight line segments are now interpolated to follow the - curve of the r-axis. +2007-08-16 + Added a set_extent method to AxesImage, allow data extent to be modified + after initial call to imshow - DSD - - Non-rectangular clipping works in more backends and with - more types of objects. +2007-08-14 + Fixed a bug in pyqt4 subplots-adjust. Thanks to Xavier Gnata for the report + and suggested fix - DSD - - Sharing an axis across figures is now done in exactly - the same way as sharing an axis between two axes in the - same figure:: +2007-08-13 + Use pickle to cache entire fontManager; change to using font_manager + module-level function findfont wrapper for the fontManager.findfont method + - EF - fig1 = figure() - fig2 = figure() +2007-08-11 + Numpification and cleanup of mlab.py and some examples - EF - ax1 = fig1.add_subplot(111) - ax2 = fig2.add_subplot(111, sharex=ax1, sharey=ax1) +2007-08-06 + Removed mathtext2 - - linestyles now include steps-pre, steps-post and - steps-mid. The old step still works and is equivalent to - step-pre. +2007-07-31 + Refactoring of distutils scripts. - - Multiple line styles may be provided to a collection. + - Will not fail on the entire build if an optional Python package (e.g., + Tkinter) is installed but its development headers are not (e.g., + tk-devel). Instead, it will continue to build all other extensions. + - Provide an overview at the top of the output to display what dependencies + and their versions were found, and (by extension) what will be built. + - Use pkg-config, when available, to find freetype2, since this was broken + on Mac OS-X when using MacPorts in a non- standard location. - See API_CHANGES for more low-level information about this - refactoring. +2007-07-30 + Reorganized configuration code to work with traited config objects. The new + config system is located in the matplotlib.config package, but it is + disabled by default. To enable it, set NEWCONFIG=True in + matplotlib.__init__.py. The new configuration system will still use the + old matplotlibrc files by default. To switch to the experimental, traited + configuration, set USE_TRAITED_CONFIG=True in config.__init__.py. -2007-10-24 Added ax kwarg to Figure.colorbar and pyplot.colorbar - EF +2007-07-29 + Changed default pcolor shading to flat; added aliases to make collection + kwargs agree with setter names, so updating works; related minor cleanups. + Removed quiver_classic, scatter_classic, pcolor_classic. - EF -2007-10-19 Removed a gsave/grestore pair surrounding _draw_ps, which - was causing a loss graphics state info (see "EPS output - problem - scatter & edgecolors" on mpl-dev, 2007-10-29) - - DSD +2007-07-26 + Major rewrite of mathtext.py, using the TeX box layout model. -2007-10-15 Fixed a bug in patches.Ellipse that was broken for - aspect='auto'. Scale free ellipses now work properly for - equal and auto on Agg and PS, and they fall back on a - polygonal approximation for nonlinear transformations until - we convince oursleves that the spline approximation holds - for nonlinear transformations. Added - unit/ellipse_compare.py to compare spline with vertex - approx for both aspects. JDH + There is one (known) backward incompatible change. The font commands + (\cal, \rm, \it, \tt) now behave as TeX does: they are in effect until the + next font change command or the end of the grouping. Therefore uses of + $\cal{R}$ should be changed to ${\cal R}$. Alternatively, you may use the + new LaTeX-style font commands (\mathcal, \mathrm, \mathit, \mathtt) which + do affect the following group, e.g., $\mathcal{R}$. -2007-10-05 remove generator expressions from texmanager and mpltraits. - generator expressions are not supported by python-2.3 - DSD + Other new features include: -2007-10-01 Made matplotlib.use() raise an exception if called after - backends has been imported. - EF + - Math may be interspersed with non-math text. Any text with an even + number of $'s (non-escaped) will be sent to the mathtext parser for + layout. -2007-09-30 Modified update* methods of Bbox and Interval so they - work with reversed axes. Prior to this, trying to - set the ticks on a reversed axis failed with an - uninformative error message. - EF - -2007-09-30 Applied patches to axes3d to fix index error problem - EF - -2007-09-24 Applied Eike Welk's patch reported on mpl-dev on 2007-09-22 - Fixes a bug with multiple plot windows in the qt backend, - ported the changes to backend_qt4 as well - DSD - -2007-09-21 Changed cbook.reversed to yield the same result as the - python reversed builtin - DSD - -2007-09-13 The usetex support in the pdf backend is more usable now, - so I am enabling it. - JKS - -2007-09-12 Fixed a Axes.bar unit bug - JDH - -2007-09-10 Made skiprows=1 the default on csv2rec - JDH - -2007-09-09 Split out the plotting part of pylab and put it in - pyplot.py; removed numerix from the remaining pylab.py, - which imports everything from pyplot.py. The intention - is that apart from cleanups, the result of importing - from pylab is nearly unchanged, but there is the - new alternative of importing from pyplot to get - the state-engine graphics without all the numeric - functions. - Numpified examples; deleted two that were obsolete; - modified some to use pyplot. - EF + - Sub/superscripts are less likely to accidentally overlap. -2007-09-08 Eliminated gd and paint backends - EF + - Support for sub/superscripts in either order, e.g., $x^i_j$ and $x_j^i$ + are equivalent. -2007-09-06 .bmp file format is now longer an alias for .raw + - Double sub/superscripts (e.g., $x_i_j$) are considered ambiguous and + raise an exception. Use braces to disambiguate. -2007-09-07 Added clip path support to pdf backend. - JKS + - $\frac{x}{y}$ can be used for displaying fractions. -2007-09-06 Fixed a bug in the embedding of Type 1 fonts in PDF. - Now it doesn't crash Preview.app. - JKS + - $\sqrt[3]{x}$ can be used to display the radical symbol with a root + number and body. -2007-09-06 Refactored image saving code so that all GUI backends can - save most image types. See FILETYPES for a matrix of - backends and their supported file types. - Backend canvases should no longer write their own print_figure() - method -- instead they should write a print_xxx method for - each filetype they can output and add an entry to their - class-scoped filetypes dictionary. - MGD + - $\left(\frac{x}{y}\right)$ may be used to create parentheses and other + delimiters that automatically resize to the height of their contents. -2007-09-05 Fixed Qt version reporting in setupext.py - DSD + - Spacing around operators etc. is now generally more like TeX. -2007-09-04 Embedding Type 1 fonts in PDF, and thus usetex support - via dviread, sort of works. To test, enable it by - renaming _draw_tex to draw_tex. - JKS + - Added support (and fonts) for boldface (\bf) and sans-serif (\sf) + symbols. -2007-09-03 Added ability of errorbar show limits via caret or - arrowhead ends on the bars; patch by Manual Metz. - EF + - Log-like function name shortcuts are supported. For example, $\sin(x)$ + may be used instead of ${\rm sin}(x)$ -2007-09-03 Created type1font.py, added features to AFM and FT2Font - (see API_CHANGES), started work on embedding Type 1 fonts - in pdf files. - JKS + - Limited use of kerning for the easy case (same font) -2007-09-02 Continued work on dviread.py. - JKS + Behind the scenes, the pyparsing.py module used for doing the math parsing + was updated to the latest stable version (1.4.6). A lot of duplicate code + was refactored out of the Font classes. -2007-08-16 Added a set_extent method to AxesImage, allow data extent - to be modified after initial call to imshow - DSD + - MGD -2007-08-14 Fixed a bug in pyqt4 subplots-adjust. Thanks to - Xavier Gnata for the report and suggested fix - DSD +2007-07-19 + completed numpification of most trivial cases - NN -2007-08-13 Use pickle to cache entire fontManager; change to using - font_manager module-level function findfont wrapper for - the fontManager.findfont method - EF +2007-07-19 + converted non-numpy relicts throughout the code - NN -2007-08-11 Numpification and cleanup of mlab.py and some examples - EF +2007-07-19 + replaced the Python code in numerix/ by a minimal wrapper around numpy that + explicitly mentions all symbols that need to be addressed for further + numpification - NN -2007-08-06 Removed mathtext2 +2007-07-18 + make usetex respect changes to rcParams. texmanager used to only configure + itself when it was created, now it reconfigures when rcParams are changed. + Thank you Alexander Schmolck for contributing a patch - DSD -2007-07-31 Refactoring of distutils scripts. - - Will not fail on the entire build if an optional Python - package (e.g., Tkinter) is installed but its development - headers are not (e.g., tk-devel). Instead, it will - continue to build all other extensions. - - Provide an overview at the top of the output to display - what dependencies and their versions were found, and (by - extension) what will be built. - - Use pkg-config, when available, to find freetype2, since - this was broken on Mac OS-X when using MacPorts in a non- - standard location. +2007-07-17 + added validation to setting and changing rcParams - DSD -2007-07-30 Reorganized configuration code to work with traited config - objects. The new config system is located in the - matplotlib.config package, but it is disabled by default. - To enable it, set NEWCONFIG=True in matplotlib.__init__.py. - The new configuration system will still use the old - matplotlibrc files by default. To switch to the experimental, - traited configuration, set USE_TRAITED_CONFIG=True in - config.__init__.py. +2007-07-17 + bugfix segfault in transforms module. Thanks Ben North for the patch. - ADS -2007-07-29 Changed default pcolor shading to flat; added aliases - to make collection kwargs agree with setter names, so - updating works; related minor cleanups. - Removed quiver_classic, scatter_classic, pcolor_classic. - EF +2007-07-16 + clean up some code in ticker.ScalarFormatter, use unicode to render + multiplication sign in offset ticklabel - DSD -2007-07-26 Major rewrite of mathtext.py, using the TeX box layout model. +2007-07-16 + fixed a formatting bug in ticker.ScalarFormatter's scientific notation + (10^0 was being rendered as 10 in some cases) - DSD - There is one (known) backward incompatible change. The - font commands (\cal, \rm, \it, \tt) now behave as TeX does: - they are in effect until the next font change command or - the end of the grouping. Therefore uses of $\cal{R}$ - should be changed to ${\cal R}$. Alternatively, you may - use the new LaTeX-style font commands (\mathcal, \mathrm, - \mathit, \mathtt) which do affect the following group, - e.g., $\mathcal{R}$. +2007-07-13 + Add MPL_isfinite64() and MPL_isinf64() for testing doubles in (the now + misnamed) MPL_isnan.h. - ADS - Other new features include: +2007-07-13 + The matplotlib._isnan module removed (use numpy.isnan) - ADS - - Math may be interspersed with non-math text. Any text - with an even number of $'s (non-escaped) will be sent to - the mathtext parser for layout. +2007-07-13 + Some minor cleanups in _transforms.cpp - ADS - - Sub/superscripts are less likely to accidentally overlap. +2007-07-13 + Removed the rest of the numerix extension code detritus, numpified axes.py, + and cleaned up the imports in axes.py - JDH - - Support for sub/superscripts in either order, e.g., $x^i_j$ - and $x_j^i$ are equivalent. +2007-07-13 + Added legend.loc as configurable option that could in future default to + 'best'. - NN - - Double sub/superscripts (e.g., $x_i_j$) are considered - ambiguous and raise an exception. Use braces to disambiguate. +2007-07-12 + Bugfixes in mlab.py to coerce inputs into numpy arrays. -ADS - - $\frac{x}{y}$ can be used for displaying fractions. +2007-07-11 + Added linespacing kwarg to text.Text - EF - - $\sqrt[3]{x}$ can be used to display the radical symbol - with a root number and body. +2007-07-11 + Added code to store font paths in SVG files. - MGD - - $\left(\frac{x}{y}\right)$ may be used to create - parentheses and other delimiters that automatically - resize to the height of their contents. +2007-07-10 + Store subset of TTF font as a Type 3 font in PDF files. - MGD - - Spacing around operators etc. is now generally more like - TeX. +2007-07-09 + Store subset of TTF font as a Type 3 font in PS files. - MGD - - Added support (and fonts) for boldface (\bf) and - sans-serif (\sf) symbols. +2007-07-09 + Applied Paul's pick restructure pick and add pickers, sourceforge patch + 1749829 - JDH - - Log-like function name shortcuts are supported. For - example, $\sin(x)$ may be used instead of ${\rm sin}(x)$ +2007-07-09 + Applied Allan's draw_lines agg optimization. JDH - - Limited use of kerning for the easy case (same font) +2007-07-08 + Applied Carl Worth's patch to fix cairo draw_arc - SC - Behind the scenes, the pyparsing.py module used for doing - the math parsing was updated to the latest stable version - (1.4.6). A lot of duplicate code was refactored out of the - Font classes. +2007-07-07 + fixed bug 1712099: xpdf distiller on windows - DSD - - MGD +2007-06-30 + Applied patches to tkagg, gtk, and wx backends to reduce memory leakage. + Patches supplied by Mike Droettboom; see tracker numbers 1745400, 1745406, + 1745408. Also made unit/memleak_gui.py more flexible with command-line + options. - EF -2007-07-19 completed numpification of most trivial cases - NN +2007-06-30 + Split defaultParams into separate file rcdefaults (together with validation + code). Some heavy refactoring was necessary to do so, but the overall + behavior should be the same as before. - NN -2007-07-19 converted non-numpy relicts throughout the code - NN +2007-06-27 + Added MPLCONFIGDIR for the default location for mpl data and configuration. + useful for some apache installs where HOME is not writable. Tried to clean + up the logic in _get_config_dir to support non-writable HOME where are + writable HOME/.matplotlib already exists - JDH -2007-07-19 replaced the Python code in numerix/ by a minimal wrapper around - numpy that explicitly mentions all symbols that need to be - addressed for further numpification - NN +2007-06-27 + Fixed locale bug reported at + \http://sourceforge.net/tracker/index.php?func=detail&aid=1744154&group_id=80706&atid=560720 + by adding a cbook.unicode_safe function - JDH -2007-07-18 make usetex respect changes to rcParams. texmanager used to - only configure itself when it was created, now it - reconfigures when rcParams are changed. Thank you Alexander - Schmolck for contributing a patch - DSD +2007-06-27 + Applied Micheal's tk savefig bugfix described at + \http://sourceforge.net/tracker/index.php?func=detail&aid=1716732&group_id=80706&atid=560720 + Thanks Michael! -2007-07-17 added validation to setting and changing rcParams - DSD +2007-06-27 + Patch for get_py2exe_datafiles() to work with new directory layout. (Thanks + Tocer and also Werner Bruhin.) -ADS -2007-07-17 bugfix segfault in transforms module. Thanks Ben North for - the patch. - ADS +2007-06-27 + Added a scroll event to the mpl event handling system and implemented it + for backends GTK* -- other backend users/developers/maintainers, please add + support for your backend. - JDH -2007-07-16 clean up some code in ticker.ScalarFormatter, use unicode to - render multiplication sign in offset ticklabel - DSD +2007-06-25 + Changed default to clip=False in colors.Normalize; modified ColorbarBase + for easier colormap display - EF -2007-07-16 fixed a formatting bug in ticker.ScalarFormatter's scientific - notation (10^0 was being rendered as 10 in some cases) - DSD +2007-06-13 + Added maskedarray option to rc, numerix - EF -2007-07-13 Add MPL_isfinite64() and MPL_isinf64() for testing - doubles in (the now misnamed) MPL_isnan.h. - ADS +2007-06-11 + Python 2.5 compatibility fix for mlab.py - EF -2007-07-13 The matplotlib._isnan module removed (use numpy.isnan) - ADS +2007-06-10 + In matplotlibrc file, use 'dashed' | 'solid' instead of a pair of floats + for contour.negative_linestyle - EF -2007-07-13 Some minor cleanups in _transforms.cpp - ADS +2007-06-08 + Allow plot and fill fmt string to be any mpl string colorspec - EF -2007-07-13 Removed the rest of the numerix extension code detritus, - numpified axes.py, and cleaned up the imports in axes.py - - JDH +2007-06-08 + Added gnuplot file plotfile function to pylab -- see + examples/plotfile_demo.py - JDH -2007-07-13 Added legend.loc as configurable option that could in - future default to 'best'. - NN +2007-06-07 + Disable build of numarray and Numeric extensions for internal MPL use and + the numerix layer. - ADS -2007-07-12 Bugfixes in mlab.py to coerce inputs into numpy arrays. -ADS +2007-06-07 + Added csv2rec to matplotlib.mlab to support automatically converting csv + files to record arrays using type introspection, and turned on native + datetime support using the new units support in matplotlib.dates. See + examples/loadrec.py ! JDH -2007-07-11 Added linespacing kwarg to text.Text - EF +2007-06-07 + Simplified internal code of _auto_legend_data - NN -2007-07-11 Added code to store font paths in SVG files. - MGD +2007-06-04 + Added labeldistance arg to Axes.pie to control the raidal distance of the + wedge labels - JDH -2007-07-10 Store subset of TTF font as a Type 3 font in PDF files. - MGD - -2007-07-09 Store subset of TTF font as a Type 3 font in PS files. - MGD - -2007-07-09 Applied Paul's pick restructure pick and add pickers, - sourceforge patch 1749829 - JDH - - -2007-07-09 Applied Allan's draw_lines agg optimization. JDH - - -2007-07-08 Applied Carl Worth's patch to fix cairo draw_arc - SC - -2007-07-07 fixed bug 1712099: xpdf distiller on windows - DSD - -2007-06-30 Applied patches to tkagg, gtk, and wx backends to reduce - memory leakage. Patches supplied by Mike Droettboom; - see tracker numbers 1745400, 1745406, 1745408. - Also made unit/memleak_gui.py more flexible with - command-line options. - EF - -2007-06-30 Split defaultParams into separate file rcdefaults (together with - validation code). Some heavy refactoring was necessary to do so, - but the overall behavior should be the same as before. - NN - -2007-06-27 Added MPLCONFIGDIR for the default location for mpl data - and configuration. useful for some apache installs where - HOME is not writable. Tried to clean up the logic in - _get_config_dir to support non-writable HOME where are - writable HOME/.matplotlib already exists - JDH - -2007-06-27 Fixed locale bug reported at - \http://sourceforge.net/tracker/index.php?func=detail&aid=1744154&group_id=80706&atid=560720 - by adding a cbook.unicode_safe function - JDH - -2007-06-27 Applied Micheal's tk savefig bugfix described at - \http://sourceforge.net/tracker/index.php?func=detail&aid=1716732&group_id=80706&atid=560720 - Thanks Michael! - - -2007-06-27 Patch for get_py2exe_datafiles() to work with new directory - layout. (Thanks Tocer and also Werner Bruhin.) -ADS - - -2007-06-27 Added a scroll event to the mpl event handling system and - implemented it for backends GTK* -- other backend - users/developers/maintainers, please add support for your - backend. - JDH - -2007-06-25 Changed default to clip=False in colors.Normalize; - modified ColorbarBase for easier colormap display - EF - -2007-06-13 Added maskedarray option to rc, numerix - EF - -2007-06-11 Python 2.5 compatibility fix for mlab.py - EF - -2007-06-10 In matplotlibrc file, use 'dashed' | 'solid' instead - of a pair of floats for contour.negative_linestyle - EF - -2007-06-08 Allow plot and fill fmt string to be any mpl string - colorspec - EF - -2007-06-08 Added gnuplot file plotfile function to pylab -- see - examples/plotfile_demo.py - JDH - -2007-06-07 Disable build of numarray and Numeric extensions for - internal MPL use and the numerix layer. - ADS - -2007-06-07 Added csv2rec to matplotlib.mlab to support automatically - converting csv files to record arrays using type - introspection, and turned on native datetime support using - the new units support in matplotlib.dates. See - examples/loadrec.py ! JDH - -2007-06-07 Simplified internal code of _auto_legend_data - NN - -2007-06-04 Added labeldistance arg to Axes.pie to control the raidal - distance of the wedge labels - JDH - -2007-06-03 Turned mathtext in SVG into single with multiple - objects (easier to edit in inkscape). - NN +2007-06-03 + Turned mathtext in SVG into single with multiple objects + (easier to edit in inkscape). - NN ---------------------------- -2007-06-02 Released 0.90.1 at revision 3352 - -2007-06-02 Display only meaningful labels when calling legend() - without args. - NN - -2007-06-02 Have errorbar follow the color cycle even if line is not plotted. - Suppress plotting of errorbar caps for capsize=0. - NN - -2007-06-02 Set markers to same alpha value as line. - NN - -2007-06-02 Fix mathtext position in svg backend. - NN - -2007-06-01 Deprecate Numeric and numarray for use as numerix. Props to - Travis -- job well done. - ADS +2007-06-02 + Released 0.90.1 at revision 3352 -2007-05-18 Added LaTeX unicode support. Enable with the - 'text.latex.unicode' rcParam. This requires the ucs and - inputenc LaTeX packages. - ADS +2007-06-02 + Display only meaningful labels when calling legend() without args. - NN -2007-04-23 Fixed some problems with polar -- added general polygon - clipping to clip the lines and grids to the polar axes. - Added support for set_rmax to easily change the maximum - radial grid. Added support for polar legend - JDH +2007-06-02 + Have errorbar follow the color cycle even if line is not plotted. Suppress + plotting of errorbar caps for capsize=0. - NN -2007-04-16 Added Figure.autofmt_xdate to handle adjusting the bottom - and rotating the tick labels for date plots when the ticks - often overlap - JDH +2007-06-02 + Set markers to same alpha value as line. - NN -2007-04-09 Beginnings of usetex support for pdf backend. -JKS +2007-06-02 + Fix mathtext position in svg backend. - NN -2007-04-07 Fixed legend/LineCollection bug. Added label support - to collections. - EF +2007-06-01 + Deprecate Numeric and numarray for use as numerix. Props to Travis -- job + well done. - ADS -2007-04-06 Removed deprecated support for a float value as a gray-scale; - now it must be a string, like '0.5'. Added alpha kwarg to - ColorConverter.to_rgba_list. - EF +2007-05-18 + Added LaTeX unicode support. Enable with the 'text.latex.unicode' rcParam. + This requires the ucs and inputenc LaTeX packages. - ADS -2007-04-06 Fixed rotation of ellipses in pdf backend - (sf bug #1690559) -JKS +2007-04-23 + Fixed some problems with polar -- added general polygon clipping to clip + the lines and grids to the polar axes. Added support for set_rmax to + easily change the maximum radial grid. Added support for polar legend - + JDH -2007-04-04 More matshow tweaks; documentation updates; new method - set_bounds() for formatters and locators. - EF +2007-04-16 + Added Figure.autofmt_xdate to handle adjusting the bottom and rotating the + tick labels for date plots when the ticks often overlap - JDH -2007-04-02 Fixed problem with imshow and matshow of integer arrays; - fixed problems with changes to color autoscaling. - EF +2007-04-09 + Beginnings of usetex support for pdf backend. -JKS -2007-04-01 Made image color autoscaling work correctly with - a tracking colorbar; norm.autoscale now scales - unconditionally, while norm.autoscale_None changes - only None-valued vmin, vmax. - EF +2007-04-07 + Fixed legend/LineCollection bug. Added label support to collections. - EF -2007-03-31 Added a qt-based subplot-adjustment dialog - DSD +2007-04-06 + Removed deprecated support for a float value as a gray-scale; now it must + be a string, like '0.5'. Added alpha kwarg to ColorConverter.to_rgba_list. + - EF -2007-03-30 Fixed a bug in backend_qt4, reported on mpl-dev - DSD +2007-04-06 + Fixed rotation of ellipses in pdf backend (sf bug #1690559) -JKS -2007-03-26 Removed colorbar_classic from figure.py; fixed bug in - Figure.clf() in which _axobservers was not getting - cleared. Modernization and cleanups. - EF +2007-04-04 + More matshow tweaks; documentation updates; new method set_bounds() for + formatters and locators. - EF -2007-03-26 Refactored some of the units support -- units now live in - the respective x and y Axis instances. See also - API_CHANGES for some alterations to the conversion - interface. JDH +2007-04-02 + Fixed problem with imshow and matshow of integer arrays; fixed problems + with changes to color autoscaling. - EF -2007-03-25 Fix masked array handling in quiver.py for numpy. (Numeric - and numarray support for masked arrays is broken in other - ways when using quiver. I didn't pursue that.) - ADS +2007-04-01 + Made image color autoscaling work correctly with a tracking colorbar; + norm.autoscale now scales unconditionally, while norm.autoscale_None + changes only None-valued vmin, vmax. - EF -2007-03-23 Made font_manager.py close opened files. - JKS +2007-03-31 + Added a qt-based subplot-adjustment dialog - DSD -2007-03-22 Made imshow default extent match matshow - EF +2007-03-30 + Fixed a bug in backend_qt4, reported on mpl-dev - DSD -2007-03-22 Some more niceties for xcorr -- a maxlags option, normed - now works for xcorr as well as axorr, usevlines is - supported, and a zero correlation hline is added. See - examples/xcorr_demo.py. Thanks Sameer for the patch. - - JDH +2007-03-26 + Removed colorbar_classic from figure.py; fixed bug in Figure.clear() in which + _axobservers was not getting cleared. Modernization and cleanups. - EF -2007-03-21 Axes.vlines and Axes.hlines now create and returns a - LineCollection, not a list of lines. This is much faster. - The kwarg signature has changed, so consult the docs. - Modified Axes.errorbar which uses vlines and hlines. See - API_CHANGES; the return signature for these three functions - is now different +2007-03-26 + Refactored some of the units support -- units now live in the respective x + and y Axis instances. See also API_CHANGES for some alterations to the + conversion interface. JDH -2007-03-20 Refactored units support and added new examples - JDH +2007-03-25 + Fix masked array handling in quiver.py for numpy. (Numeric and numarray + support for masked arrays is broken in other ways when using quiver. I + didn't pursue that.) - ADS -2007-03-19 Added Mike's units patch - JDH +2007-03-23 + Made font_manager.py close opened files. - JKS -2007-03-18 Matshow as an Axes method; test version matshow1() in - pylab; added 'integer' Boolean kwarg to MaxNLocator - initializer to force ticks at integer locations. - EF +2007-03-22 + Made imshow default extent match matshow - EF -2007-03-17 Preliminary support for clipping to paths agg - JDH +2007-03-22 + Some more niceties for xcorr -- a maxlags option, normed now works for + xcorr as well as axorr, usevlines is supported, and a zero correlation + hline is added. See examples/xcorr_demo.py. Thanks Sameer for the patch. + - JDH -2007-03-17 Text.set_text() accepts anything convertible with '%s' - EF +2007-03-21 + Axes.vlines and Axes.hlines now create and returns a LineCollection, not a + list of lines. This is much faster. The kwarg signature has changed, so + consult the docs. Modified Axes.errorbar which uses vlines and hlines. + See API_CHANGES; the return signature for these three functions is now + different -2007-03-14 Add masked-array support to hist. - EF +2007-03-20 + Refactored units support and added new examples - JDH -2007-03-03 Change barh to take a kwargs dict and pass it to bar. - Fixes sf bug #1669506. +2007-03-19 + Added Mike's units patch - JDH -2007-03-02 Add rc parameter pdf.inheritcolor, which disables all - color-setting operations in the pdf backend. The idea is - that you include the resulting file in another program and - set the colors (both stroke and fill color) there, so you - can use the same pdf file for e.g., a paper and a - presentation and have them in the surrounding color. You - will probably not want to draw figure and axis frames in - that case, since they would be filled in the same color. - JKS +2007-03-18 + Matshow as an Axes method; test version matshow1() in pylab; added + 'integer' Boolean kwarg to MaxNLocator initializer to force ticks at + integer locations. - EF -2007-02-26 Prevent building _wxagg.so with broken Mac OS X wxPython. - ADS +2007-03-17 + Preliminary support for clipping to paths agg - JDH -2007-02-23 Require setuptools for Python 2.3 - ADS +2007-03-17 + Text.set_text() accepts anything convertible with '%s' - EF -2007-02-22 WXAgg accelerator updates - KM - WXAgg's C++ accelerator has been fixed to use the correct wxBitmap - constructor. +2007-03-14 + Add masked-array support to hist. - EF - The backend has been updated to use new wxPython functionality to - provide fast blit() animation without the C++ accelerator. This - requires wxPython 2.8 or later. Previous versions of wxPython can - use the C++ acclerator or the old pure Python routines. +2007-03-03 + Change barh to take a kwargs dict and pass it to bar. Fixes sf bug + #1669506. - setup.py no longer builds the C++ accelerator when wxPython >= 2.8 - is present. +2007-03-02 + Add rc parameter pdf.inheritcolor, which disables all color-setting + operations in the pdf backend. The idea is that you include the resulting + file in another program and set the colors (both stroke and fill color) + there, so you can use the same pdf file for e.g., a paper and a + presentation and have them in the surrounding color. You will probably not + want to draw figure and axis frames in that case, since they would be + filled in the same color. - JKS - The blit() method is now faster regardless of which agg/wxPython - conversion routines are used. +2007-02-26 + Prevent building _wxagg.so with broken Mac OS X wxPython. - ADS -2007-02-21 Applied the PDF backend patch by Nicolas Grilly. - This impacts several files and directories in matplotlib: +2007-02-23 + Require setuptools for Python 2.3 - ADS - - Created the directory lib/matplotlib/mpl-data/fonts/pdfcorefonts, - holding AFM files for the 14 PDF core fonts. These fonts are - embedded in every PDF viewing application. +2007-02-22 + WXAgg accelerator updates - KM - - setup.py: Added the directory pdfcorefonts to package_data. + WXAgg's C++ accelerator has been fixed to use the correct wxBitmap + constructor. - - lib/matplotlib/__init__.py: Added the default parameter - 'pdf.use14corefonts'. When True, the PDF backend uses - only the 14 PDF core fonts. + The backend has been updated to use new wxPython functionality to provide + fast blit() animation without the C++ accelerator. This requires wxPython + 2.8 or later. Previous versions of wxPython can use the C++ accelerator or + the old pure Python routines. - - lib/matplotlib/afm.py: Added some keywords found in - recent AFM files. Added a little workaround to handle - Euro symbol. + setup.py no longer builds the C++ accelerator when wxPython >= 2.8 is + present. - - lib/matplotlib/fontmanager.py: Added support for the 14 - PDF core fonts. These fonts have a dedicated cache (file - pdfcorefont.cache), not the same as for other AFM files - (file .afmfont.cache). Also cleaned comments to conform - to CODING_GUIDE. - - - lib/matplotlib/backends/backend_pdf.py: - Added support for 14 PDF core fonts. - Fixed some issues with incorrect character widths and - encodings (works only for the most common encoding, - WinAnsiEncoding, defined by the official PDF Reference). - Removed parameter 'dpi' because it causes alignment issues. - - -JKS (patch by Nicolas Grilly) - -2007-02-17 Changed ft2font.get_charmap, and updated all the files where - get_charmap is mentioned - ES - -2007-02-13 Added barcode demo- JDH - -2007-02-13 Added binary colormap to cm - JDH - -2007-02-13 Added twiny to pylab - JDH - -2007-02-12 Moved data files into lib/matplotlib so that setuptools' - develop mode works. Re-organized the mpl-data layout so - that this source structure is maintained in the - installation. (i.e., the 'fonts' and 'images' - sub-directories are maintained in site-packages.) Suggest - removing site-packages/matplotlib/mpl-data and - ~/.matplotlib/ttffont.cache before installing - ADS - -2007-02-07 Committed Rob Hetland's patch for qt4: remove - references to text()/latin1(), plus some improvements - to the toolbar layout - DSD - ---------------------------- + The blit() method is now faster regardless of which agg/wxPython conversion + routines are used. -2007-02-06 Released 0.90.0 at revision 3003 +2007-02-21 + Applied the PDF backend patch by Nicolas Grilly. This impacts several + files and directories in matplotlib: -2007-01-22 Extended the new picker API to text, patches and patch - collections. Added support for user customizable pick hit - testing and attribute tagging of the PickEvent - Details - and examples in examples/pick_event_demo.py - JDH + - Created the directory lib/matplotlib/mpl-data/fonts/pdfcorefonts, holding + AFM files for the 14 PDF core fonts. These fonts are embedded in every + PDF viewing application. -2007-01-16 Begun work on a new pick API using the mpl event handling - frameowrk. Artists will define their own pick method with - a configurable epsilon tolerance and return pick attrs. - All artists that meet the tolerance threshold will fire a - PickEvent with artist dependent attrs; e.g., a Line2D can set - the indices attribute that shows the indices into the line - that are within epsilon of the pick point. See - examples/pick_event_demo.py. The implementation of pick - for the remaining Artists remains to be done, but the core - infrastructure at the level of event handling is in place - with a proof-of-concept implementation for Line2D - JDH + - setup.py: Added the directory pdfcorefonts to package_data. -2007-01-16 src/_image.cpp: update to use Py_ssize_t (for 64-bit systems). - Use return value of fread() to prevent warning messages - SC. + - lib/matplotlib/__init__.py: Added the default parameter + 'pdf.use14corefonts'. When True, the PDF backend uses only the 14 PDF + core fonts. -2007-01-15 src/_image.cpp: combine buffer_argb32() and buffer_bgra32() into - a new method color_conv(format) - SC + - lib/matplotlib/afm.py: Added some keywords found in recent AFM files. + Added a little workaround to handle Euro symbol. -2007-01-14 backend_cairo.py: update draw_arc() so that - examples/arctest.py looks correct - SC + - lib/matplotlib/fontmanager.py: Added support for the 14 PDF core fonts. + These fonts have a dedicated cache (file pdfcorefont.cache), not the same + as for other AFM files (file .afmfont.cache). Also cleaned comments to + conform to CODING_GUIDE. -2007-01-12 backend_cairo.py: enable clipping. Update draw_image() so that - examples/contour_demo.py looks correct - SC + - lib/matplotlib/backends/backend_pdf.py: Added support for 14 PDF core + fonts. Fixed some issues with incorrect character widths and encodings + (works only for the most common encoding, WinAnsiEncoding, defined by the + official PDF Reference). Removed parameter 'dpi' because it causes + alignment issues. -2007-01-12 backend_cairo.py: fix draw_image() so that examples/image_demo.py - now looks correct - SC + -JKS (patch by Nicolas Grilly) -2007-01-11 Added Axes.xcorr and Axes.acorr to plot the cross - correlation of x vs. y or the autocorrelation of x. pylab - wrappers also provided. See examples/xcorr_demo.py - JDH +2007-02-17 + Changed ft2font.get_charmap, and updated all the files where get_charmap is + mentioned - ES -2007-01-10 Added "Subplot.label_outer" method. It will set the - visibility of the ticklabels so that yticklabels are only - visible in the first column and xticklabels are only - visible in the last row - JDH +2007-02-13 + Added barcode demo- JDH -2007-01-02 Added additional kwarg documentation - JDH +2007-02-13 + Added binary colormap to cm - JDH -2006-12-28 Improved error message for nonpositive input to log - transform; added log kwarg to bar, barh, and hist, - and modified bar method to behave sensibly by default - when the ordinate has a log scale. (This only works - if the log scale is set before or by the call to bar, - hence the utility of the log kwarg.) - EF +2007-02-13 + Added twiny to pylab - JDH -2006-12-27 backend_cairo.py: update draw_image() and _draw_mathtext() to work - with numpy - SC +2007-02-12 + Moved data files into lib/matplotlib so that setuptools' develop mode + works. Re-organized the mpl-data layout so that this source structure is + maintained in the installation. (i.e., the 'fonts' and 'images' + sub-directories are maintained in site-packages.) Suggest removing + site-packages/matplotlib/mpl-data and ~/.matplotlib/ttffont.cache before + installing - ADS -2006-12-20 Fixed xpdf dependency check, which was failing on windows. - Removed ps2eps dependency check. - DSD +2007-02-07 + Committed Rob Hetland's patch for qt4: remove references to + text()/latin1(), plus some improvements to the toolbar layout - DSD -2006-12-19 Added Tim Leslie's spectral patch - JDH - -2006-12-17 Added rc param 'axes.formatter.limits' to control - the default threshold for switching to scientific - notation. Added convenience method - Axes.ticklabel_format() for turning scientific notation - on or off on either or both axes. - EF - -2006-12-16 Added ability to turn control scientific notation - in ScalarFormatter - EF - -2006-12-16 Enhanced boxplot to handle more flexible inputs - EF - -2006-12-13 Replaced calls to where() in colors.py with much faster - clip() and putmask() calls; removed inappropriate - uses of getmaskorNone (which should be needed only - very rarely); all in response to profiling by - David Cournapeau. Also fixed bugs in my 2-D - array support from 12-09. - EF - -2006-12-09 Replaced spy and spy2 with the new spy that combines - marker and image capabilities - EF - -2006-12-09 Added support for plotting 2-D arrays with plot: - columns are plotted as in Matlab - EF - -2006-12-09 Added linewidth kwarg to bar and barh; fixed arg - checking bugs - EF - -2006-12-07 Made pcolormesh argument handling match pcolor; - fixed kwarg handling problem noted by Pierre GM - EF - -2006-12-06 Made pcolor support vector X and/or Y instead of - requiring 2-D arrays - EF - -2006-12-05 Made the default Artist._transform None (rather than - invoking identity_transform for each artist only to have it - overridden later). Use artist.get_transform() rather than - artist._transform, even in derived classes, so that the - default transform will be created lazily as needed - JDH - -2006-12-03 Added LogNorm to colors.py as illustrated by - examples/pcolor_log.py, based on suggestion by - Jim McDonald. Colorbar modified to handle LogNorm. - Norms have additional "inverse" method. - EF - -2006-12-02 Changed class names in colors.py to match convention: - normalize -> Normalize, no_norm -> NoNorm. Old names - are still available. - Changed __init__.py rc defaults to match those in - matplotlibrc - EF - -2006-11-22 Fixed bug in set_*lim that I had introduced on 11-15 - EF - -2006-11-22 Added examples/clippedline.py, which shows how to clip line - data based on view limits -- it also changes the marker - style when zoomed in - JDH - -2006-11-21 Some spy bug-fixes and added precision arg per Robert C's - suggestion - JDH - -2006-11-19 Added semi-automatic docstring generation detailing all the - kwargs that functions take using the artist introspection - tools; e.g., 'help text now details the scatter kwargs - that control the Text properties - JDH - -2006-11-17 Removed obsolete scatter_classic, leaving a stub to - raise NotImplementedError; same for pcolor_classic - EF - -2006-11-15 Removed obsolete pcolor_classic - EF - -2006-11-15 Fixed 1588908 reported by Russel Owen; factored - nonsingular method out of ticker.py, put it into - transforms.py as a function, and used it in - set_xlim and set_ylim. - EF - -2006-11-14 Applied patch 1591716 by Ulf Larssen to fix a bug in - apply_aspect. Modified and applied patch - 1594894 by mdehoon to fix bugs and improve - formatting in lines.py. Applied patch 1573008 - by Greg Willden to make psd etc. plot full frequency - range for complex inputs. - EF - -2006-11-14 Improved the ability of the colorbar to track - changes in corresponding image, pcolor, or - contourf. - EF - -2006-11-11 Fixed bug that broke Numeric compatibility; - added support for alpha to colorbar. The - alpha information is taken from the mappable - object, not specified as a kwarg. - EF - -2006-11-05 Added broken_barh function for makring a sequence of - horizontal bars broken by gaps -- see examples/broken_barh.py - -2006-11-05 Removed lineprops and markerprops from the Annotation code - and replaced them with an arrow configurable with kwarg - arrowprops. See examples/annotation_demo.py - JDH - -2006-11-02 Fixed a pylab subplot bug that was causing axes to be - deleted with hspace or wspace equals zero in - subplots_adjust - JDH +--------------------------- -2006-10-31 Applied axes3d patch 1587359 - \http://sourceforge.net/tracker/index.php?func=detail&aid=1587359&group_id=80706&atid=560722 - JDH +2007-02-06 + Released 0.90.0 at revision 3003 + +2007-01-22 + Extended the new picker API to text, patches and patch collections. Added + support for user customizable pick hit testing and attribute tagging of the + PickEvent - Details and examples in examples/pick_event_demo.py - JDH + +2007-01-16 + Begun work on a new pick API using the mpl event handling framework. + Artists will define their own pick method with a configurable epsilon + tolerance and return pick attrs. All artists that meet the tolerance + threshold will fire a PickEvent with artist dependent attrs; e.g., a Line2D + can set the indices attribute that shows the indices into the line that are + within epsilon of the pick point. See examples/pick_event_demo.py. The + implementation of pick for the remaining Artists remains to be done, but + the core infrastructure at the level of event handling is in place with a + proof-of-concept implementation for Line2D - JDH + +2007-01-16 + src/_image.cpp: update to use Py_ssize_t (for 64-bit systems). Use return + value of fread() to prevent warning messages - SC. + +2007-01-15 + src/_image.cpp: combine buffer_argb32() and buffer_bgra32() into a new + method color_conv(format) - SC + +2007-01-14 + backend_cairo.py: update draw_arc() so that examples/arctest.py looks + correct - SC + +2007-01-12 + backend_cairo.py: enable clipping. Update draw_image() so that + examples/contour_demo.py looks correct - SC + +2007-01-12 + backend_cairo.py: fix draw_image() so that examples/image_demo.py now looks + correct - SC + +2007-01-11 + Added Axes.xcorr and Axes.acorr to plot the cross correlation of x vs. y or + the autocorrelation of x. pylab wrappers also provided. See + examples/xcorr_demo.py - JDH + +2007-01-10 + Added "Subplot.label_outer" method. It will set the visibility of the + ticklabels so that yticklabels are only visible in the first column and + xticklabels are only visible in the last row - JDH + +2007-01-02 + Added additional kwarg documentation - JDH + +2006-12-28 + Improved error message for nonpositive input to log transform; added log + kwarg to bar, barh, and hist, and modified bar method to behave sensibly by + default when the ordinate has a log scale. (This only works if the log + scale is set before or by the call to bar, hence the utility of the log + kwarg.) - EF + +2006-12-27 + backend_cairo.py: update draw_image() and _draw_mathtext() to work with + numpy - SC + +2006-12-20 + Fixed xpdf dependency check, which was failing on windows. Removed ps2eps + dependency check. - DSD + +2006-12-19 + Added Tim Leslie's spectral patch - JDH + +2006-12-17 + Added rc param 'axes.formatter.limits' to control the default threshold for + switching to scientific notation. Added convenience method + Axes.ticklabel_format() for turning scientific notation on or off on either + or both axes. - EF + +2006-12-16 + Added ability to turn control scientific notation in ScalarFormatter - EF + +2006-12-16 + Enhanced boxplot to handle more flexible inputs - EF + +2006-12-13 + Replaced calls to where() in colors.py with much faster clip() and + putmask() calls; removed inappropriate uses of getmaskorNone (which should + be needed only very rarely); all in response to profiling by David + Cournapeau. Also fixed bugs in my 2-D array support from 12-09. - EF + +2006-12-09 + Replaced spy and spy2 with the new spy that combines marker and image + capabilities - EF + +2006-12-09 + Added support for plotting 2-D arrays with plot: columns are plotted as in + Matlab - EF + +2006-12-09 + Added linewidth kwarg to bar and barh; fixed arg checking bugs - EF + +2006-12-07 + Made pcolormesh argument handling match pcolor; fixed kwarg handling + problem noted by Pierre GM - EF + +2006-12-06 + Made pcolor support vector X and/or Y instead of requiring 2-D arrays - EF + +2006-12-05 + Made the default Artist._transform None (rather than invoking + identity_transform for each artist only to have it overridden later). Use + artist.get_transform() rather than artist._transform, even in derived + classes, so that the default transform will be created lazily as needed - + JDH + +2006-12-03 + Added LogNorm to colors.py as illustrated by examples/pcolor_log.py, based + on suggestion by Jim McDonald. Colorbar modified to handle LogNorm. Norms + have additional "inverse" method. - EF + +2006-12-02 + Changed class names in colors.py to match convention: normalize -> + Normalize, no_norm -> NoNorm. Old names are still available. Changed + __init__.py rc defaults to match those in matplotlibrc - EF + +2006-11-22 + Fixed bug in set_*lim that I had introduced on 11-15 - EF + +2006-11-22 + Added examples/clippedline.py, which shows how to clip line data based on + view limits -- it also changes the marker style when zoomed in - JDH + +2006-11-21 + Some spy bug-fixes and added precision arg per Robert C's suggestion - JDH + +2006-11-19 + Added semi-automatic docstring generation detailing all the kwargs that + functions take using the artist introspection tools; e.g., 'help text now + details the scatter kwargs that control the Text properties - JDH + +2006-11-17 + Removed obsolete scatter_classic, leaving a stub to raise + NotImplementedError; same for pcolor_classic - EF + +2006-11-15 + Removed obsolete pcolor_classic - EF + +2006-11-15 + Fixed 1588908 reported by Russel Owen; factored nonsingular method out of + ticker.py, put it into transforms.py as a function, and used it in set_xlim + and set_ylim. - EF + +2006-11-14 + Applied patch 1591716 by Ulf Larssen to fix a bug in apply_aspect. + Modified and applied patch 1594894 by mdehoon to fix bugs and improve + formatting in lines.py. Applied patch 1573008 by Greg Willden to make psd + etc. plot full frequency range for complex inputs. - EF + +2006-11-14 + Improved the ability of the colorbar to track changes in corresponding + image, pcolor, or contourf. - EF + +2006-11-11 + Fixed bug that broke Numeric compatibility; added support for alpha to + colorbar. The alpha information is taken from the mappable object, not + specified as a kwarg. - EF + +2006-11-05 + Added broken_barh function for making a sequence of horizontal bars broken + by gaps -- see examples/broken_barh.py + +2006-11-05 + Removed lineprops and markerprops from the Annotation code and replaced + them with an arrow configurable with kwarg arrowprops. See + examples/annotation_demo.py - JDH + +2006-11-02 + Fixed a pylab subplot bug that was causing axes to be deleted with hspace + or wspace equals zero in subplots_adjust - JDH + +2006-10-31 + Applied axes3d patch 1587359 + \http://sourceforge.net/tracker/index.php?func=detail&aid=1587359&group_id=80706&atid=560722 + JDH ------------------------- -2006-10-26 Released 0.87.7 at revision 2835 +2006-10-26 + Released 0.87.7 at revision 2835 -2006-10-25 Made "tiny" kwarg in Locator.nonsingular much smaller - EF +2006-10-25 + Made "tiny" kwarg in Locator.nonsingular much smaller - EF -2006-10-17 Closed sf bug 1562496 update line props dash/solid/cap/join - styles - JDH +2006-10-17 + Closed sf bug 1562496 update line props dash/solid/cap/join styles - JDH -2006-10-17 Complete overhaul of the annotations API and example code - - See matplotlib.text.Annotation and - examples/annotation_demo.py JDH +2006-10-17 + Complete overhaul of the annotations API and example code - See + matplotlib.text.Annotation and examples/annotation_demo.py JDH -2006-10-12 Committed Manuel Metz's StarPolygon code and - examples/scatter_star_poly.py - JDH +2006-10-12 + Committed Manuel Metz's StarPolygon code and examples/scatter_star_poly.py + - JDH +2006-10-11 + commented out all default values in matplotlibrc.template Default values + should generally be taken from defaultParam in __init__.py - the file + matplotlib should only contain those values that the user wants to + explicitly change from the default. (see thread "marker color handling" on + matplotlib-devel) -2006-10-11 commented out all default values in matplotlibrc.template - Default values should generally be taken from defaultParam in - __init__.py - the file matplotlib should only contain those values - that the user wants to explicitly change from the default. - (see thread "marker color handling" on matplotlib-devel) +2006-10-10 + Changed default comment character for load to '#' - JDH -2006-10-10 Changed default comment character for load to '#' - JDH +2006-10-10 + deactivated rcfile-configurability of markerfacecolor and markeredgecolor. + Both are now hardcoded to the special value 'auto' to follow the line + color. Configurability at run-time (using function arguments) remains + functional. - NN -2006-10-10 deactivated rcfile-configurability of markerfacecolor - and markeredgecolor. Both are now hardcoded to the special value - 'auto' to follow the line color. Configurability at run-time - (using function arguments) remains functional. - NN +2006-10-07 + introduced dummy argument magnification=1.0 to FigImage.make_image to + satisfy unit test figimage_demo.py The argument is not yet handled + correctly, which should only show up when using non-standard DPI settings + in PS backend, introduced by patch #1562394. - NN -2006-10-07 introduced dummy argument magnification=1.0 to - FigImage.make_image to satisfy unit test figimage_demo.py - The argument is not yet handled correctly, which should only - show up when using non-standard DPI settings in PS backend, - introduced by patch #1562394. - NN +2006-10-06 + add backend-agnostic example: simple3d.py - NN -2006-10-06 add backend-agnostic example: simple3d.py - NN +2006-09-29 + fix line-breaking for SVG-inline images (purely cosmetic) - NN -2006-09-29 fix line-breaking for SVG-inline images (purely cosmetic) - NN +2006-09-29 + reworked set_linestyle and set_marker markeredgecolor and markerfacecolor + now default to a special value "auto" that keeps the color in sync with the + line color further, the intelligence of axes.plot is cleaned up, improved + and simplified. Complete compatibility cannot be guaranteed, but the new + behavior should be much more predictable (see patch #1104615 for details) - + NN -2006-09-29 reworked set_linestyle and set_marker - markeredgecolor and markerfacecolor now default to - a special value "auto" that keeps the color in sync with - the line color - further, the intelligence of axes.plot is cleaned up, - improved and simplified. Complete compatibility cannot be - guaranteed, but the new behavior should be much more predictable - (see patch #1104615 for details) - NN +2006-09-29 + changed implementation of clip-path in SVG to work around a limitation in + inkscape - NN -2006-09-29 changed implementation of clip-path in SVG to work around a - limitation in inkscape - NN +2006-09-29 + added two options to matplotlibrc: -2006-09-29 added two options to matplotlibrc: - svg.image_inline - svg.image_noscale - see patch #1533010 for details - NN + - svg.image_inline + - svg.image_noscale -2006-09-29 axes.py: cleaned up kwargs checking - NN + see patch #1533010 for details - NN -2006-09-29 setup.py: cleaned up setup logic - NN +2006-09-29 + axes.py: cleaned up kwargs checking - NN -2006-09-29 setup.py: check for required pygtk versions, fixes bug #1460783 - SC +2006-09-29 + setup.py: cleaned up setup logic - NN + +2006-09-29 + setup.py: check for required pygtk versions, fixes bug #1460783 - SC --------------------------------- -2006-09-27 Released 0.87.6 at revision 2783 +2006-09-27 + Released 0.87.6 at revision 2783 -2006-09-24 Added line pointers to the Annotation code, and a pylab - interface. See matplotlib.text.Annotation, - examples/annotation_demo.py and - examples/annotation_demo_pylab.py - JDH +2006-09-24 + Added line pointers to the Annotation code, and a pylab interface. See + matplotlib.text.Annotation, examples/annotation_demo.py and + examples/annotation_demo_pylab.py - JDH -2006-09-18 mathtext2.py: The SVG backend now supports the same things that - the AGG backend does. Fixed some bugs with rendering, and out of - bounds errors in the AGG backend - ES. Changed the return values - of math_parse_s_ft2font_svg to support lines (fractions etc.) +2006-09-18 + mathtext2.py: The SVG backend now supports the same things that the AGG + backend does. Fixed some bugs with rendering, and out of bounds errors in + the AGG backend - ES. Changed the return values of math_parse_s_ft2font_svg + to support lines (fractions etc.) -2006-09-17 Added an Annotation class to facilitate annotating objects - and an examples file examples/annotation_demo.py. I want - to add dash support as in TextWithDash, but haven't decided - yet whether inheriting from TextWithDash is the right base - class or if another approach is needed - JDH +2006-09-17 + Added an Annotation class to facilitate annotating objects and an examples + file examples/annotation_demo.py. I want to add dash support as in + TextWithDash, but haven't decided yet whether inheriting from TextWithDash + is the right base class or if another approach is needed - JDH ------------------------------ -2006-09-05 Released 0.87.5 at revision 2761 +2006-09-05 + Released 0.87.5 at revision 2761 -2006-09-04 Added nxutils for some numeric add-on extension code -- - specifically a better/more efficient inside polygon tester (see - unit/inside_poly_*.py) - JDH +2006-09-04 + Added nxutils for some numeric add-on extension code -- specifically a + better/more efficient inside polygon tester (see unit/inside_poly_*.py) - + JDH -2006-09-04 Made bitstream fonts the rc default - JDH +2006-09-04 + Made bitstream fonts the rc default - JDH -2006-08-31 Fixed alpha-handling bug in ColorConverter, affecting - collections in general and contour/contourf in - particular. - EF +2006-08-31 + Fixed alpha-handling bug in ColorConverter, affecting collections in + general and contour/contourf in particular. - EF -2006-08-30 ft2font.cpp: Added draw_rect_filled method (now used by mathtext2 - to draw the fraction bar) to FT2Font - ES +2006-08-30 + ft2font.cpp: Added draw_rect_filled method (now used by mathtext2 to draw + the fraction bar) to FT2Font - ES -2006-08-29 setupext.py: wrap calls to tk.getvar() with str(). On some - systems, getvar returns a Tcl_Obj instead of a string - DSD +2006-08-29 + setupext.py: wrap calls to tk.getvar() with str(). On some systems, getvar + returns a Tcl_Obj instead of a string - DSD -2006-08-28 mathtext2.py: Sub/superscripts can now be complex (i.e. - fractions etc.). The demo is also updated - ES +2006-08-28 + mathtext2.py: Sub/superscripts can now be complex (i.e. fractions etc.). + The demo is also updated - ES -2006-08-28 font_manager.py: Added /usr/local/share/fonts to list of - X11 font directories - DSD +2006-08-28 + font_manager.py: Added /usr/local/share/fonts to list of X11 font + directories - DSD -2006-08-28 mahtext2.py: Initial support for complex fractions. Also, - rendering is now completely separated from parsing. The - sub/superscripts now work better. - Updated the mathtext2_demo.py - ES +2006-08-28 + mathtext2.py: Initial support for complex fractions. Also, rendering is now + completely separated from parsing. The sub/superscripts now work better. + Updated the mathtext2_demo.py - ES -2006-08-27 qt backends: don't create a QApplication when backend is - imported, do it when the FigureCanvasQt is created. Simplifies - applications where mpl is embedded in qt. Updated - embedding_in_qt* examples - DSD +2006-08-27 + qt backends: don't create a QApplication when backend is imported, do it + when the FigureCanvasQt is created. Simplifies applications where mpl is + embedded in qt. Updated embedding_in_qt* examples - DSD -2006-08-27 mahtext2.py: Now the fonts are searched in the OS font dir and - in the mpl-data dir. Also env is not a dict anymore. - ES +2006-08-27 + mathtext2.py: Now the fonts are searched in the OS font dir and in the + mpl-data dir. Also env is not a dict anymore. - ES -2006-08-26 minor changes to __init__.py, mathtex2_demo.py. Added matplotlibrc - key "mathtext.mathtext2" (removed the key "mathtext2") - ES +2006-08-26 + minor changes to __init__.py, mathtex2_demo.py. Added matplotlibrc key + "mathtext.mathtext2" (removed the key "mathtext2") - ES -2006-08-21 mathtext2.py: Initial support for fractions - Updated the mathtext2_demo.py - _mathtext_data.py: removed "\" from the unicode dicts - mathtext.py: Minor modification (because of _mathtext_data.py)- ES +2006-08-21 + mathtext2.py: Initial support for fractions Updated the mathtext2_demo.py + _mathtext_data.py: removed "\" from the unicode dicts mathtext.py: Minor + modification (because of _mathtext_data.py)- ES -2006-08-20 Added mathtext2.py: Replacement for mathtext.py. Supports _ ^, - \rm, \cal etc., \sin, \cos etc., unicode, recursive nestings, - inline math mode. The only backend currently supported is Agg - __init__.py: added new rc params for mathtext2 - added mathtext2_demo.py example - ES +2006-08-20 + Added mathtext2.py: Replacement for mathtext.py. Supports _ ^, \rm, \cal + etc., \sin, \cos etc., unicode, recursive nestings, inline math mode. The + only backend currently supported is Agg __init__.py: added new rc params + for mathtext2 added mathtext2_demo.py example - ES -2006-08-19 Added embedding_in_qt4.py example - DSD +2006-08-19 + Added embedding_in_qt4.py example - DSD -2006-08-11 Added scale free Ellipse patch for Agg - CM +2006-08-11 + Added scale free Ellipse patch for Agg - CM -2006-08-10 Added converters to and from julian dates to matplotlib.dates - (num2julian and julian2num) - JDH +2006-08-10 + Added converters to and from julian dates to matplotlib.dates (num2julian + and julian2num) - JDH -2006-08-08 Fixed widget locking so multiple widgets could share the - event handling - JDH +2006-08-08 + Fixed widget locking so multiple widgets could share the event handling - + JDH -2006-08-07 Added scale free Ellipse patch to SVG and PS - CM +2006-08-07 + Added scale free Ellipse patch to SVG and PS - CM -2006-08-05 Re-organized imports in numerix for numpy 1.0b2 -- TEO +2006-08-05 + Re-organized imports in numerix for numpy 1.0b2 -- TEO -2006-08-04 Added draw_markers to PDF backend. - JKS +2006-08-04 + Added draw_markers to PDF backend. - JKS -2006-08-01 Fixed a bug in postscript's rendering of dashed lines - DSD +2006-08-01 + Fixed a bug in postscript's rendering of dashed lines - DSD -2006-08-01 figure.py: savefig() update docstring to add support for 'format' - argument. - backend_cairo.py: print_figure() add support 'format' argument. - SC +2006-08-01 + figure.py: savefig() update docstring to add support for 'format' argument. + backend_cairo.py: print_figure() add support 'format' argument. - SC -2006-07-31 Don't let postscript's xpdf distiller compress images - DSD +2006-07-31 + Don't let postscript's xpdf distiller compress images - DSD -2006-07-31 Added shallowcopy() methods to all Transformations; - removed copy_bbox_transform and copy_bbox_transform_shallow - from transforms.py; - added offset_copy() function to transforms.py to - facilitate positioning artists with offsets. - See examples/transoffset.py. - EF +2006-07-31 + Added shallowcopy() methods to all Transformations; removed + copy_bbox_transform and copy_bbox_transform_shallow from transforms.py; + added offset_copy() function to transforms.py to facilitate positioning + artists with offsets. See examples/transoffset.py. - EF -2006-07-31 Don't let postscript's xpdf distiller compress images - DSD +2006-07-31 + Don't let postscript's xpdf distiller compress images - DSD -2006-07-29 Fixed numerix polygon bug reported by Nick Fotopoulos. - Added inverse_numerix_xy() transform method. - Made autoscale_view() preserve axis direction - (e.g., increasing down).- EF +2006-07-29 + Fixed numerix polygon bug reported by Nick Fotopoulos. Added + inverse_numerix_xy() transform method. Made autoscale_view() preserve axis + direction (e.g., increasing down).- EF -2006-07-28 Added shallow bbox copy routine for transforms -- mainly - useful for copying transforms to apply offset to. - JDH +2006-07-28 + Added shallow bbox copy routine for transforms -- mainly useful for copying + transforms to apply offset to. - JDH -2006-07-28 Added resize method to FigureManager class - for Qt and Gtk backend - CM +2006-07-28 + Added resize method to FigureManager class for Qt and Gtk backend - CM -2006-07-28 Added subplots_adjust button to Qt backend - CM +2006-07-28 + Added subplots_adjust button to Qt backend - CM -2006-07-26 Use numerix more in collections. - Quiver now handles masked arrays. - EF +2006-07-26 + Use numerix more in collections. Quiver now handles masked arrays. - EF -2006-07-22 Fixed bug #1209354 - DSD +2006-07-22 + Fixed bug #1209354 - DSD -2006-07-22 make scatter() work with the kwarg "color". Closes bug - 1285750 - DSD +2006-07-22 + make scatter() work with the kwarg "color". Closes bug 1285750 - DSD -2006-07-20 backend_cairo.py: require pycairo 1.2.0. - print_figure() update to output SVG using cairo. +2006-07-20 + backend_cairo.py: require pycairo 1.2.0. print_figure() update to output + SVG using cairo. -2006-07-19 Added blitting for Qt4Agg - CM +2006-07-19 + Added blitting for Qt4Agg - CM -2006-07-19 Added lasso widget and example examples/lasso_demo.py - JDH +2006-07-19 + Added lasso widget and example examples/lasso_demo.py - JDH -2006-07-18 Added blitting for QtAgg backend - CM +2006-07-18 + Added blitting for QtAgg backend - CM -2006-07-17 Fixed bug #1523585: skip nans in semilog plots - DSD +2006-07-17 + Fixed bug #1523585: skip nans in semilog plots - DSD -2006-07-12 Add support to render the scientific notation label - over the right-side y-axis - DSD +2006-07-12 + Add support to render the scientific notation label over the right-side + y-axis - DSD ------------------------------ -2006-07-11 Released 0.87.4 at revision 2558 - -2006-07-07 Fixed a usetex bug with older versions of latex - DSD - -2006-07-07 Add compatibility for NumPy 1.0 - TEO - -2006-06-29 Added a Qt4Agg backend. Thank you James Amundson - DSD - -2006-06-26 Fixed a usetex bug. On Windows, usetex will process - postscript output in the current directory rather than - in a temp directory. This is due to the use of spaces - and tildes in windows paths, which cause problems with - latex. The subprocess module is no longer used. - DSD - -2006-06-22 Various changes to bar(), barh(), and hist(). - Added 'edgecolor' keyword arg to bar() and barh(). - The x and y args in barh() have been renamed to width - and bottom respectively, and their order has been swapped - to maintain a (position, value) order ala matlab. left, - height, width and bottom args can now all be scalars or - sequences. barh() now defaults to edge alignment instead - of center alignment. Added a keyword arg 'align' to bar(), - barh() and hist() that controls between edge or center bar - alignment. Fixed ignoring the rcParams['patch.facecolor'] - for bar color in bar() and barh(). Fixed ignoring the - rcParams['lines.color'] for error bar color in bar() - and barh(). Fixed a bug where patches would be cleared - when error bars were plotted if rcParams['axes.hold'] - was False. - MAS - -2006-06-22 Added support for numerix 2-D arrays as alternatives to - a sequence of (x,y) tuples for specifying paths in - collections, quiver, contour, pcolor, transforms. - Fixed contour bug involving setting limits for - colormapping. Added numpy-style all() to numerix. - EF - -2006-06-20 Added custom FigureClass hook to pylab interface - see - examples/custom_figure_class.py - -2006-06-16 Added colormaps from gist (gist_earth, gist_stern, - gist_rainbow, gist_gray, gist_yarg, gist_heat, gist_ncar) - JW - -2006-06-16 Added a pointer to parent in figure canvas so you can - access the container with fig.canvas.manager. Useful if - you want to set the window title, e.g., in gtk - fig.canvas.manager.window.set_title, though a GUI neutral - method would be preferable JDH - -2006-06-16 Fixed colorbar.py to handle indexed colors (i.e., - norm = no_norm()) by centering each colored region - on its index. - EF - -2006-06-15 Added scalex and scaley to Axes.autoscale_view to support - selective autoscaling just the x or y axis, and supported - these command in plot so you can say plot(something, - scaley=False) and just the x axis will be autoscaled. - Modified axvline and axhline to support this, so for - example axvline will no longer autoscale the y axis. JDH - -2006-06-13 Fix so numpy updates are backward compatible - TEO - -2006-06-12 Updated numerix to handle numpy restructuring of - oldnumeric - TEO - -2006-06-12 Updated numerix.fft to handle numpy restructuring - Added ImportError to numerix.linear_algebra for numpy -TEO - -2006-06-11 Added quiverkey command to pylab and Axes, using - QuiverKey class in quiver.py. Changed pylab and Axes - to use quiver2 if possible, but drop back to the - newly-renamed quiver_classic if necessary. Modified - examples/quiver_demo.py to illustrate the new quiver - and quiverkey. Changed LineCollection implementation - slightly to improve compatibility with PolyCollection. - EF - -2006-06-11 Fixed a usetex bug for windows, running latex on files - with spaces in their names or paths was failing - DSD - -2006-06-09 Made additions to numerix, changes to quiver to make it - work with all numeric flavors. - EF - -2006-06-09 Added quiver2 function to pylab and method to axes, - with implementation via a Quiver class in quiver.py. - quiver2 will replace quiver before the next release; - it is placed alongside it initially to facilitate - testing and transition. See also - examples/quiver2_demo.py. - EF - -2006-06-08 Minor bug fix to make ticker.py draw proper minus signs - with usetex - DSD +2006-07-11 + Released 0.87.4 at revision 2558 + +2006-07-07 + Fixed a usetex bug with older versions of latex - DSD + +2006-07-07 + Add compatibility for NumPy 1.0 - TEO + +2006-06-29 + Added a Qt4Agg backend. Thank you James Amundson - DSD + +2006-06-26 + Fixed a usetex bug. On Windows, usetex will process postscript output in + the current directory rather than in a temp directory. This is due to the + use of spaces and tildes in windows paths, which cause problems with latex. + The subprocess module is no longer used. - DSD + +2006-06-22 + Various changes to bar(), barh(), and hist(). Added 'edgecolor' keyword + arg to bar() and barh(). The x and y args in barh() have been renamed to + width and bottom respectively, and their order has been swapped to maintain + a (position, value) order ala matlab. left, height, width and bottom args + can now all be scalars or sequences. barh() now defaults to edge alignment + instead of center alignment. Added a keyword arg 'align' to bar(), barh() + and hist() that controls between edge or center bar alignment. Fixed + ignoring the rcParams['patch.facecolor'] for bar color in bar() and barh(). + Fixed ignoring the rcParams['lines.color'] for error bar color in bar() and + barh(). Fixed a bug where patches would be cleared when error bars were + plotted if rcParams['axes.hold'] was False. - MAS + +2006-06-22 + Added support for numerix 2-D arrays as alternatives to a sequence of (x,y) + tuples for specifying paths in collections, quiver, contour, pcolor, + transforms. Fixed contour bug involving setting limits for colormapping. + Added numpy-style all() to numerix. - EF + +2006-06-20 + Added custom FigureClass hook to pylab interface - see + examples/custom_figure_class.py + +2006-06-16 + Added colormaps from gist (gist_earth, gist_stern, gist_rainbow, gist_gray, + gist_yarg, gist_heat, gist_ncar) - JW + +2006-06-16 + Added a pointer to parent in figure canvas so you can access the container + with fig.canvas.manager. Useful if you want to set the window title, e.g., + in gtk fig.canvas.manager.window.set_title, though a GUI neutral method + would be preferable JDH + +2006-06-16 + Fixed colorbar.py to handle indexed colors (i.e., norm = no_norm()) by + centering each colored region on its index. - EF + +2006-06-15 + Added scalex and scaley to Axes.autoscale_view to support selective + autoscaling just the x or y axis, and supported these command in plot so + you can say plot(something, scaley=False) and just the x axis will be + autoscaled. Modified axvline and axhline to support this, so for example + axvline will no longer autoscale the y axis. JDH + +2006-06-13 + Fix so numpy updates are backward compatible - TEO + +2006-06-12 + Updated numerix to handle numpy restructuring of oldnumeric - TEO + +2006-06-12 + Updated numerix.fft to handle numpy restructuring Added ImportError to + numerix.linear_algebra for numpy -TEO + +2006-06-11 + Added quiverkey command to pylab and Axes, using QuiverKey class in + quiver.py. Changed pylab and Axes to use quiver2 if possible, but drop + back to the newly-renamed quiver_classic if necessary. Modified + examples/quiver_demo.py to illustrate the new quiver and quiverkey. + Changed LineCollection implementation slightly to improve compatibility + with PolyCollection. - EF + +2006-06-11 + Fixed a usetex bug for windows, running latex on files with spaces in their + names or paths was failing - DSD + +2006-06-09 + Made additions to numerix, changes to quiver to make it work with all + numeric flavors. - EF + +2006-06-09 + Added quiver2 function to pylab and method to axes, with implementation via + a Quiver class in quiver.py. quiver2 will replace quiver before the next + release; it is placed alongside it initially to facilitate testing and + transition. See also examples/quiver2_demo.py. - EF + +2006-06-08 + Minor bug fix to make ticker.py draw proper minus signs with usetex - DSD ----------------------- -2006-06-06 Released 0.87.3 at revision 2432 - -2006-05-30 More partial support for polygons with outline or fill, - but not both. Made LineCollection inherit from - ScalarMappable. - EF +2006-06-06 + Released 0.87.3 at revision 2432 -2006-05-29 Yet another revision of aspect-ratio handling. - EF +2006-05-30 + More partial support for polygons with outline or fill, but not both. Made + LineCollection inherit from ScalarMappable. - EF -2006-05-27 Committed a patch to prevent stroking zero-width lines in - the svg backend - DSD +2006-05-29 + Yet another revision of aspect-ratio handling. - EF -2006-05-24 Fixed colorbar positioning bug identified by Helge - Avlesen, and improved the algorithm; added a 'pad' - kwarg to control the spacing between colorbar and - parent axes. - EF +2006-05-27 + Committed a patch to prevent stroking zero-width lines in the svg backend - + DSD -2006-05-23 Changed color handling so that collection initializers - can take any mpl color arg or sequence of args; deprecated - float as grayscale, replaced by string representation of - float. - EF +2006-05-24 + Fixed colorbar positioning bug identified by Helge Avlesen, and improved + the algorithm; added a 'pad' kwarg to control the spacing between colorbar + and parent axes. - EF -2006-05-19 Fixed bug: plot failed if all points were masked - EF +2006-05-23 + Changed color handling so that collection initializers can take any mpl + color arg or sequence of args; deprecated float as grayscale, replaced by + string representation of float. - EF -2006-05-19 Added custom symbol option to scatter - JDH +2006-05-19 + Fixed bug: plot failed if all points were masked - EF -2006-05-18 New example, multi_image.py; colorbar fixed to show - offset text when the ScalarFormatter is used; FixedFormatter - augmented to accept and display offset text. - EF +2006-05-19 + Added custom symbol option to scatter - JDH -2006-05-14 New colorbar; old one is renamed to colorbar_classic. - New colorbar code is in colorbar.py, with wrappers in - figure.py and pylab.py. - Fixed aspect-handling bug reported by Michael Mossey. - Made backend_bases.draw_quad_mesh() run.- EF +2006-05-18 + New example, multi_image.py; colorbar fixed to show offset text when the + ScalarFormatter is used; FixedFormatter augmented to accept and display + offset text. - EF -2006-05-08 Changed handling of end ranges in contourf: replaced - "clip-ends" kwarg with "extend". See docstring for - details. -EF +2006-05-14 + New colorbar; old one is renamed to colorbar_classic. New colorbar code is + in colorbar.py, with wrappers in figure.py and pylab.py. Fixed + aspect-handling bug reported by Michael Mossey. Made + backend_bases.draw_quad_mesh() run.- EF -2006-05-08 Added axisbelow to rc - JDH +2006-05-08 + Changed handling of end ranges in contourf: replaced "clip-ends" kwarg with + "extend". See docstring for details. -EF -2006-05-08 If using PyGTK require version 2.2+ - SC +2006-05-08 + Added axisbelow to rc - JDH -2006-04-19 Added compression support to PDF backend, controlled by - new pdf.compression rc setting. - JKS +2006-05-08 + If using PyGTK require version 2.2+ - SC -2006-04-19 Added Jouni's PDF backend +2006-04-19 + Added compression support to PDF backend, controlled by new pdf.compression + rc setting. - JKS -2006-04-18 Fixed a bug that caused agg to not render long lines +2006-04-19 + Added Jouni's PDF backend -2006-04-16 Masked array support for pcolormesh; made pcolormesh support the - same combinations of X,Y,C dimensions as pcolor does; - improved (I hope) description of grid used in pcolor, - pcolormesh. - EF +2006-04-18 + Fixed a bug that caused agg to not render long lines -2006-04-14 Reorganized axes.py - EF +2006-04-16 + Masked array support for pcolormesh; made pcolormesh support the same + combinations of X,Y,C dimensions as pcolor does; improved (I hope) + description of grid used in pcolor, pcolormesh. - EF -2006-04-13 Fixed a bug Ryan found using usetex with sans-serif fonts and - exponential tick labels - DSD +2006-04-14 + Reorganized axes.py - EF -2006-04-11 Refactored backend_ps and backend_agg to prevent module-level - texmanager imports. Now these imports only occur if text.usetex - rc setting is true - DSD +2006-04-13 + Fixed a bug Ryan found using usetex with sans-serif fonts and exponential + tick labels - DSD -2006-04-10 Committed changes required for building mpl on win32 - platforms with visual studio. This allows wxpython - blitting for fast animations. - CM +2006-04-11 + Refactored backend_ps and backend_agg to prevent module-level texmanager + imports. Now these imports only occur if text.usetex rc setting is true - + DSD -2006-04-10 Fixed an off-by-one bug in Axes.change_geometry. +2006-04-10 + Committed changes required for building mpl on win32 platforms with visual + studio. This allows wxpython blitting for fast animations. - CM -2006-04-10 Fixed bug in pie charts where wedge wouldn't have label in - legend. Submitted by Simon Hildebrandt. - ADS +2006-04-10 + Fixed an off-by-one bug in Axes.change_geometry. -2006-05-06 Usetex makes temporary latex and dvi files in a temporary - directory, rather than in the user's current working - directory - DSD +2006-04-10 + Fixed bug in pie charts where wedge wouldn't have label in legend. + Submitted by Simon Hildebrandt. - ADS -2006-04-05 Applied Ken's wx deprecation warning patch closing sf patch - #1465371 - JDH +2006-05-06 + Usetex makes temporary latex and dvi files in a temporary directory, rather + than in the user's current working directory - DSD -2006-04-05 Added support for the new API in the postscript backend. - Allows values to be masked using nan's, and faster file - creation - DSD +2006-04-05 + Applied Ken's wx deprecation warning patch closing sf patch #1465371 - JDH -2006-04-05 Use python's subprocess module for usetex calls to - external programs. subprocess catches when they exit - abnormally so an error can be raised. - DSD +2006-04-05 + Added support for the new API in the postscript backend. Allows values to + be masked using nan's, and faster file creation - DSD -2006-04-03 Fixed the bug in which widgets would not respond to - events. This regressed the twinx functionality, so I - also updated subplots_adjust to update axes that share - an x or y with a subplot instance. - CM +2006-04-05 + Use python's subprocess module for usetex calls to external programs. + subprocess catches when they exit abnormally so an error can be raised. - + DSD -2006-04-02 Moved PBox class to transforms and deleted pbox.py; - made pylab axis command a thin wrapper for Axes.axis; - more tweaks to aspect-ratio handling; fixed Axes.specgram - to account for the new imshow default of unit aspect - ratio; made contour set the Axes.dataLim. - EF +2006-04-03 + Fixed the bug in which widgets would not respond to events. This regressed + the twinx functionality, so I also updated subplots_adjust to update axes + that share an x or y with a subplot instance. - CM -2006-03-31 Fixed the Qt "Underlying C/C++ object deleted" bug. - JRE +2006-04-02 + Moved PBox class to transforms and deleted pbox.py; made pylab axis command + a thin wrapper for Axes.axis; more tweaks to aspect-ratio handling; fixed + Axes.specgram to account for the new imshow default of unit aspect ratio; + made contour set the Axes.dataLim. - EF -2006-03-31 Applied Vasily Sulatskov's Qt Navigation Toolbar enhancement. - JRE +2006-03-31 + Fixed the Qt "Underlying C/C++ object deleted" bug. - JRE -2006-03-31 Ported Norbert's rewriting of Halldor's stineman_interp - algorithm to make it numerix compatible and added code to - matplotlib.mlab. See examples/interp_demo.py - JDH +2006-03-31 + Applied Vasily Sulatskov's Qt Navigation Toolbar enhancement. - JRE -2006-03-30 Fixed a bug in aspect ratio handling; blocked potential - crashes when panning with button 3; added axis('image') - support. - EF +2006-03-31 + Ported Norbert's rewriting of Halldor's stineman_interp algorithm to make + it numerix compatible and added code to matplotlib.mlab. See + examples/interp_demo.py - JDH -2006-03-28 More changes to aspect ratio handling; new PBox class - in new file pbox.py to facilitate resizing and repositioning - axes; made PolarAxes maintain unit aspect ratio. - EF +2006-03-30 + Fixed a bug in aspect ratio handling; blocked potential crashes when + panning with button 3; added axis('image') support. - EF -2006-03-23 Refactored TextWithDash class to inherit from, rather than - delegate to, the Text class. Improves object inspection - and closes bug # 1357969 - DSD +2006-03-28 + More changes to aspect ratio handling; new PBox class in new file pbox.py + to facilitate resizing and repositioning axes; made PolarAxes maintain unit + aspect ratio. - EF -2006-03-22 Improved aspect ratio handling, including pylab interface. - Interactive resizing, pan, zoom of images and plots - (including panels with a shared axis) should work. - Additions and possible refactoring are still likely. - EF +2006-03-23 + Refactored TextWithDash class to inherit from, rather than delegate to, the + Text class. Improves object inspection and closes bug # 1357969 - DSD -2006-03-21 Added another colorbrewer colormap (RdYlBu) - JSWHIT +2006-03-22 + Improved aspect ratio handling, including pylab interface. Interactive + resizing, pan, zoom of images and plots (including panels with a shared + axis) should work. Additions and possible refactoring are still likely. - + EF -2006-03-21 Fixed tickmarks for logscale plots over very large ranges. - Closes bug # 1232920 - DSD +2006-03-21 + Added another colorbrewer colormap (RdYlBu) - JSWHIT -2006-03-21 Added Rob Knight's arrow code; see examples/arrow_demo.py - JDH +2006-03-21 + Fixed tickmarks for logscale plots over very large ranges. Closes bug # + 1232920 - DSD -2006-03-20 Added support for masking values with nan's, using ADS's - isnan module and the new API. Works for \*Agg backends - DSD +2006-03-21 + Added Rob Knight's arrow code; see examples/arrow_demo.py - JDH -2006-03-20 Added contour.negative_linestyle rcParam - ADS +2006-03-20 + Added support for masking values with nan's, using ADS's isnan module and + the new API. Works for \*Agg backends - DSD -2006-03-20 Added _isnan extension module to test for nan with Numeric - - ADS +2006-03-20 + Added contour.negative_linestyle rcParam - ADS -2006-03-17 Added Paul and Alex's support for faceting with quadmesh - in sf patch 1411223 - JDH +2006-03-20 + Added _isnan extension module to test for nan with Numeric - ADS -2006-03-17 Added Charle Twardy's pie patch to support colors=None. - Closes sf patch 1387861 - JDH +2006-03-17 + Added Paul and Alex's support for faceting with quadmesh in sf patch + 1411223 - JDH -2006-03-17 Applied sophana's patch to support overlapping axes with - toolbar navigation by toggling activation with the 'a' key. - Closes sf patch 1432252 - JDH +2006-03-17 + Added Charle Twardy's pie patch to support colors=None. Closes sf patch + 1387861 - JDH -2006-03-17 Applied Aarre's linestyle patch for backend EMF; closes sf - patch 1449279 - JDH +2006-03-17 + Applied sophana's patch to support overlapping axes with toolbar navigation + by toggling activation with the 'a' key. Closes sf patch 1432252 - JDH -2006-03-17 Applied Jordan Dawe's patch to support kwarg properties - for grid lines in the grid command. Closes sf patch - 1451661 - JDH +2006-03-17 + Applied Aarre's linestyle patch for backend EMF; closes sf patch 1449279 - + JDH -2006-03-17 Center postscript output on page when using usetex - DSD +2006-03-17 + Applied Jordan Dawe's patch to support kwarg properties for grid lines in + the grid command. Closes sf patch 1451661 - JDH -2006-03-17 subprocess module built if Python <2.4 even if subprocess - can be imported from an egg - ADS +2006-03-17 + Center postscript output on page when using usetex - DSD -2006-03-17 Added _subprocess.c from Python upstream and hopefully - enabled building (without breaking) on Windows, although - not tested. - ADS +2006-03-17 + subprocess module built if Python <2.4 even if subprocess can be imported + from an egg - ADS -2006-03-17 Updated subprocess.py to latest Python upstream and - reverted name back to subprocess.py - ADS +2006-03-17 + Added _subprocess.c from Python upstream and hopefully enabled building + (without breaking) on Windows, although not tested. - ADS -2006-03-16 Added John Porter's 3D handling code +2006-03-17 + Updated subprocess.py to latest Python upstream and reverted name back to + subprocess.py - ADS +2006-03-16 + Added John Porter's 3D handling code ------------------------ -2006-03-16 Released 0.87.2 at revision 2150 +2006-03-16 + Released 0.87.2 at revision 2150 -2006-03-15 Fixed bug in MaxNLocator revealed by daigos@infinito.it. - The main change is that Locator.nonsingular now adjusts - vmin and vmax if they are nearly the same, not just if - they are equal. A new kwarg, "tiny", sets the threshold. - - EF +2006-03-15 + Fixed bug in MaxNLocator revealed by daigos@infinito.it. The main change + is that Locator.nonsingular now adjusts vmin and vmax if they are nearly + the same, not just if they are equal. A new kwarg, "tiny", sets the + threshold. - EF -2006-03-14 Added import of compatibility library for newer numpy - linear_algebra - TEO +2006-03-14 + Added import of compatibility library for newer numpy linear_algebra - TEO -2006-03-12 Extended "load" function to support individual columns and - moved "load" and "save" into matplotlib.mlab so they can be - used outside of pylab -- see examples/load_converter.py - - JDH +2006-03-12 + Extended "load" function to support individual columns and moved "load" and + "save" into matplotlib.mlab so they can be used outside of pylab -- see + examples/load_converter.py - JDH -2006-03-12 Added AutoDateFormatter and AutoDateLocator submitted - by James Evans. Try the load_converter.py example for a - demo. - ADS +2006-03-12 + Added AutoDateFormatter and AutoDateLocator submitted by James Evans. Try + the load_converter.py example for a demo. - ADS -2006-03-11 Added subprocess module from python-2.4 - DSD +2006-03-11 + Added subprocess module from python-2.4 - DSD -2006-03-11 Fixed landscape orientation support with the usetex - option. The backend_ps print_figure method was - getting complicated, I added a _print_figure_tex - method to maintain some degree of sanity - DSD +2006-03-11 + Fixed landscape orientation support with the usetex option. The backend_ps + print_figure method was getting complicated, I added a _print_figure_tex + method to maintain some degree of sanity - DSD -2006-03-11 Added "papertype" savefig kwarg for setting - postscript papersizes. papertype and ps.papersize - rc setting can also be set to "auto" to autoscale - pagesizes - DSD +2006-03-11 + Added "papertype" savefig kwarg for setting postscript papersizes. + papertype and ps.papersize rc setting can also be set to "auto" to + autoscale pagesizes - DSD -2006-03-09 Apply P-J's patch to make pstoeps work on windows - patch report # 1445612 - DSD +2006-03-09 + Apply P-J's patch to make pstoeps work on windows patch report # 1445612 - + DSD -2006-03-09 Make backend rc parameter case-insensitive - DSD +2006-03-09 + Make backend rc parameter case-insensitive - DSD -2006-03-07 Fixed bug in backend_ps related to C0-C6 papersizes, - which were causing problems with postscript viewers. - Supported page sizes include letter, legal, ledger, - A0-A10, and B0-B10 - DSD +2006-03-07 + Fixed bug in backend_ps related to C0-C6 papersizes, which were causing + problems with postscript viewers. Supported page sizes include letter, + legal, ledger, A0-A10, and B0-B10 - DSD ------------------------------------ -2006-03-07 Released 0.87.1 +2006-03-07 + Released 0.87.1 -2006-03-04 backend_cairo.py: - fix get_rgb() bug reported by Keith Briggs. - Require pycairo 1.0.2. - Support saving png to file-like objects. - SC +2006-03-04 + backend_cairo.py: fix get_rgb() bug reported by Keith Briggs. Require + pycairo 1.0.2. Support saving png to file-like objects. - SC -2006-03-03 Fixed pcolor handling of vmin, vmax - EF +2006-03-03 + Fixed pcolor handling of vmin, vmax - EF -2006-03-02 improve page sizing with usetex with the latex - geometry package. Closes bug # 1441629 - DSD +2006-03-02 + improve page sizing with usetex with the latex geometry package. Closes bug + # 1441629 - DSD -2006-03-02 Fixed dpi problem with usetex png output. Accepted a - modified version of patch # 1441809 - DSD +2006-03-02 + Fixed dpi problem with usetex png output. Accepted a modified version of + patch # 1441809 - DSD -2006-03-01 Fixed axis('scaled') to deal with case xmax < xmin - JSWHIT +2006-03-01 + Fixed axis('scaled') to deal with case xmax < xmin - JSWHIT -2006-03-01 Added reversed colormaps (with '_r' appended to name) - JSWHIT +2006-03-01 + Added reversed colormaps (with '_r' appended to name) - JSWHIT -2006-02-27 Improved eps bounding boxes with usetex - DSD +2006-02-27 + Improved eps bounding boxes with usetex - DSD -2006-02-27 Test svn commit, again! +2006-02-27 + Test svn commit, again! -2006-02-27 Fixed two dependency checking bugs related to usetex - on Windows - DSD +2006-02-27 + Fixed two dependency checking bugs related to usetex on Windows - DSD -2006-02-27 Made the rc deprecation warnings a little more human - readable. +2006-02-27 + Made the rc deprecation warnings a little more human readable. -2006-02-26 Update the previous gtk.main_quit() bug fix to use gtk.main_level() - - SC +2006-02-26 + Update the previous gtk.main_quit() bug fix to use gtk.main_level() - SC -2006-02-24 Implemented alpha support in contour and contourf - EF +2006-02-24 + Implemented alpha support in contour and contourf - EF -2006-02-22 Fixed gtk main quit bug when quit was called before - mainloop. - JDH +2006-02-22 + Fixed gtk main quit bug when quit was called before mainloop. - JDH -2006-02-22 Small change to colors.py to workaround apparent - bug in numpy masked array module - JSWHIT +2006-02-22 + Small change to colors.py to workaround apparent bug in numpy masked array + module - JSWHIT -2006-02-22 Fixed bug in ScalarMappable.to_rgba() reported by - Ray Jones, and fixed incorrect fix found by Jeff - Whitaker - EF +2006-02-22 + Fixed bug in ScalarMappable.to_rgba() reported by Ray Jones, and fixed + incorrect fix found by Jeff Whitaker - EF -------------------------------- -2006-02-22 Released 0.87 +2006-02-22 + Released 0.87 -2006-02-21 Fixed portrait/landscape orientation in postscript backend - DSD +2006-02-21 + Fixed portrait/landscape orientation in postscript backend - DSD -2006-02-21 Fix bug introduced in yesterday's bug fix - SC +2006-02-21 + Fix bug introduced in yesterday's bug fix - SC -2006-02-20 backend_gtk.py FigureCanvasGTK.draw(): fix bug reported by - David Tremouilles - SC +2006-02-20 + backend_gtk.py FigureCanvasGTK.draw(): fix bug reported by David + Tremouilles - SC -2006-02-20 Remove the "pygtk.require('2.4')" error from - examples/embedding_in_gtk2.py - SC +2006-02-20 + Remove the "pygtk.require('2.4')" error from examples/embedding_in_gtk2.py + - SC -2006-02-18 backend_gtk.py FigureCanvasGTK.draw(): simplify to use (rather than - duplicate) the expose_event() drawing code - SC +2006-02-18 + backend_gtk.py FigureCanvasGTK.draw(): simplify to use (rather than + duplicate) the expose_event() drawing code - SC -2006-02-12 Added stagger or waterfall plot capability to LineCollection; - illustrated in examples/collections.py. - EF +2006-02-12 + Added stagger or waterfall plot capability to LineCollection; illustrated + in examples/collections.py. - EF -2006-02-11 Massive cleanup of the usetex code in the postscript backend. Possibly - fixed the clipping issue users were reporting with older versions of - ghostscript - DSD +2006-02-11 + Massive cleanup of the usetex code in the postscript backend. Possibly + fixed the clipping issue users were reporting with older versions of + ghostscript - DSD -2006-02-11 Added autolim kwarg to axes.add_collection. Changed - collection get_verts() methods accordingly. - EF +2006-02-11 + Added autolim kwarg to axes.add_collection. Changed collection get_verts() + methods accordingly. - EF -2006-02-09 added a temporary rc parameter text.dvipnghack, to allow Mac users to get nice - results with the usetex option. - DSD +2006-02-09 + added a temporary rc parameter text.dvipnghack, to allow Mac users to get + nice results with the usetex option. - DSD -2006-02-09 Fixed a bug related to setting font sizes with the usetex option. - DSD +2006-02-09 + Fixed a bug related to setting font sizes with the usetex option. - DSD -2006-02-09 Fixed a bug related to usetex's latex code. - DSD +2006-02-09 + Fixed a bug related to usetex's latex code. - DSD -2006-02-09 Modified behavior of font.size rc setting. You should define font.size in pts, - which will set the "medium" or default fontsize. Special text sizes like axis - labels or tick labels can be given relative font sizes like small, large, - x-large, etc. and will scale accordingly. - DSD +2006-02-09 + Modified behavior of font.size rc setting. You should define font.size in + pts, which will set the "medium" or default fontsize. Special text sizes + like axis labels or tick labels can be given relative font sizes like + small, large, x-large, etc. and will scale accordingly. - DSD -2006-02-08 Added py2exe specific datapath check again. Also added new - py2exe helper function get_py2exe_datafiles for use in py2exe - setup.py scripts. - CM +2006-02-08 + Added py2exe specific datapath check again. Also added new py2exe helper + function get_py2exe_datafiles for use in py2exe setup.py scripts. - CM -2006-02-02 Added box function to pylab +2006-02-02 + Added box function to pylab -2006-02-02 Fixed a problem in setupext.py, tk library formatted in unicode - caused build problems - DSD +2006-02-02 + Fixed a problem in setupext.py, tk library formatted in unicode caused + build problems - DSD -2006-02-01 Dropped TeX engine support in usetex to focus on LaTeX. - DSD +2006-02-01 + Dropped TeX engine support in usetex to focus on LaTeX. - DSD -2006-01-29 Improved usetex option to respect the serif, sans-serif, monospace, - and cursive rc settings. Removed the font.latex.package rc setting, - it is no longer required - DSD +2006-01-29 + Improved usetex option to respect the serif, sans-serif, monospace, and + cursive rc settings. Removed the font.latex.package rc setting, it is no + longer required - DSD -2006-01-29 Fixed tex's caching to include font.family rc information - DSD +2006-01-29 + Fixed tex's caching to include font.family rc information - DSD -2006-01-29 Fixed subpixel rendering bug in \*Agg that was causing - uneven gridlines - JDH +2006-01-29 + Fixed subpixel rendering bug in \*Agg that was causing uneven gridlines - + JDH -2006-01-28 Added fontcmd to backend_ps's RendererPS.draw_tex, to support other - font families in eps output - DSD +2006-01-28 + Added fontcmd to backend_ps's RendererPS.draw_tex, to support other font + families in eps output - DSD -2006-01-28 Added MaxNLocator to ticker.py, and changed contour.py to - use it by default. - EF +2006-01-28 + Added MaxNLocator to ticker.py, and changed contour.py to use it by + default. - EF -2006-01-28 Added fontcmd to backend_ps's RendererPS.draw_tex, to support other - font families in eps output - DSD +2006-01-28 + Added fontcmd to backend_ps's RendererPS.draw_tex, to support other font + families in eps output - DSD -2006-01-27 Buffered reading of matplotlibrc parameters in order to allow - 'verbose' settings to be processed first (allows verbose.report - during rc validation process) - DSD +2006-01-27 + Buffered reading of matplotlibrc parameters in order to allow 'verbose' + settings to be processed first (allows verbose.report during rc validation + process) - DSD -2006-01-27 Removed setuptools support from setup.py and created a - separate setupegg.py file to replace it. - CM +2006-01-27 + Removed setuptools support from setup.py and created a separate setupegg.py + file to replace it. - CM -2006-01-26 Replaced the ugly datapath logic with a cleaner approach from - http://wiki.python.org/moin/DistutilsInstallDataScattered. - Overrides the install_data command. - CM +2006-01-26 + Replaced the ugly datapath logic with a cleaner approach from + http://wiki.python.org/moin/DistutilsInstallDataScattered. Overrides the + install_data command. - CM -2006-01-24 Don't use character typecodes in cntr.c --- changed to use - defined typenumbers instead. - TEO +2006-01-24 + Don't use character typecodes in cntr.c --- changed to use defined + typenumbers instead. - TEO -2006-01-24 Fixed some bugs in usetex's and ps.usedistiller's dependency +2006-01-24 + Fixed some bugs in usetex's and ps.usedistiller's dependency -2006-01-24 Added masked array support to scatter - EF +2006-01-24 + Added masked array support to scatter - EF -2006-01-24 Fixed some bugs in usetex's and ps.usedistiller's dependency - checking - DSD +2006-01-24 + Fixed some bugs in usetex's and ps.usedistiller's dependency checking - DSD ------------------------------- -2006-01-24 Released 0.86.2 - -2006-01-20 Added a converters dict to pylab load to convert selected - columns to float -- especially useful for files with date - strings, uses a datestr2num converter - JDH +2006-01-24 + Released 0.86.2 -2006-01-20 Added datestr2num to matplotlib dates to convert a string - or sequence of strings to a matplotlib datenum +2006-01-20 + Added a converters dict to pylab load to convert selected columns to float + -- especially useful for files with date strings, uses a datestr2num + converter - JDH -2006-01-18 Added quadrilateral pcolormesh patch 1409190 by Alex Mont - and Paul Kienzle -- this is \*Agg only for now. See - examples/quadmesh_demo.py - JDH +2006-01-20 + Added datestr2num to matplotlib dates to convert a string or sequence of + strings to a matplotlib datenum -2006-01-18 Added Jouni's boxplot patch - JDH +2006-01-18 + Added quadrilateral pcolormesh patch 1409190 by Alex Mont and Paul Kienzle + -- this is \*Agg only for now. See examples/quadmesh_demo.py - JDH -2006-01-18 Added comma delimiter for pylab save - JDH +2006-01-18 + Added Jouni's boxplot patch - JDH -2006-01-12 Added Ryan's legend patch - JDH +2006-01-18 + Added comma delimiter for pylab save - JDH +2006-01-12 + Added Ryan's legend patch - JDH -2006-1-12 Fixed numpy / numeric to use .dtype.char to keep in SYNC with numpy SVN +2006-01-12 + Fixed numpy / numeric to use .dtype.char to keep in SYNC with numpy SVN --------------------------- -2006-1-11 Released 0.86.1 +2006-01-11 + Released 0.86.1 -2006-1-11 Fixed setup.py for win32 build and added rc template to the MANIFEST.in +2006-01-11 + Fixed setup.py for win32 build and added rc template to the MANIFEST.in -2006-1-10 Added xpdf distiller option. matplotlibrc ps.usedistiller can now be - none, false, ghostscript, or xpdf. Validation checks for - dependencies. This needs testing, but the xpdf option should produce - the highest-quality output and small file sizes - DSD +2006-01-10 + Added xpdf distiller option. matplotlibrc ps.usedistiller can now be none, + false, ghostscript, or xpdf. Validation checks for dependencies. This needs + testing, but the xpdf option should produce the highest-quality output and + small file sizes - DSD -2006-01-10 For the usetex option, backend_ps now does all the LaTeX work in the - os's temp directory - DSD +2006-01-10 + For the usetex option, backend_ps now does all the LaTeX work in the os's + temp directory - DSD -2006-1-10 Added checks for usetex dependencies. - DSD +2006-01-10 + Added checks for usetex dependencies. - DSD --------------------------------- -2006-1-9 Released 0.86 +2006-01-09 + Released 0.86 -2006-1-4 Changed to support numpy (new name for scipy_core) - TEO +2006-01-04 + Changed to support numpy (new name for scipy_core) - TEO -2006-1-4 Added Mark's scaled axes patch for shared axis +2006-01-04 + Added Mark's scaled axes patch for shared axis -2005-12-28 Added Chris Barker's build_wxagg patch - JDH +2005-12-28 + Added Chris Barker's build_wxagg patch - JDH -2005-12-27 Altered numerix/scipy to support new scipy package - structure - TEO +2005-12-27 + Altered numerix/scipy to support new scipy package structure - TEO -2005-12-20 Fixed Jame's Boyles date tick reversal problem - JDH +2005-12-20 + Fixed Jame's Boyles date tick reversal problem - JDH -2005-12-20 Added Jouni's rc patch to support lists of keys to set on - - JDH +2005-12-20 + Added Jouni's rc patch to support lists of keys to set on - JDH -2005-12-12 Updated pyparsing and mathtext for some speed enhancements - (Thanks Paul McGuire) and minor fixes to scipy numerix and - setuptools +2005-12-12 + Updated pyparsing and mathtext for some speed enhancements (Thanks Paul + McGuire) and minor fixes to scipy numerix and setuptools -2005-12-12 Matplotlib data is now installed as package_data in - the matplotlib module. This gets rid of checking the - many possibilities in matplotlib._get_data_path() - CM +2005-12-12 + Matplotlib data is now installed as package_data in the matplotlib module. + This gets rid of checking the many possibilities in + matplotlib._get_data_path() - CM -2005-12-11 Support for setuptools/pkg_resources to build and use - matplotlib as an egg. Still allows matplotlib to exist - using a traditional distutils install. - ADS +2005-12-11 + Support for setuptools/pkg_resources to build and use matplotlib as an egg. + Still allows matplotlib to exist using a traditional distutils install. - + ADS -2005-12-03 Modified setup to build matplotlibrc based on compile time - findings. It will set numerix in the order of scipy, - numarray, Numeric depending on which are founds, and - backend as in preference order GTKAgg, WXAgg, TkAgg, GTK, - Agg, PS +2005-12-03 + Modified setup to build matplotlibrc based on compile time findings. It + will set numerix in the order of scipy, numarray, Numeric depending on + which are founds, and backend as in preference order GTKAgg, WXAgg, TkAgg, + GTK, Agg, PS -2005-12-03 Modified scipy patch to support Numeric, scipy and numarray - Some work remains to be done because some of the scipy - imports are broken if only the core is installed. e.g., - apparently we need from scipy.basic.fftpack import * rather - than from scipy.fftpack import * +2005-12-03 + Modified scipy patch to support Numeric, scipy and numarray Some work + remains to be done because some of the scipy imports are broken if only the + core is installed. e.g., apparently we need from scipy.basic.fftpack + import * rather than from scipy.fftpack import * -2005-12-03 Applied some fixes to Nicholas Young's nonuniform image - patch +2005-12-03 + Applied some fixes to Nicholas Young's nonuniform image patch -2005-12-01 Applied Alex Gontmakher hatch patch - PS only for now +2005-12-01 + Applied Alex Gontmakher hatch patch - PS only for now -2005-11-30 Added Rob McMullen's EMF patch +2005-11-30 + Added Rob McMullen's EMF patch -2005-11-30 Added Daishi's patch for scipy +2005-11-30 + Added Daishi's patch for scipy -2005-11-30 Fixed out of bounds draw markers segfault in agg +2005-11-30 + Fixed out of bounds draw markers segfault in agg -2005-11-28 Got TkAgg blitting working 100% (cross fingers) correctly. - CM +2005-11-28 + Got TkAgg blitting working 100% (cross fingers) correctly. - CM -2005-11-27 Multiple changes in cm.py, colors.py, figure.py, image.py, - contour.py, contour_demo.py; new _cm.py, examples/image_masked.py. - 1) Separated the color table data from cm.py out into - a new file, _cm.py, to make it easier to find the actual - code in cm.py and to add new colormaps. Also added - some line breaks to the color data dictionaries. Everything - from _cm.py is imported by cm.py, so the split should be - transparent. - 2) Enabled automatic generation of a colormap from - a list of colors in contour; see modified - examples/contour_demo.py. - 3) Support for imshow of a masked array, with the - ability to specify colors (or no color at all) for - masked regions, and for regions that are above or - below the normally mapped region. See - examples/image_masked.py. - 4) In support of the above, added two new classes, - ListedColormap, and no_norm, to colors.py, and modified - the Colormap class to include common functionality. Added - a clip kwarg to the normalize class. Reworked color - handling in contour.py, especially in the ContourLabeller - mixin. - - EF +2005-11-27 + Multiple changes in cm.py, colors.py, figure.py, image.py, contour.py, + contour_demo.py; new _cm.py, examples/image_masked.py. -2005-11-25 Changed text.py to ensure color is hashable. EF + 1. Separated the color table data from cm.py out into a new file, _cm.py, + to make it easier to find the actual code in cm.py and to add new + colormaps. Also added some line breaks to the color data dictionaries. + Everything from _cm.py is imported by cm.py, so the split should be + transparent. + 2. Enabled automatic generation of a colormap from a list of colors in + contour; see modified examples/contour_demo.py. + 3. Support for imshow of a masked array, with the ability to specify colors + (or no color at all) for masked regions, and for regions that are above + or below the normally mapped region. See examples/image_masked.py. + 4. In support of the above, added two new classes, ListedColormap, and + no_norm, to colors.py, and modified the Colormap class to include common + functionality. Added a clip kwarg to the normalize class. Reworked + color handling in contour.py, especially in the ContourLabeller mixin. --------------------------------- + - EF -2005-11-16 Released 0.85 +2005-11-25 + Changed text.py to ensure color is hashable. EF -2005-11-16 Changed the default default linewidth in rc to 1.0 +-------------------------------- -2005-11-16 Replaced agg_to_gtk_drawable with pure pygtk pixbuf code in - backend_gtkagg. When the equivalent is doe for blit, the - agg extension code will no longer be needed +2005-11-16 + Released 0.85 -2005-11-16 Added a maxdict item to cbook to prevent caches from - growing w/o bounds +2005-11-16 + Changed the default linewidth in rc to 1.0 -2005-11-15 Fixed a colorup/colordown reversal bug in finance.py -- - Thanks Gilles +2005-11-16 + Replaced agg_to_gtk_drawable with pure pygtk pixbuf code in backend_gtkagg. + When the equivalent is doe for blit, the agg extension code will no longer + be needed -2005-11-15 Applied Jouni K Steppanen's boxplot patch SF patch#1349997 - - JDH +2005-11-16 + Added a maxdict item to cbook to prevent caches from growing w/o bounds +2005-11-15 + Fixed a colorup/colordown reversal bug in finance.py -- Thanks Gilles -2005-11-09 added axisbelow attr for Axes to determine whether ticks and such - are above or below the actors +2005-11-15 + Applied Jouni K Steppanen's boxplot patch SF patch#1349997 - JDH -2005-11-08 Added Nicolas' irregularly spaced image patch +2005-11-09 + added axisbelow attr for Axes to determine whether ticks and such are above + or below the actors +2005-11-08 + Added Nicolas' irregularly spaced image patch -2005-11-08 Deprecated HorizontalSpanSelector and replaced with - SpanSelection that takes a third arg, direction. The - new SpanSelector supports horizontal and vertical span - selection, and the appropriate min/max is returned. - CM +2005-11-08 + Deprecated HorizontalSpanSelector and replaced with SpanSelection that + takes a third arg, direction. The new SpanSelector supports horizontal and + vertical span selection, and the appropriate min/max is returned. - CM -2005-11-08 Added lineprops dialog for gtk +2005-11-08 + Added lineprops dialog for gtk -2005-11-03 Added FIFOBuffer class to mlab to support real time feeds - and examples/fifo_buffer.py +2005-11-03 + Added FIFOBuffer class to mlab to support real time feeds and + examples/fifo_buffer.py -2005-11-01 Contributed Nickolas Young's patch for afm mathtext to - support mathtext based upon the standard postscript Symbol - font when ps.usetex = True. +2005-11-01 + Contributed Nickolas Young's patch for afm mathtext to support mathtext + based upon the standard postscript Symbol font when ps.usetex = True. -2005-10-26 Added support for scatter legends - thanks John Gill +2005-10-26 + Added support for scatter legends - thanks John Gill -2005-10-20 Fixed image clipping bug that made some tex labels - disappear. JDH +2005-10-20 + Fixed image clipping bug that made some tex labels disappear. JDH -2005-10-14 Removed sqrt from dvipng 1.6 alpha channel mask. +2005-10-14 + Removed sqrt from dvipng 1.6 alpha channel mask. -2005-10-14 Added width kwarg to hist function +2005-10-14 + Added width kwarg to hist function -2005-10-10 Replaced all instances of os.rename with shutil.move +2005-10-10 + Replaced all instances of os.rename with shutil.move -2005-10-05 Added Michael Brady's ydate patch +2005-10-05 + Added Michael Brady's ydate patch -2005-10-04 Added rkern's texmanager patch +2005-10-04 + Added rkern's texmanager patch -2005-09-25 contour.py modified to use a single ContourSet class - that handles filled contours, line contours, and labels; - added keyword arg (clip_ends) to contourf. - Colorbar modified to work with new ContourSet object; - if the ContourSet has lines rather than polygons, the - colorbar will follow suit. Fixed a bug introduced in - 0.84, in which contourf(...,colors=...) was broken - EF +2005-09-25 + contour.py modified to use a single ContourSet class that handles filled + contours, line contours, and labels; added keyword arg (clip_ends) to + contourf. Colorbar modified to work with new ContourSet object; if the + ContourSet has lines rather than polygons, the colorbar will follow suit. + Fixed a bug introduced in 0.84, in which contourf(...,colors=...) was + broken - EF ------------------------------- -2005-09-19 Released 0.84 +2005-09-19 + Released 0.84 -2005-09-14 Added a new 'resize_event' which triggers a callback with a - backend_bases.ResizeEvent object - JDH +2005-09-14 + Added a new 'resize_event' which triggers a callback with a + backend_bases.ResizeEvent object - JDH -2005-09-14 font_manager.py: removed chkfontpath from x11FontDirectory() - SC +2005-09-14 + font_manager.py: removed chkfontpath from x11FontDirectory() - SC -2005-09-14 Factored out auto date locator/formatter factory code into - matplotlib.date.date_ticker_factory; applies John Bryne's - quiver patch. +2005-09-14 + Factored out auto date locator/formatter factory code into + matplotlib.date.date_ticker_factory; applies John Bryne's quiver patch. -2005-09-13 Added Mark's axes positions history patch #1286915 +2005-09-13 + Added Mark's axes positions history patch #1286915 -2005-09-09 Added support for auto canvas resizing with - fig.set_figsize_inches(9,5,forward=True) # inches - OR - fig.resize(400,300) # pixels +2005-09-09 + Added support for auto canvas resizing with:: -2005-09-07 figure.py: update Figure.draw() to use the updated - renderer.draw_image() so that examples/figimage_demo.py works again. - examples/stock_demo.py: remove data_clipping (which no longer - exists) - SC + fig.set_figsize_inches(9,5,forward=True) # inches -2005-09-06 Added Eric's tick.direction patch: in or out in rc + OR:: -2005-09-06 Added Martin's rectangle selector widget + fig.resize(400,300) # pixels -2005-09-04 Fixed a logic err in text.py that was preventing rgxsuper - from matching - JDH +2005-09-07 + figure.py: update Figure.draw() to use the updated renderer.draw_image() so + that examples/figimage_demo.py works again. examples/stock_demo.py: remove + data_clipping (which no longer exists) - SC -2005-08-29 Committed Ken's wx blit patch #1275002 +2005-09-06 + Added Eric's tick.direction patch: in or out in rc -2005-08-26 colorbar modifications - now uses contourf instead of imshow - so that colors used by contourf are displayed correctly. - Added two new keyword args (cspacing and clabels) that are - only relevant for ContourMappable images - JSWHIT +2005-09-06 + Added Martin's rectangle selector widget -2005-08-24 Fixed a PS image bug reported by Darren - JDH +2005-09-04 + Fixed a logic err in text.py that was preventing rgxsuper from matching - + JDH -2005-08-23 colors.py: change hex2color() to accept unicode strings as well as - normal strings. Use isinstance() instead of types.IntType etc - SC +2005-08-29 + Committed Ken's wx blit patch #1275002 -2005-08-16 removed data_clipping line and rc property - JDH +2005-08-26 + colorbar modifications - now uses contourf instead of imshow so that colors + used by contourf are displayed correctly. Added two new keyword args + (cspacing and clabels) that are only relevant for ContourMappable images - + JSWHIT -2005-08-22 backend_svg.py: Remove redundant "x=0.0 y=0.0" from svg element. - Increase svg version from 1.0 to 1.1. Add viewBox attribute to svg - element to allow SVG documents to scale-to-fit into an arbitrary - viewport - SC +2005-08-24 + Fixed a PS image bug reported by Darren - JDH -2005-08-16 Added Eric's dot marker patch - JDH +2005-08-23 + colors.py: change hex2color() to accept unicode strings as well as normal + strings. Use isinstance() instead of types.IntType etc - SC -2005-08-08 Added blitting/animation for TkAgg - CM +2005-08-16 + removed data_clipping line and rc property - JDH -2005-08-05 Fixed duplicate tickline bug - JDH - -2005-08-05 Fixed a GTK animation bug that cropped up when doing - animations in gtk//gtkagg canvases that had widgets packed - above them - -2005-08-05 Added Clovis Goldemberg patch to the tk save dialog - -2005-08-04 Removed origin kwarg from backend.draw_image. origin is - handled entirely by the frontend now. - -2005-07-03 Fixed a bug related to TeX commands in backend_ps - -2005-08-03 Fixed SVG images to respect upper and lower origins. - -2005-08-03 Added flipud method to image and removed it from to_str. - -2005-07-29 Modified figure.figaspect to take an array or number; - modified backend_svg to write utf-8 - JDH - -2005-07-30 backend_svg.py: embed png image files in svg rather than linking - to a separate png file, fixes bug #1245306 (thanks to Norbert Nemec - for the patch) - SC - ---------------------------- +2005-08-22 + backend_svg.py: Remove redundant "x=0.0 y=0.0" from svg element. Increase + svg version from 1.0 to 1.1. Add viewBox attribute to svg element to allow + SVG documents to scale-to-fit into an arbitrary viewport - SC -2005-07-29 Released 0.83.2 +2005-08-16 + Added Eric's dot marker patch - JDH -2005-07-27 Applied SF patch 1242648: minor rounding error in - IndexDateFormatter in dates.py +2005-08-08 + Added blitting/animation for TkAgg - CM -2005-07-27 Applied sf patch 1244732: Scale axis such that circle - looks like circle - JDH +2005-08-05 + Fixed duplicate tickline bug - JDH -2005-07-29 Improved message reporting in texmanager and backend_ps - DSD +2005-08-05 + Fixed a GTK animation bug that cropped up when doing animations in + gtk//gtkagg canvases that had widgets packed above them -2005-07-28 backend_gtk.py: update FigureCanvasGTK.draw() (needed due to the - recent expose_event() change) so that examples/anim.py works in the - usual way - SC +2005-08-05 + Added Clovis Goldemberg patch to the tk save dialog -2005-07-26 Added new widgets Cursor and HorizontalSpanSelector to - matplotlib.widgets. See examples/widgets/cursor.py and - examples/widgets/span_selector.py - JDH +2005-08-04 + Removed origin kwarg from backend.draw_image. origin is handled entirely + by the frontend now. -2005-07-26 added draw event to mpl event hierarchy -- triggered on - figure.draw +2005-07-03 + Fixed a bug related to TeX commands in backend_ps -2005-07-26 backend_gtk.py: allow 'f' key to toggle window fullscreen mode +2005-08-03 + Fixed SVG images to respect upper and lower origins. -2005-07-26 backend_svg.py: write "<.../>" elements all on one line and remove - surplus spaces - SC +2005-08-03 + Added flipud method to image and removed it from to_str. -2005-07-25 backend_svg.py: simplify code by deleting GraphicsContextSVG and - RendererSVG.new_gc(), and moving the gc.get_capstyle() code into - RendererSVG._get_gc_props_svg() - SC +2005-07-29 + Modified figure.figaspect to take an array or number; modified backend_svg + to write utf-8 - JDH -2005-07-24 backend_gtk.py: call FigureCanvasBase.motion_notify_event() on - all motion-notify-events, not just ones where a modifier key or - button has been pressed (fixes bug report from Niklas Volbers) - SC +2005-07-30 + backend_svg.py: embed png image files in svg rather than linking to a + separate png file, fixes bug #1245306 (thanks to Norbert Nemec for the + patch) - SC -2005-07-24 backend_gtk.py: modify print_figure() use own pixmap, fixing - problems where print_figure() overwrites the display pixmap. - return False from all button/key etc events - to allow the event - to propagate further - SC - -2005-07-23 backend_gtk.py: change expose_event from using set_back_pixmap(); - clear() to draw_drawable() - SC - -2005-07-23 backend_gtk.py: removed pygtk.require() - matplotlib/__init__.py: delete 'FROZEN' and 'McPLError' which are - no longer used - SC - -2005-07-22 backend_gdk.py: removed pygtk.require() - SC - -2005-07-21 backend_svg.py: Remove unused imports. Remove methods doc strings - which just duplicate the docs from backend_bases.py. Rename - draw_mathtext to _draw_mathtext. - SC - -2005-07-17 examples/embedding_in_gtk3.py: new example demonstrating placing - a FigureCanvas in a gtk.ScrolledWindow - SC - -2005-07-14 Fixed a Windows related bug (#1238412) in texmanager - DSD - -2005-07-11 Fixed color kwarg bug, setting color=1 or 0 caused an - exception - DSD - -2005-07-07 Added Eric's MA set_xdata Line2D fix - JDH - -2005-07-06 Made HOME/.matplotlib the new config dir where the - matplotlibrc file, the ttf.cache, and the tex.cache live. - The new default filenames in .matplotlib have no leading - dot and are not hidden. e.g., the new names are matplotlibrc - tex.cache ttffont.cache. This is how ipython does it so it - must be right. If old files are found, a warning is issued - and they are moved to the new location. Also fixed - texmanager to put all files, including temp files in - ~/.matplotlib/tex.cache, which allows you to usetex in - non-writable dirs. - -2005-07-05 Fixed bug #1231611 in subplots adjust layout. The problem - was that the text caching mechanism was not using the - transformation affine in the key. - JDH - -2005-07-05 Fixed default backend import problem when using API (SF bug - # 1209354 - see API_CHANGES for more info - JDH - -2005-07-04 backend_gtk.py: require PyGTK version 2.0.0 or higher - SC - -2005-06-30 setupext.py: added numarray_inc_dirs for building against - numarray when not installed in standard location - ADS - -2005-06-27 backend_svg.py: write figure width, height as int, not float. - Update to fix some of the pychecker warnings - SC - -2005-06-23 Updated examples/agg_test.py to demonstrate curved paths - and fills - JDH +--------------------------- -2005-06-21 Moved some texmanager and backend_agg tex caching to class - level rather than instance level - JDH +2005-07-29 + Released 0.83.2 -2005-06-20 setupext.py: fix problem where _nc_backend_gdk is installed to the - wrong directory - SC +2005-07-27 + Applied SF patch 1242648: minor rounding error in IndexDateFormatter in + dates.py -2005-06-19 Added 10.4 support for CocoaAgg. - CM +2005-07-27 + Applied sf patch 1244732: Scale axis such that circle looks like circle - + JDH -2005-06-18 Move Figure.get_width_height() to FigureCanvasBase and return - int instead of float. - SC +2005-07-29 + Improved message reporting in texmanager and backend_ps - DSD -2005-06-18 Applied Ted Drain's QtAgg patch: 1) Changed the toolbar to - be a horizontal bar of push buttons instead of a QToolbar - and updated the layout algorithms in the main window - accordingly. This eliminates the ability to drag and drop - the toolbar and detach it from the window. 2) Updated the - resize algorithm in the main window to show the correct - size for the plot widget as requested. This works almost - correctly right now. It looks to me like the final size of - the widget is off by the border of the main window but I - haven't figured out a way to get that information yet. We - could just add a small margin to the new size but that - seems a little hacky. 3) Changed the x/y location label to - be in the toolbar like the Tk backend instead of as a - status line at the bottom of the widget. 4) Changed the - toolbar pixmaps to use the ppm files instead of the png - files. I noticed that the Tk backend buttons looked much - nicer and it uses the ppm files so I switched them. +2005-07-28 + backend_gtk.py: update FigureCanvasGTK.draw() (needed due to the recent + expose_event() change) so that examples/anim.py works in the usual way - SC -2005-06-17 Modified the gtk backend to not queue mouse motion events. - This allows for live updates when dragging a slider. - CM +2005-07-26 + Added new widgets Cursor and HorizontalSpanSelector to matplotlib.widgets. + See examples/widgets/cursor.py and examples/widgets/span_selector.py - JDH -2005-06-17 Added starter CocoaAgg backend. Only works on OS 10.3 for - now and requires PyObjC. (10.4 is high priority) - CM +2005-07-26 + added draw event to mpl event hierarchy -- triggered on figure.draw -2005-06-17 Upgraded pyparsing and applied Paul McGuire's suggestions - for speeding things up. This more than doubles the speed - of mathtext in my simple tests. JDH +2005-07-26 + backend_gtk.py: allow 'f' key to toggle window fullscreen mode -2005-06-16 Applied David Cooke's subplot make_key patch +2005-07-26 + backend_svg.py: write "<.../>" elements all on one line and remove surplus + spaces - SC + +2005-07-25 + backend_svg.py: simplify code by deleting GraphicsContextSVG and + RendererSVG.new_gc(), and moving the gc.get_capstyle() code into + RendererSVG._get_gc_props_svg() - SC + +2005-07-24 + backend_gtk.py: call FigureCanvasBase.motion_notify_event() on all + motion-notify-events, not just ones where a modifier key or button has been + pressed (fixes bug report from Niklas Volbers) - SC + +2005-07-24 + backend_gtk.py: modify print_figure() use own pixmap, fixing problems where + print_figure() overwrites the display pixmap. return False from all + button/key etc events - to allow the event to propagate further - SC + +2005-07-23 + backend_gtk.py: change expose_event from using set_back_pixmap(); clear() + to draw_drawable() - SC + +2005-07-23 + backend_gtk.py: removed pygtk.require() matplotlib/__init__.py: delete + 'FROZEN' and 'McPLError' which are no longer used - SC + +2005-07-22 + backend_gdk.py: removed pygtk.require() - SC + +2005-07-21 + backend_svg.py: Remove unused imports. Remove methods doc strings which + just duplicate the docs from backend_bases.py. Rename draw_mathtext to + _draw_mathtext. - SC + +2005-07-17 + examples/embedding_in_gtk3.py: new example demonstrating placing a + FigureCanvas in a gtk.ScrolledWindow - SC + +2005-07-14 + Fixed a Windows related bug (#1238412) in texmanager - DSD + +2005-07-11 + Fixed color kwarg bug, setting color=1 or 0 caused an exception - DSD + +2005-07-07 + Added Eric's MA set_xdata Line2D fix - JDH + +2005-07-06 + Made HOME/.matplotlib the new config dir where the matplotlibrc file, the + ttf.cache, and the tex.cache live. The new default filenames in + .matplotlib have no leading dot and are not hidden. e.g., the new names + are matplotlibrc tex.cache ttffont.cache. This is how ipython does it so + it must be right. If old files are found, a warning is issued and they are + moved to the new location. Also fixed texmanager to put all files, + including temp files in ~/.matplotlib/tex.cache, which allows you to usetex + in non-writable dirs. + +2005-07-05 + Fixed bug #1231611 in subplots adjust layout. The problem was that the + text caching mechanism was not using the transformation affine in the key. + - JDH + +2005-07-05 + Fixed default backend import problem when using API (SF bug # 1209354 - + see API_CHANGES for more info - JDH + +2005-07-04 + backend_gtk.py: require PyGTK version 2.0.0 or higher - SC + +2005-06-30 + setupext.py: added numarray_inc_dirs for building against numarray when not + installed in standard location - ADS + +2005-06-27 + backend_svg.py: write figure width, height as int, not float. Update to + fix some of the pychecker warnings - SC + +2005-06-23 + Updated examples/agg_test.py to demonstrate curved paths and fills - JDH + +2005-06-21 + Moved some texmanager and backend_agg tex caching to class level rather + than instance level - JDH + +2005-06-20 + setupext.py: fix problem where _nc_backend_gdk is installed to the wrong + directory - SC + +2005-06-19 + Added 10.4 support for CocoaAgg. - CM + +2005-06-18 + Move Figure.get_width_height() to FigureCanvasBase and return int instead + of float. - SC + +2005-06-18 + Applied Ted Drain's QtAgg patch: 1) Changed the toolbar to be a horizontal + bar of push buttons instead of a QToolbar and updated the layout algorithms + in the main window accordingly. This eliminates the ability to drag and + drop the toolbar and detach it from the window. 2) Updated the resize + algorithm in the main window to show the correct size for the plot widget + as requested. This works almost correctly right now. It looks to me like + the final size of the widget is off by the border of the main window but I + haven't figured out a way to get that information yet. We could just add a + small margin to the new size but that seems a little hacky. 3) Changed the + x/y location label to be in the toolbar like the Tk backend instead of as a + status line at the bottom of the widget. 4) Changed the toolbar pixmaps to + use the ppm files instead of the png files. I noticed that the Tk backend + buttons looked much nicer and it uses the ppm files so I switched them. + +2005-06-17 + Modified the gtk backend to not queue mouse motion events. This allows for + live updates when dragging a slider. - CM + +2005-06-17 + Added starter CocoaAgg backend. Only works on OS 10.3 for now and requires + PyObjC. (10.4 is high priority) - CM + +2005-06-17 + Upgraded pyparsing and applied Paul McGuire's suggestions for speeding + things up. This more than doubles the speed of mathtext in my simple + tests. JDH + +2005-06-16 + Applied David Cooke's subplot make_key patch ---------------------------------- -2005-06-15 0.82 released +0.82 (2005-06-15) +----------------- -2005-06-15 Added subplot config tool to GTK* backends -- note you must - now import the NavigationToolbar2 from your backend of - choice rather than from backend_gtk because it needs to - know about the backend specific canvas -- see - examples/embedding_in_gtk2.py. Ditto for wx backend -- see - examples/embedding_in_wxagg.py +2005-06-15 + Added subplot config tool to GTK* backends -- note you must now import the + NavigationToolbar2 from your backend of choice rather than from backend_gtk + because it needs to know about the backend specific canvas -- see + examples/embedding_in_gtk2.py. Ditto for wx backend -- see + examples/embedding_in_wxagg.py -2005-06-15 backend_cairo.py: updated to use pycairo 0.5.0 - SC +2005-06-15 + backend_cairo.py: updated to use pycairo 0.5.0 - SC -2005-06-14 Wrote some GUI neutral widgets (Button, Slider, - RadioButtons, CheckButtons) in matplotlib.widgets. See - examples/widgets/\*.py - JDH +2005-06-14 + Wrote some GUI neutral widgets (Button, Slider, RadioButtons, CheckButtons) + in matplotlib.widgets. See examples/widgets/\*.py - JDH -2005-06-14 Exposed subplot parameters as rc vars and as the fig - SubplotParams instance subplotpars. See - figure.SubplotParams, figure.Figure.subplots_adjust and the - pylab method subplots_adjust and - examples/subplots_adjust.py . Also added a GUI neutral - widget for adjusting subplots, see - examples/subplot_toolbar.py - JDH +2005-06-14 + Exposed subplot parameters as rc vars and as the fig SubplotParams instance + subplotpars. See figure.SubplotParams, figure.Figure.subplots_adjust and + the pylab method subplots_adjust and examples/subplots_adjust.py . Also + added a GUI neutral widget for adjusting subplots, see + examples/subplot_toolbar.py - JDH -2005-06-13 Exposed cap and join style for lines with new rc params and - line properties +2005-06-13 + Exposed cap and join style for lines with new rc params and line properties:: lines.dash_joinstyle : miter # miter|round|bevel lines.dash_capstyle : butt # butt|round|projecting @@ -4037,1405 +4750,1654 @@ the `API changes <../../api/api_changes.html>`_. lines.solid_capstyle : projecting # butt|round|projecting -2005-06-13 Added kwargs to Axes init +2005-06-13 + Added kwargs to Axes init -2005-06-13 Applied Baptiste's tick patch - JDH +2005-06-13 + Applied Baptiste's tick patch - JDH -2005-06-13 Fixed rc alias 'l' bug reported by Fernando by removing - aliases for mainlevel rc options. - JDH +2005-06-13 + Fixed rc alias 'l' bug reported by Fernando by removing aliases for + mainlevel rc options. - JDH -2005-06-10 Fixed bug #1217637 in ticker.py - DSD +2005-06-10 + Fixed bug #1217637 in ticker.py - DSD -2005-06-07 Fixed a bug in texmanager.py: .aux files not being removed - DSD +2005-06-07 + Fixed a bug in texmanager.py: .aux files not being removed - DSD -2005-06-08 Added Sean Richard's hist binning fix -- see API_CHANGES - JDH +2005-06-08 + Added Sean Richard's hist binning fix -- see API_CHANGES - JDH -2005-06-07 Fixed a bug in texmanager.py: .aux files not being removed - - DSD +2005-06-07 + Fixed a bug in texmanager.py: .aux files not being removed - DSD ---------------------- -2005-06-07 matplotlib-0.81 released +0.81 (2005-06-07) +----------------- -2005-06-06 Added autoscale_on prop to axes +2005-06-06 + Added autoscale_on prop to axes -2005-06-06 Added Nick's picker "among" patch - JDH +2005-06-06 + Added Nick's picker "among" patch - JDH -2005-06-05 Fixed a TeX/LaTeX font discrepency in backend_ps. - DSD +2005-06-05 + Fixed a TeX/LaTeX font discrepancy in backend_ps. - DSD -2005-06-05 Added a ps.distill option in rc settings. If True, postscript - output will be distilled using ghostscript, which should trim - the file size and allow it to load more quickly. Hopefully this - will address the issue of large ps files due to font - definitions. Tested with gnu-ghostscript-8.16. - DSD +2005-06-05 + Added a ps.distill option in rc settings. If True, postscript output will + be distilled using ghostscript, which should trim the file size and allow + it to load more quickly. Hopefully this will address the issue of large ps + files due to font definitions. Tested with gnu-ghostscript-8.16. - DSD -2005-06-03 Improved support for tex handling of text in backend_ps. - DSD +2005-06-03 + Improved support for tex handling of text in backend_ps. - DSD -2005-06-03 Added rc options to render text with tex or latex, and to select - the latex font package. - DSD +2005-06-03 + Added rc options to render text with tex or latex, and to select the latex + font package. - DSD -2005-06-03 Fixed a bug in ticker.py causing a ZeroDivisionError +2005-06-03 + Fixed a bug in ticker.py causing a ZeroDivisionError -2005-06-02 backend_gtk.py remove DBL_BUFFER, add line to expose_event to - try to fix pygtk 2.6 redraw problem - SC +2005-06-02 + backend_gtk.py remove DBL_BUFFER, add line to expose_event to try to fix + pygtk 2.6 redraw problem - SC -2005-06-01 The default behavior of ScalarFormatter now renders scientific - notation and large numerical offsets in a label at the end of - the axis. - DSD +2005-06-01 + The default behavior of ScalarFormatter now renders scientific notation and + large numerical offsets in a label at the end of the axis. - DSD -2005-06-01 Added Nicholas' frombyte image patch - JDH +2005-06-01 + Added Nicholas' frombyte image patch - JDH -2005-05-31 Added vertical TeX support for agg - JDH +2005-05-31 + Added vertical TeX support for agg - JDH -2005-05-31 Applied Eric's cntr patch - JDH +2005-05-31 + Applied Eric's cntr patch - JDH -2005-05-27 Finally found the pesky agg bug (which Maxim was kind - enough to fix within hours) that was causing a segfault in - the win32 cached marker drawing. Now windows users can get - the enormouse performance benefits of caced markers w/o - those occasional pesy screenshots. - JDH +2005-05-27 + Finally found the pesky agg bug (which Maxim was kind enough to fix within + hours) that was causing a segfault in the win32 cached marker drawing. Now + windows users can get the enormous performance benefits of cached markers + w/o those occasional pesy screenshots. - JDH -2005-05-27 Got win32 build system working again, using a more recent - version of gtk and pygtk in the win32 build, gtk 2.6 from - https://web.archive.org/web/20050527002647/https://www.gimp.org/~tml/gimp/win32/downloads.html (you - will also need libpng12.dll to use these). I haven't - tested whether this binary build of mpl for win32 will work - with older gtk runtimes, so you may need to upgrade. +2005-05-27 + Got win32 build system working again, using a more recent version of gtk + and pygtk in the win32 build, gtk 2.6 from + https://web.archive.org/web/20050527002647/https://www.gimp.org/~tml/gimp/win32/downloads.html + (you will also need libpng12.dll to use these). I haven't tested whether + this binary build of mpl for win32 will work with older gtk runtimes, so + you may need to upgrade. -2005-05-27 Fixed bug where 2nd wxapp could be started if using wxagg - backend. - ADS +2005-05-27 + Fixed bug where 2nd wxapp could be started if using wxagg backend. - ADS -2005-05-26 Added Daishi text with dash patch -- see examples/dashtick.py +2005-05-26 + Added Daishi text with dash patch -- see examples/dashtick.py -2005-05-26 Moved backend_latex functionality into backend_ps. If - text.usetex=True, the PostScript backend will use LaTeX to - generate the .ps or .eps file. Ghostscript is required for - eps output. - DSD +2005-05-26 + Moved backend_latex functionality into backend_ps. If text.usetex=True, the + PostScript backend will use LaTeX to generate the .ps or .eps file. + Ghostscript is required for eps output. - DSD -2005-05-24 Fixed alignment and color issues in latex backend. - DSD +2005-05-24 + Fixed alignment and color issues in latex backend. - DSD -2005-05-21 Fixed raster problem for small rasters with dvipng -- looks - like it was a premultipled alpha problem - JDH +2005-05-21 + Fixed raster problem for small rasters with dvipng -- looks like it was a + premultiplied alpha problem - JDH -2005-05-20 Added linewidth and faceted kwarg to scatter to control - edgewidth and color. Also added autolegend patch to - inspect line segments. +2005-05-20 + Added linewidth and faceted kwarg to scatter to control edgewidth and + color. Also added autolegend patch to inspect line segments. -2005-05-18 Added Orsay and JPL qt fixes - JDH +2005-05-18 + Added Orsay and JPL qt fixes - JDH -2005-05-17 Added a psfrag latex backend -- some alignment issues need - to be worked out. Run with -dLaTeX and a *.tex file and - *.eps file are generated. latex and dvips the generated - latex file to get ps output. Note xdvi *does* not work, - you must generate ps.- JDH +2005-05-17 + Added a psfrag latex backend -- some alignment issues need to be worked + out. Run with -dLaTeX and a *.tex file and *.eps file are generated. latex + and dvips the generated latex file to get ps output. Note xdvi *does* not + work, you must generate ps.- JDH -2005-05-13 Added Florent Rougon's Axis set_label1 - patch +2005-05-13 + Added Florent Rougon's Axis set_label1 patch -2005-05-17 pcolor optimization, fixed bug in previous pcolor patch - JSWHIT +2005-05-17 + pcolor optimization, fixed bug in previous pcolor patch - JSWHIT -2005-05-16 Added support for masked arrays in pcolor - JSWHIT +2005-05-16 + Added support for masked arrays in pcolor - JSWHIT -2005-05-12 Started work on TeX text for antigrain using pngdvi -- see - examples/tex_demo.py and the new module - matplotlib.texmanager. Rotated text not supported and - rendering small glyps is not working right yet. BUt large - fontsizes and/or high dpi saved figs work great. +2005-05-12 + Started work on TeX text for antigrain using pngdvi -- see + examples/tex_demo.py and the new module matplotlib.texmanager. Rotated + text not supported and rendering small glyphs is not working right yet. But + large fontsizes and/or high dpi saved figs work great. -2005-05-10 New image resize options interpolation options. New values - for the interp kwarg are +2005-05-10 + New image resize options interpolation options. New values for the interp + kwarg are 'nearest', 'bilinear', 'bicubic', 'spline16', 'spline36', 'hanning', 'hamming', 'hermite', 'kaiser', 'quadric', 'catrom', 'gaussian', 'bessel', 'mitchell', 'sinc', 'lanczos', 'blackman' - See help(imshow) for details, particularly the - interpolation, filternorm and filterrad kwargs + See help(imshow) for details, particularly the interpolation, filternorm + and filterrad kwargs -2005-05-10 Applied Eric's contour mem leak fixes - JDH +2005-05-10 + Applied Eric's contour mem leak fixes - JDH -2005-05-10 Extended python agg wrapper and started implementing - backend_agg2, an agg renderer based on the python wrapper. - This will be more flexible and easier to extend than the - current backend_agg. See also examples/agg_test.py - JDH +2005-05-10 + Extended python agg wrapper and started implementing backend_agg2, an agg + renderer based on the python wrapper. This will be more flexible and + easier to extend than the current backend_agg. See also + examples/agg_test.py - JDH -2005-05-09 Added Marcin's no legend patch to exclude lines from the - autolegend builder +2005-05-09 + Added Marcin's no legend patch to exclude lines from the autolegend builder:: plot(x, y, label='nolegend') -2005-05-05 Upgraded to agg23 +2005-05-05 + Upgraded to agg23 -2005-05-05 Added newscalarformatter_demo.py to examples. -DSD +2005-05-05 + Added newscalarformatter_demo.py to examples. -DSD -2005-05-04 Added NewScalarFormatter. Improved formatting of ticklabels, - scientific notation, and the ability to plot large large - numbers with small ranges, by determining a numerical offset. - See ticker.NewScalarFormatter for more details. -DSD +2005-05-04 + Added NewScalarFormatter. Improved formatting of ticklabels, scientific + notation, and the ability to plot large numbers with small ranges, by + determining a numerical offset. See ticker.NewScalarFormatter for more + details. -DSD -2005-05-03 Added the option to specify a delimiter in pylab.load -DSD +2005-05-03 + Added the option to specify a delimiter in pylab.load -DSD -2005-04-28 Added Darren's line collection example +2005-04-28 + Added Darren's line collection example -2005-04-28 Fixed aa property in agg - JDH +2005-04-28 + Fixed aa property in agg - JDH -2005-04-27 Set postscript page size in .matplotlibrc - DSD +2005-04-27 + Set postscript page size in .matplotlibrc - DSD -2005-04-26 Added embedding in qt example. - JDH +2005-04-26 + Added embedding in qt example. - JDH -2005-04-14 Applied Michael Brady's qt backend patch: 1) fix a bug - where keyboard input was grabbed by the figure and not - released 2) turn on cursor changes 3) clean up a typo - and commented-out print statement. - JDH +2005-04-14 + Applied Michael Brady's qt backend patch: 1) fix a bug where keyboard input + was grabbed by the figure and not released 2) turn on cursor changes 3) + clean up a typo and commented-out print statement. - JDH +2005-04-14 + Applied Eric Firing's masked data lines patch and contour patch. Support + for masked arrays has been added to the plot command and to the Line2D + object. Only the valid points are plotted. A "valid_only" kwarg was added + to the get_xdata() and get_ydata() methods of Line2D; by default it is + False, so that the original data arrays are returned. Setting it to True + returns the plottable points. - see examples/masked_demo.py - JDH -2005-04-14 Applied Eric Firing's masked data lines patch and contour - patch. Support for masked arrays has been added to the - plot command and to the Line2D object. Only the valid - points are plotted. A "valid_only" kwarg was added to the - get_xdata() and get_ydata() methods of Line2D; by default - it is False, so that the original data arrays are - returned. Setting it to True returns the plottable points. - - see examples/masked_demo.py - JDH - -2005-04-13 Applied Tim Leslie's arrow key event handling patch - JDH - +2005-04-13 + Applied Tim Leslie's arrow key event handling patch - JDH --------------------------- -0.80 released +0.80 +---- -2005-04-11 Applied a variant of rick's xlim/ylim/axis patch. These - functions now take kwargs to let you selectively alter only - the min or max if desired. e.g., xlim(xmin=2) or - axis(ymax=3). They always return the new lim. - JDH +2005-04-11 + Applied a variant of rick's xlim/ylim/axis patch. These functions now take + kwargs to let you selectively alter only the min or max if desired. e.g., + xlim(xmin=2) or axis(ymax=3). They always return the new lim. - JDH -2005-04-11 Incorporated Werner's wx patch -- wx backend should be - compatible with wxpython2.4 and recent versions of 2.5. - Some early versions of wxpython 2.5 will not work because - there was a temporary change in the dc API that was rolled - back to make it 2.4 compliant +2005-04-11 + Incorporated Werner's wx patch -- wx backend should be compatible with + wxpython2.4 and recent versions of 2.5. Some early versions of wxpython + 2.5 will not work because there was a temporary change in the dc API that + was rolled back to make it 2.4 compliant -2005-04-11 modified tkagg show so that new figure window pops up on - call to figure +2005-04-11 + modified tkagg show so that new figure window pops up on call to figure -2005-04-11 fixed wxapp init bug +2005-04-11 + fixed wxapp init bug -2005-04-02 updated backend_ps.draw_lines, draw_markers for use with the - new API - DSD +2005-04-02 + updated backend_ps.draw_lines, draw_markers for use with the new API - DSD -2005-04-01 Added editable polygon example +2005-04-01 + Added editable polygon example ------------------------------ -2005-03-31 0.74 released +0.74 (2005-03-31) +----------------- -2005-03-30 Fixed and added checks for floating point inaccuracy in - ticker.Base - DSD +2005-03-30 + Fixed and added checks for floating point inaccuracy in ticker.Base - DSD -2005-03-30 updated /ellipse definition in backend_ps.py to address bug - #1122041 - DSD +2005-03-30 + updated /ellipse definition in backend_ps.py to address bug #1122041 - DSD -2005-03-29 Added unicode support for Agg and PS - JDH +2005-03-29 + Added unicode support for Agg and PS - JDH -2005-03-28 Added Jarrod's svg patch for text - JDH +2005-03-28 + Added Jarrod's svg patch for text - JDH -2005-03-28 Added Ludal's arrow and quiver patch - JDH +2005-03-28 + Added Ludal's arrow and quiver patch - JDH -2005-03-28 Added label kwarg to Axes to facilitate forcing the - creation of new Axes with otherwise identical attributes +2005-03-28 + Added label kwarg to Axes to facilitate forcing the creation of new Axes + with otherwise identical attributes -2005-03-28 Applied boxplot and OSX font search patches +2005-03-28 + Applied boxplot and OSX font search patches -2005-03-27 Added ft2font NULL check to fix Japanase font bug - JDH +2005-03-27 + Added ft2font NULL check to fix Japanese font bug - JDH -2005-03-27 Added sprint legend patch plus John Gill's tests and fix -- - see examples/legend_auto.py - JDH +2005-03-27 + Added sprint legend patch plus John Gill's tests and fix -- see + examples/legend_auto.py - JDH --------------------------- -2005-03-19 0.73.1 released +0.73.1 (2005-03-19) +------------------- -2005-03-19 Reverted wxapp handling because it crashed win32 - JDH +2005-03-19 + Reverted wxapp handling because it crashed win32 - JDH -2005-03-18 Add .number attribute to figure objects returned by figure() - FP +2005-03-18 + Add .number attribute to figure objects returned by figure() - FP --------------------------- -2005-03-18 0.73 released - -2005-03-16 Fixed labelsep bug - -2005-03-16 Applied Darren's ticker fix for small ranges - JDH - -2005-03-16 Fixed tick on horiz colorbar - JDH - -2005-03-16 Added Japanese winreg patch - JDH - -2005-03-15 backend_gtkagg.py: changed to use double buffering, this fixes - the problem reported Joachim Berdal Haga - "Parts of plot lagging - from previous frame in animation". Tested with anim.py and it makes - no noticeable difference to performance (23.7 before, 23.6 after) - - SC - -2005-03-14 add src/_backend_gdk.c extension to provide a substitute function - for pixbuf.get_pixels_array(). Currently pixbuf.get_pixels_array() - only works with Numeric, and then only works if pygtk has been - compiled with Numeric support. The change provides a function - pixbuf_get_pixels_array() which works with Numeric and numarray and - is always available. It means that backend_gtk should be able to - display images and mathtext in all circumstances. - SC - -2005-03-11 Upgraded CXX to 5.3.1 - -2005-03-10 remove GraphicsContextPS.set_linestyle() - and GraphicsContextSVG.set_linestyle() since they do no more than - the base class GraphicsContext.set_linestyle() - SC - -2005-03-09 Refactored contour functionality into dedicated module - -2005-03-09 Added Eric's contourf updates and Nadia's clabel functionality - -2005-03-09 Moved colorbar to figure.Figure to expose it for API developers - - JDH - -2005-03-09 backend_cairo.py: implemented draw_markers() - SC - -2005-03-09 cbook.py: only use enumerate() (the python version) if the builtin - version is not available. - Add new function 'izip' which is set to itertools.izip if available - and the python equivalent if not available. - SC - -2005-03-07 backend_gdk.py: remove PIXELS_PER_INCH from points_to_pixels(), but - still use it to adjust font sizes. This allows the GTK version of - line_styles.py to more closely match GTKAgg, previously the markers - were being drawn too large. - SC - -2005-03-01 Added Eric's contourf routines - -2005-03-01 Added start of proper agg SWIG wrapper. I would like to - expose agg functionality directly a the user level and this - module will serve that purpose eventually, and will - hopefully take over most of the functionality of the - current _image and _backend_agg modules. - JDH - -2005-02-28 Fixed polyfit / polyval to convert input args to float - arrays - JDH - - -2005-02-25 Add experimental feature to backend_gtk.py to enable/disable - double buffering (DBL_BUFFER=True/False) - SC - -2005-02-24 colors.py change ColorConverter.to_rgb() so it always returns rgb - (and not rgba), allow cnames keys to be cached, change the exception - raised from RuntimeError to ValueError (like hex2color()) - hex2color() use a regular expression to check the color string is - valid - SC - - -2005-02-23 Added rc param ps.useafm so backend ps can use native afm - fonts or truetype. afme breaks mathtext but causes much - smaller font sizes and may result in images that display - better in some contexts (e.g., pdfs incorporated into latex - docs viewed in acrobat reader). I would like to extend - this approach to allow the user to use truetype only for - mathtext, which should be easy. - -2005-02-23 Used sequence protocol rather than tuple in agg collection - drawing routines for greater flexibility - JDH - +0.73 (2005-03-18) +----------------- + +2005-03-16 + Fixed labelsep bug + +2005-03-16 + Applied Darren's ticker fix for small ranges - JDH + +2005-03-16 + Fixed tick on horiz colorbar - JDH + +2005-03-16 + Added Japanese winreg patch - JDH + +2005-03-15 + backend_gtkagg.py: changed to use double buffering, this fixes the problem + reported Joachim Berdal Haga - "Parts of plot lagging from previous frame + in animation". Tested with anim.py and it makes no noticeable difference to + performance (23.7 before, 23.6 after) - SC + +2005-03-14 + add src/_backend_gdk.c extension to provide a substitute function for + pixbuf.get_pixels_array(). Currently pixbuf.get_pixels_array() only works + with Numeric, and then only works if pygtk has been compiled with Numeric + support. The change provides a function pixbuf_get_pixels_array() which + works with Numeric and numarray and is always available. It means that + backend_gtk should be able to display images and mathtext in all + circumstances. - SC + +2005-03-11 + Upgraded CXX to 5.3.1 + +2005-03-10 + remove GraphicsContextPS.set_linestyle() and + GraphicsContextSVG.set_linestyle() since they do no more than the base + class GraphicsContext.set_linestyle() - SC + +2005-03-09 + Refactored contour functionality into dedicated module + +2005-03-09 + Added Eric's contourf updates and Nadia's clabel functionality + +2005-03-09 + Moved colorbar to figure.Figure to expose it for API developers - JDH + +2005-03-09 + backend_cairo.py: implemented draw_markers() - SC + +2005-03-09 + cbook.py: only use enumerate() (the python version) if the builtin version + is not available. Add new function 'izip' which is set to itertools.izip + if available and the python equivalent if not available. - SC + +2005-03-07 + backend_gdk.py: remove PIXELS_PER_INCH from points_to_pixels(), but still + use it to adjust font sizes. This allows the GTK version of line_styles.py + to more closely match GTKAgg, previously the markers were being drawn too + large. - SC + +2005-03-01 + Added Eric's contourf routines + +2005-03-01 + Added start of proper agg SWIG wrapper. I would like to expose agg + functionality directly a the user level and this module will serve that + purpose eventually, and will hopefully take over most of the functionality + of the current _image and _backend_agg modules. - JDH + +2005-02-28 + Fixed polyfit / polyval to convert input args to float arrays - JDH + +2005-02-25 + Add experimental feature to backend_gtk.py to enable/disable double + buffering (DBL_BUFFER=True/False) - SC + +2005-02-24 + colors.py change ColorConverter.to_rgb() so it always returns rgb (and not + rgba), allow cnames keys to be cached, change the exception raised from + RuntimeError to ValueError (like hex2color()) hex2color() use a regular + expression to check the color string is valid - SC + +2005-02-23 + Added rc param ps.useafm so backend ps can use native afm fonts or + truetype. afme breaks mathtext but causes much smaller font sizes and may + result in images that display better in some contexts (e.g., pdfs + incorporated into latex docs viewed in acrobat reader). I would like to + extend this approach to allow the user to use truetype only for mathtext, + which should be easy. + +2005-02-23 + Used sequence protocol rather than tuple in agg collection drawing routines + for greater flexibility - JDH -------------------------------- -2005-02-22 0.72.1 released - -2005-02-21 fixed linestyles for collections -- contour now dashes for - levels <0 +0.72.1 (2005-02-22) +------------------- -2005-02-21 fixed ps color bug - JDH +2005-02-21 + fixed linestyles for collections -- contour now dashes for levels <0 -2005-02-15 fixed missing qt file +2005-02-21 + fixed ps color bug - JDH -2005-02-15 banished error_msg and report_error. Internal backend - methods like error_msg_gtk are preserved. backend writers, - check your backends, and diff against 0.72 to make sure I - did the right thing! - JDH +2005-02-15 + fixed missing qt file +2005-02-15 + banished error_msg and report_error. Internal backend methods like + error_msg_gtk are preserved. backend writers, check your backends, and + diff against 0.72 to make sure I did the right thing! - JDH -2005-02-14 Added enthought traits to matplotlib tree - JDH +2005-02-14 + Added enthought traits to matplotlib tree - JDH ------------------------ -2005-02-14 0.72 released +0.72 (2005-02-14) +----------------- -2005-02-14 fix bug in cbook alltrue() and onetrue() - SC +2005-02-14 + fix bug in cbook alltrue() and onetrue() - SC -2005-02-11 updated qtagg backend from Ted - JDH +2005-02-11 + updated qtagg backend from Ted - JDH -2005-02-11 matshow fixes for figure numbering, return value and docs - FP +2005-02-11 + matshow fixes for figure numbering, return value and docs - FP -2005-02-09 new zorder example for fine control in zorder_demo.py - FP +2005-02-09 + new zorder example for fine control in zorder_demo.py - FP -2005-02-09 backend renderer draw_lines now has transform in backend, - as in draw_markers; use numerix in _backend_agg, aded small - line optimization to agg +2005-02-09 + backend renderer draw_lines now has transform in backend, as in + draw_markers; use numerix in _backend_agg, added small line optimization to + agg -2005-02-09 subplot now deletes axes that it overlaps +2005-02-09 + subplot now deletes axes that it overlaps -2005-02-08 Added transparent support for gzipped files in load/save - Fernando - Perez (FP from now on). +2005-02-08 + Added transparent support for gzipped files in load/save - Fernando Perez + (FP from now on). -2005-02-08 Small optimizations in PS backend. They may have a big impact for - large plots, otherwise they don't hurt - FP +2005-02-08 + Small optimizations in PS backend. They may have a big impact for large + plots, otherwise they don't hurt - FP -2005-02-08 Added transparent support for gzipped files in load/save - Fernando - Perez (FP from now on). +2005-02-08 + Added transparent support for gzipped files in load/save - Fernando Perez + (FP from now on). -2005-02-07 Added newstyle path drawing for markers - only implemented - in agg currently - JDH +2005-02-07 + Added newstyle path drawing for markers - only implemented in agg currently + - JDH -2005-02-05 Some superscript text optimizations for ticking log plots +2005-02-05 + Some superscript text optimizations for ticking log plots -2005-02-05 Added some default key press events to pylab figures: 'g' - toggles grid - JDH +2005-02-05 + Added some default key press events to pylab figures: 'g' toggles grid - + JDH -2005-02-05 Added some support for handling log switching for lines - that have nonpos data - JDH +2005-02-05 + Added some support for handling log switching for lines that have nonpos + data - JDH -2005-02-04 Added Nadia's contour patch - contour now has matlab - compatible syntax; this also fixed an unequal sized contour - array bug- JDH +2005-02-04 + Added Nadia's contour patch - contour now has matlab compatible syntax; + this also fixed an unequal sized contour array bug- JDH -2005-02-04 Modified GTK backends to allow the FigureCanvas to be resized - smaller than its original size - SC +2005-02-04 + Modified GTK backends to allow the FigureCanvas to be resized smaller than + its original size - SC -2005-02-02 Fixed a bug in dates mx2num - JDH +2005-02-02 + Fixed a bug in dates mx2num - JDH -2005-02-02 Incorporated Fernando's matshow - JDH +2005-02-02 + Incorporated Fernando's matshow - JDH -2005-02-01 Added Fernando's figure num patch, including experimental - support for pylab backend switching, LineCOllection.color - warns, savefig now a figure method, fixed a close(fig) bug - - JDH +2005-02-01 + Added Fernando's figure num patch, including experimental support for pylab + backend switching, LineCOllection.color warns, savefig now a figure method, + fixed a close(fig) bug - JDH -2005-01-31 updated datalim in contour - JDH +2005-01-31 + updated datalim in contour - JDH -2005-01-30 Added backend_qtagg.py provided by Sigve Tjora - SC +2005-01-30 + Added backend_qtagg.py provided by Sigve Tjora - SC -2005-01-28 Added tk.inspect rc param to .matplotlibrc. IDLE users - should set tk.pythoninspect:True and interactive:True and - backend:TkAgg +2005-01-28 + Added tk.inspect rc param to .matplotlibrc. IDLE users should set + tk.pythoninspect:True and interactive:True and backend:TkAgg -2005-01-28 Replaced examples/interactive.py with an updated script from - Fernando Perez - SC +2005-01-28 + Replaced examples/interactive.py with an updated script from Fernando Perez + - SC -2005-01-27 Added support for shared x or y axes. See - examples/shared_axis_demo.py and examples/ganged_plots.py +2005-01-27 + Added support for shared x or y axes. See examples/shared_axis_demo.py and + examples/ganged_plots.py -2005-01-27 Added Lee's patch for missing symbols \leq and \LEFTbracket - to _mathtext_data - JDH +2005-01-27 + Added Lee's patch for missing symbols \leq and \LEFTbracket to + _mathtext_data - JDH -2005-01-26 Added Baptiste's two scales patch -- see help(twinx) in the - pylab interface for more info. See also - examples/two_scales.py +2005-01-26 + Added Baptiste's two scales patch -- see help(twinx) in the pylab interface + for more info. See also examples/two_scales.py -2005-01-24 Fixed a mathtext parser bug that prevented font changes in - sub/superscripts - JDH +2005-01-24 + Fixed a mathtext parser bug that prevented font changes in sub/superscripts + - JDH -2005-01-24 Fixed contour to work w/ interactive changes in colormaps, - clim, etc - JDH +2005-01-24 + Fixed contour to work w/ interactive changes in colormaps, clim, etc - JDH ----------------------------- -2005-01-21 matplotlib-0.71 released +0.71 (2005-01-21) +----------------- -2005-01-21 Refactored numerix to solve vexing namespace issues - JDH +2005-01-21 + Refactored numerix to solve vexing namespace issues - JDH -2005-01-21 Applied Nadia's contour bug fix - JDH +2005-01-21 + Applied Nadia's contour bug fix - JDH -2005-01-20 Made some changes to the contour routine - particularly - region=1 seems t fix a lot of the zigzag strangeness. - Added colormaps as default for contour - JDH +2005-01-20 + Made some changes to the contour routine - particularly region=1 seems t + fix a lot of the zigzag strangeness. Added colormaps as default for + contour - JDH -2005-01-19 Restored builtin names which were overridden (min, max, - abs, round, and sum) in pylab. This is a potentially - significant change for those who were relying on an array - version of those functions that previously overrode builtin - function names. - ADS +2005-01-19 + Restored builtin names which were overridden (min, max, abs, round, and + sum) in pylab. This is a potentially significant change for those who were + relying on an array version of those functions that previously overrode + builtin function names. - ADS -2005-01-18 Added accents to mathtext: \hat, \breve, \grave, \bar, - \acute, \tilde, \vec, \dot, \ddot. All of them have the - same syntax, e.g., to make an overbar you do \bar{o} or to - make an o umlaut you do \ddot{o}. The shortcuts are also - provided, e.g., \"o \'e \`e \~n \.x \^y - JDH +2005-01-18 + Added accents to mathtext: \hat, \breve, \grave, \bar, \acute, \tilde, + \vec, \dot, \ddot. All of them have the same syntax, e.g., to make an + overbar you do \bar{o} or to make an o umlaut you do \ddot{o}. The + shortcuts are also provided, e.g., \"o \'e \`e \~n \.x \^y - JDH -2005-01-18 Plugged image resize memory leaks - JDH +2005-01-18 + Plugged image resize memory leaks - JDH -2005-01-18 Fixed some mathtext parser problems relating to superscripts +2005-01-18 + Fixed some mathtext parser problems relating to superscripts -2005-01-17 Fixed a yticklabel problem for colorbars under change of - clim - JDH +2005-01-17 + Fixed a yticklabel problem for colorbars under change of clim - JDH -2005-01-17 Cleaned up Destroy handling in wx reducing memleak/fig from - approx 800k to approx 6k- JDH +2005-01-17 + Cleaned up Destroy handling in wx reducing memleak/fig from approx 800k to + approx 6k- JDH -2005-01-17 Added kappa to latex_to_bakoma - JDH +2005-01-17 + Added kappa to latex_to_bakoma - JDH -2005-01-15 Support arbitrary colorbar axes and horizontal colorbars - JDH +2005-01-15 + Support arbitrary colorbar axes and horizontal colorbars - JDH -2005-01-15 Fixed colormap number of colors bug so that the colorbar - has the same discretization as the image - JDH +2005-01-15 + Fixed colormap number of colors bug so that the colorbar has the same + discretization as the image - JDH -2005-01-15 Added Nadia's x,y contour fix - JDH +2005-01-15 + Added Nadia's x,y contour fix - JDH -2005-01-15 backend_cairo: added PDF support which requires pycairo 0.1.4. - Its not usable yet, but is ready for when the Cairo PDF backend - matures - SC +2005-01-15 + backend_cairo: added PDF support which requires pycairo 0.1.4. Its not + usable yet, but is ready for when the Cairo PDF backend matures - SC -2005-01-15 Added Nadia's x,y contour fix +2005-01-15 + Added Nadia's x,y contour fix -2005-01-12 Fixed set clip_on bug in artist - JDH +2005-01-12 + Fixed set clip_on bug in artist - JDH -2005-01-11 Reverted pythoninspect in tkagg - JDH +2005-01-11 + Reverted pythoninspect in tkagg - JDH -2005-01-09 Fixed a backend_bases event bug caused when an event is - triggered when location is None - JDH +2005-01-09 + Fixed a backend_bases event bug caused when an event is triggered when + location is None - JDH -2005-01-07 Add patch from Stephen Walton to fix bug in pylab.load() - when the % character is included in a comment. - ADS +2005-01-07 + Add patch from Stephen Walton to fix bug in pylab.load() when the % + character is included in a comment. - ADS -2005-01-07 Added markerscale attribute to Legend class. This allows - the marker size in the legend to be adjusted relative to - that in the plot. - ADS +2005-01-07 + Added markerscale attribute to Legend class. This allows the marker size + in the legend to be adjusted relative to that in the plot. - ADS -2005-01-06 Add patch from Ben Vanhaeren to make the FigureManagerGTK vbox a - public attribute - SC +2005-01-06 + Add patch from Ben Vanhaeren to make the FigureManagerGTK vbox a public + attribute - SC ---------------------------- -2004-12-30 Release 0.70 +2004-12-30 + Release 0.70 -2004-12-28 Added coord location to key press and added a - examples/picker_demo.py +2004-12-28 + Added coord location to key press and added a examples/picker_demo.py -2004-12-28 Fixed coords notification in wx toolbar - JDH +2004-12-28 + Fixed coords notification in wx toolbar - JDH -2004-12-28 Moved connection and disconnection event handling to the - FigureCanvasBase. Backends now only need to connect one - time for each of the button press, button release and key - press/release functions. The base class deals with - callbacks and multiple connections. This fixes flakiness - on some backends (tk, wx) in the presence of multiple - connections and/or disconnect - JDH +2004-12-28 + Moved connection and disconnection event handling to the FigureCanvasBase. + Backends now only need to connect one time for each of the button press, + button release and key press/release functions. The base class deals with + callbacks and multiple connections. This fixes flakiness on some backends + (tk, wx) in the presence of multiple connections and/or disconnect - JDH -2004-12-27 Fixed PS mathtext bug where color was not set - Jochen - please verify correct - JDH +2004-12-27 + Fixed PS mathtext bug where color was not set - Jochen please verify + correct - JDH -2004-12-27 Added Shadow class and added shadow kwarg to legend and pie - for shadow effect - JDH +2004-12-27 + Added Shadow class and added shadow kwarg to legend and pie for shadow + effect - JDH -2004-12-27 Added pie charts and new example/pie_demo.py +2004-12-27 + Added pie charts and new example/pie_demo.py -2004-12-23 Fixed an agg text rotation alignment bug, fixed some text - kwarg processing bugs, and added examples/text_rotation.py - to explain and demonstrate how text rotations and alignment - work in matplotlib. - JDH +2004-12-23 + Fixed an agg text rotation alignment bug, fixed some text kwarg processing + bugs, and added examples/text_rotation.py to explain and demonstrate how + text rotations and alignment work in matplotlib. - JDH ----------------------- -2004-12-22 0.65.1 released - JDH +0.65.1 (2004-12-22) +------------------- -2004-12-22 Fixed colorbar bug which caused colorbar not to respond to - changes in colormap in some instances - JDH +2004-12-22 + Fixed colorbar bug which caused colorbar not to respond to changes in + colormap in some instances - JDH -2004-12-22 Refactored NavigationToolbar in tkagg to support app - embedding , init now takes (canvas, window) rather than - (canvas, figman) - JDH +2004-12-22 + Refactored NavigationToolbar in tkagg to support app embedding , init now + takes (canvas, window) rather than (canvas, figman) - JDH -2004-12-21 Refactored axes and subplot management - removed - add_subplot and add_axes from the FigureManager. classic - toolbar updates are done via an observer pattern on the - figure using add_axobserver. Figure now maintains the axes - stack (for gca) and supports axes deletion. Ported changes - to GTK, Tk, Wx, and FLTK. Please test! Added delaxes - JDH +2004-12-21 + Refactored axes and subplot management - removed add_subplot and add_axes + from the FigureManager. classic toolbar updates are done via an observer + pattern on the figure using add_axobserver. Figure now maintains the axes + stack (for gca) and supports axes deletion. Ported changes to GTK, Tk, Wx, + and FLTK. Please test! Added delaxes - JDH -2004-12-21 Lots of image optimizations - 4x performance boost over - 0.65 JDH +2004-12-21 + Lots of image optimizations - 4x performance boost over 0.65 JDH -2004-12-20 Fixed a figimage bug where the axes is shown and modified - tkagg to move the destroy binding into the show method. +2004-12-20 + Fixed a figimage bug where the axes is shown and modified tkagg to move the + destroy binding into the show method. -2004-12-18 Minor refactoring of NavigationToolbar2 to support - embedding in an application - JDH +2004-12-18 + Minor refactoring of NavigationToolbar2 to support embedding in an + application - JDH -2004-12-14 Added linestyle to collections (currently broken) - JDH +2004-12-14 + Added linestyle to collections (currently broken) - JDH -2004-12-14 Applied Nadia's setupext patch to fix libstdc++ link - problem with contour and solaris -JDH +2004-12-14 + Applied Nadia's setupext patch to fix libstdc++ link problem with contour + and solaris -JDH -2004-12-14 A number of pychecker inspired fixes, including removal of - True and False from cbook which I erroneously thought was - needed for python2.2 - JDH +2004-12-14 + A number of pychecker inspired fixes, including removal of True and False + from cbook which I erroneously thought was needed for python2.2 - JDH -2004-12-14 Finished porting doc strings for set introspection. - Used silent_list for many get funcs that return - lists. JDH +2004-12-14 + Finished porting doc strings for set introspection. Used silent_list for + many get funcs that return lists. JDH -2004-12-13 dates.py: removed all timezone() calls, except for UTC - SC +2004-12-13 + dates.py: removed all timezone() calls, except for UTC - SC ---------------------------- -2004-12-13 0.65 released - JDH - -2004-12-13 colors.py: rgb2hex(), hex2color() made simpler (and faster), also - rgb2hex() - added round() instead of integer truncation - hex2color() - changed 256.0 divisor to 255.0, so now - '#ffffff' becomes (1.0,1.0,1.0) not (0.996,0.996,0.996) - SC +0.65 (2004-12-13) +----------------- -2004-12-11 Added ion and ioff to pylab interface - JDH +2004-12-13 + colors.py: rgb2hex(), hex2color() made simpler (and faster), also rgb2hex() + - added round() instead of integer truncation hex2color() - changed 256.0 + divisor to 255.0, so now '#ffffff' becomes (1.0,1.0,1.0) not + (0.996,0.996,0.996) - SC -2004-12-11 backend_template.py: delete FigureCanvasTemplate.realize() - most - backends don't use it and its no longer needed +2004-12-11 + Added ion and ioff to pylab interface - JDH - backend_ps.py, backend_svg.py: delete show() and - draw_if_interactive() - they are not needed for image backends +2004-12-11 + backend_template.py: delete FigureCanvasTemplate.realize() - most backends + don't use it and its no longer needed - backend_svg.py: write direct to file instead of StringIO - - SC + backend_ps.py, backend_svg.py: delete show() and draw_if_interactive() - + they are not needed for image backends -2004-12-10 Added zorder to artists to control drawing order of lines, - patches and text in axes. See examples/zoder_demo.py - JDH + backend_svg.py: write direct to file instead of StringIO -2004-12-10 Fixed colorbar bug with scatter - JDH + - SC -2004-12-10 Added Nadia Dencheva contour code - JDH +2004-12-10 + Added zorder to artists to control drawing order of lines, patches and text + in axes. See examples/zoder_demo.py - JDH -2004-12-10 backend_cairo.py: got mathtext working - SC +2004-12-10 + Fixed colorbar bug with scatter - JDH -2004-12-09 Added Norm Peterson's svg clipping patch +2004-12-10 + Added Nadia Dencheva contour code - JDH -2004-12-09 Added Matthew Newville's wx printing patch +2004-12-10 + backend_cairo.py: got mathtext working - SC -2004-12-09 Migrated matlab to pylab - JDH +2004-12-09 + Added Norm Peterson's svg clipping patch -2004-12-09 backend_gtk.py: split into two parts - - backend_gdk.py - an image backend - - backend_gtk.py - A GUI backend that uses GDK - SC +2004-12-09 + Added Matthew Newville's wx printing patch -2004-12-08 backend_gtk.py: remove quit_after_print_xvfb(\*args), show_xvfb(), - Dialog_MeasureTool(gtk.Dialog) one month after sending mail to - matplotlib-users asking if anyone still uses these functions - SC +2004-12-09 + Migrated matlab to pylab - JDH -2004-12-02 backend_bases.py, backend_template.py: updated some of the method - documentation to make them consistent with each other - SC +2004-12-09 + backend_gtk.py: split into two parts -2004-12-04 Fixed multiple bindings per event for TkAgg mpl_connect and - mpl_disconnect. Added a "test_disconnect" command line - parameter to coords_demo.py JTM + - backend_gdk.py - an image backend + - backend_gtk.py - A GUI backend that uses GDK - SC -2004-12-04 Fixed some legend bugs JDH +2004-12-08 + backend_gtk.py: remove quit_after_print_xvfb(\*args), show_xvfb(), + Dialog_MeasureTool(gtk.Dialog) one month after sending mail to + matplotlib-users asking if anyone still uses these functions - SC -2004-11-30 Added over command for oneoff over plots. e.g., over(plot, x, - y, lw=2). Works with any plot function. +2004-12-02 + backend_bases.py, backend_template.py: updated some of the method + documentation to make them consistent with each other - SC -2004-11-30 Added bbox property to text - JDH +2004-12-04 + Fixed multiple bindings per event for TkAgg mpl_connect and mpl_disconnect. + Added a "test_disconnect" command line parameter to coords_demo.py JTM -2004-11-29 Zoom to rect now respect reversed axes limits (for both - linear and log axes). - GL +2004-12-04 + Fixed some legend bugs JDH -2004-11-29 Added the over command to the matlab interface. over - allows you to add an overlay plot regardless of hold - state. - JDH +2004-11-30 + Added over command for oneoff over plots. e.g., over(plot, x, y, lw=2). + Works with any plot function. -2004-11-25 Added Printf to mplutils for printf style format string - formatting in C++ (should help write better exceptions) +2004-11-30 + Added bbox property to text - JDH -2004-11-24 IMAGE_FORMAT: remove from agg and gtkagg backends as its no longer - used - SC +2004-11-29 + Zoom to rect now respect reversed axes limits (for both linear and log + axes). - GL -2004-11-23 Added matplotlib compatible set and get introspection. See - set_and_get.py +2004-11-29 + Added the over command to the matlab interface. over allows you to add an + overlay plot regardless of hold state. - JDH -2004-11-23 applied Norbert's patched and exposed legend configuration - to kwargs - JDH +2004-11-25 + Added Printf to mplutils for printf style format string formatting in C++ + (should help write better exceptions) -2004-11-23 backend_gtk.py: added a default exception handler - SC +2004-11-24 + IMAGE_FORMAT: remove from agg and gtkagg backends as its no longer used - + SC -2004-11-18 backend_gtk.py: change so that the backend knows about all image - formats and does not need to use IMAGE_FORMAT in other backends - SC +2004-11-23 + Added matplotlib compatible set and get introspection. See set_and_get.py -2004-11-18 Fixed some report_error bugs in string interpolation as - reported on SF bug tracker- JDH +2004-11-23 + applied Norbert's patched and exposed legend configuration to kwargs - JDH -2004-11-17 backend_gtkcairo.py: change so all print_figure() calls render using - Cairo and get saved using backend_gtk.print_figure() - SC +2004-11-23 + backend_gtk.py: added a default exception handler - SC -2004-11-13 backend_cairo.py: Discovered the magic number (96) required for - Cairo PS plots to come out the right size. Restored Cairo PS output - and added support for landscape mode - SC +2004-11-18 + backend_gtk.py: change so that the backend knows about all image formats + and does not need to use IMAGE_FORMAT in other backends - SC -2004-11-13 Added ishold - JDH +2004-11-18 + Fixed some report_error bugs in string interpolation as reported on SF bug + tracker- JDH -2004-11-12 Added many new matlab colormaps - autumn bone cool copper - flag gray hot hsv jet pink prism spring summer winter - PG +2004-11-17 + backend_gtkcairo.py: change so all print_figure() calls render using Cairo + and get saved using backend_gtk.print_figure() - SC -2004-11-11 greatly simplify the emitted postscript code - JV +2004-11-13 + backend_cairo.py: Discovered the magic number (96) required for Cairo PS + plots to come out the right size. Restored Cairo PS output and added + support for landscape mode - SC -2004-11-12 Added new plotting functions spy, spy2 for sparse matrix - visualization - JDH +2004-11-13 + Added ishold - JDH -2004-11-11 Added rgrids, thetragrids for customizing the grid - locations and labels for polar plots - JDH +2004-11-12 + Added many new matlab colormaps - autumn bone cool copper flag gray hot hsv + jet pink prism spring summer winter - PG -2004-11-11 make the Gtk backends build without an X-server connection - JV +2004-11-11 + greatly simplify the emitted postscript code - JV -2004-11-10 matplotlib/__init__.py: Added FROZEN to signal we are running under - py2exe (or similar) - is used by backend_gtk.py - SC +2004-11-12 + Added new plotting functions spy, spy2 for sparse matrix visualization - + JDH -2004-11-09 backend_gtk.py: Made fix suggested by maffew@cat.org.au - to prevent problems when py2exe calls pygtk.require(). - SC +2004-11-11 + Added rgrids, thetragrids for customizing the grid locations and labels for + polar plots - JDH -2004-11-09 backend_cairo.py: Added support for printing to a fileobject. - Disabled cairo PS output which is not working correctly. - SC +2004-11-11 + make the Gtk backends build without an X-server connection - JV ----------------------------------- +2004-11-10 + matplotlib/__init__.py: Added FROZEN to signal we are running under py2exe + (or similar) - is used by backend_gtk.py - SC -2004-11-08 matplotlib-0.64 released +2004-11-09 + backend_gtk.py: Made fix suggested by maffew@cat.org.au to prevent problems + when py2exe calls pygtk.require(). - SC -2004-11-04 Changed -dbackend processing to only use known backends, so - we don't clobber other non-matplotlib uses of -d, like -debug. +2004-11-09 + backend_cairo.py: Added support for printing to a fileobject. Disabled + cairo PS output which is not working correctly. - SC -2004-11-04 backend_agg.py: added IMAGE_FORMAT to list the formats that the - backend can save to. - backend_gtkagg.py: added support for saving JPG files by using the - GTK backend - SC +---------------------------------- -2004-10-31 backend_cairo.py: now produces png and ps files (although the figure - sizing needs some work). pycairo did not wrap all the necessary - functions, so I wrapped them myself, they are included in the - backend_cairo.py doc string. - SC +0.64 (2004-11-08) +----------------- + +2004-11-04 + Changed -dbackend processing to only use known backends, so we don't + clobber other non-matplotlib uses of -d, like -debug. + +2004-11-04 + backend_agg.py: added IMAGE_FORMAT to list the formats that the backend can + save to. backend_gtkagg.py: added support for saving JPG files by using + the GTK backend - SC -2004-10-31 backend_ps.py: clean up the generated PostScript code, use - the PostScript stack to hold itermediate values instead of - storing them in the dictionary. - JV +2004-10-31 + backend_cairo.py: now produces png and ps files (although the figure sizing + needs some work). pycairo did not wrap all the necessary functions, so I + wrapped them myself, they are included in the backend_cairo.py doc string. + - SC -2004-10-30 backend_ps.py, ft2font.cpp, ft2font.h: fix the position of - text in the PostScript output. The new FT2Font method - get_descent gives the distance between the lower edge of - the bounding box and the baseline of a string. In - backend_ps the text is shifted upwards by this amount. - JV +2004-10-31 + backend_ps.py: clean up the generated PostScript code, use the PostScript + stack to hold intermediate values instead of storing them in the dictionary. + - JV -2004-10-30 backend_ps.py: clean up the code a lot. Change the - PostScript output to be more DSC compliant. All - definitions for the generated PostScript are now in a - PostScript dictionary 'mpldict'. Moved the long comment - about drawing ellipses from the PostScript output into a - Python comment. - JV +2004-10-30 + backend_ps.py, ft2font.cpp, ft2font.h: fix the position of text in the + PostScript output. The new FT2Font method get_descent gives the distance + between the lower edge of the bounding box and the baseline of a string. + In backend_ps the text is shifted upwards by this amount. - JV -2004-10-30 backend_gtk.py: removed FigureCanvasGTK.realize() as its no longer - needed. Merged ColorManager into GraphicsContext - backend_bases.py: For set_capstyle/joinstyle() only set cap or - joinstyle if there is no error. - SC +2004-10-30 + backend_ps.py: clean up the code a lot. Change the PostScript output to be + more DSC compliant. All definitions for the generated PostScript are now + in a PostScript dictionary 'mpldict'. Moved the long comment about drawing + ellipses from the PostScript output into a Python comment. - JV -2004-10-30 backend_gtk.py: tidied up print_figure() and removed some of the - dependency on widget events - SC +2004-10-30 + backend_gtk.py: removed FigureCanvasGTK.realize() as its no longer needed. + Merged ColorManager into GraphicsContext backend_bases.py: For + set_capstyle/joinstyle() only set cap or joinstyle if there is no error. - + SC -2004-10-28 backend_cairo.py: The renderer is complete except for mathtext, - draw_image() and clipping. gtkcairo works reasonably well. cairo - does not yet create any files since I can't figure how to set the - 'target surface', I don't think pycairo wraps the required functions - - SC +2004-10-30 + backend_gtk.py: tidied up print_figure() and removed some of the dependency + on widget events - SC -2004-10-28 backend_gtk.py: Improved the save dialog (GTK 2.4 only) so it - presents the user with a menu of supported image formats - SC +2004-10-28 + backend_cairo.py: The renderer is complete except for mathtext, + draw_image() and clipping. gtkcairo works reasonably well. cairo does not + yet create any files since I can't figure how to set the 'target surface', + I don't think pycairo wraps the required functions - SC -2004-10-28 backend_svg.py: change print_figure() to restore original face/edge - color - backend_ps.py : change print_figure() to ensure original face/edge - colors are restored even if there's an IOError - SC +2004-10-28 + backend_gtk.py: Improved the save dialog (GTK 2.4 only) so it presents the + user with a menu of supported image formats - SC -2004-10-27 Applied Norbert's errorbar patch to support barsabove kwarg +2004-10-28 + backend_svg.py: change print_figure() to restore original face/edge color + backend_ps.py : change print_figure() to ensure original face/edge colors + are restored even if there's an IOError - SC -2004-10-27 Applied Norbert's legend patch to support None handles +2004-10-27 + Applied Norbert's errorbar patch to support barsabove kwarg -2004-10-27 Added two more backends: backend_cairo.py, backend_gtkcairo.py - They are not complete yet, currently backend_gtkcairo just renders - polygons, rectangles and lines - SC +2004-10-27 + Applied Norbert's legend patch to support None handles -2004-10-21 Added polar axes and plots - JDH +2004-10-27 + Added two more backends: backend_cairo.py, backend_gtkcairo.py They are not + complete yet, currently backend_gtkcairo just renders polygons, rectangles + and lines - SC -2004-10-20 Fixed corrcoef bug exposed by corrcoef(X) where X is matrix - - JDH +2004-10-21 + Added polar axes and plots - JDH -2004-10-19 Added kwarg support to xticks and yticks to set ticklabel - text properties -- thanks to T. Edward Whalen for the suggestion +2004-10-20 + Fixed corrcoef bug exposed by corrcoef(X) where X is matrix - JDH -2004-10-19 Added support for PIL images in imshow(), image.py - ADS +2004-10-19 + Added kwarg support to xticks and yticks to set ticklabel text properties + -- thanks to T. Edward Whalen for the suggestion -2004-10-19 Re-worked exception handling in _image.py and _transforms.py - to avoid masking problems with shared libraries. - JTM +2004-10-19 + Added support for PIL images in imshow(), image.py - ADS -2004-10-16 Streamlined the matlab interface wrapper, removed the - noplot option to hist - just use mlab.hist instead. +2004-10-19 + Re-worked exception handling in _image.py and _transforms.py to avoid + masking problems with shared libraries. - JTM -2004-09-30 Added Andrew Dalke's strftime code to extend the range of - dates supported by the DateFormatter - JDH +2004-10-16 + Streamlined the matlab interface wrapper, removed the noplot option to hist + - just use mlab.hist instead. -2004-09-30 Added barh - JDH +2004-09-30 + Added Andrew Dalke's strftime code to extend the range of dates supported + by the DateFormatter - JDH -2004-09-30 Removed fallback to alternate array package from numerix - so that ImportErrors are easier to debug. JTM +2004-09-30 + Added barh - JDH -2004-09-30 Add GTK+ 2.4 support for the message in the toolbar. SC +2004-09-30 + Removed fallback to alternate array package from numerix so that + ImportErrors are easier to debug. - JTM -2004-09-30 Made some changes to support python22 - lots of doc - fixes. - JDH +2004-09-30 + Add GTK+ 2.4 support for the message in the toolbar. SC -2004-09-29 Added a Verbose class for reporting - JDH +2004-09-30 + Made some changes to support python22 - lots of doc fixes. - JDH + +2004-09-29 + Added a Verbose class for reporting - JDH ------------------------------------ -2004-09-28 Released 0.63.0 - -2004-09-28 Added save to file object for agg - see - examples/print_stdout.py - -2004-09-24 Reorganized all py code to lib subdir - -2004-09-24 Fixed axes resize image edge effects on interpolation - - required upgrade to agg22 which fixed an agg bug related to - this problem - -2004-09-20 Added toolbar2 message display for backend_tkagg. JTM - - -2004-09-17 Added coords formatter attributes. These must be callable, - and return a string for the x or y data. These will be used - to format the x and y data for the coords box. Default is - the axis major formatter. e.g.: - - # format the coords message box - def price(x): return '$%1.2f'%x - ax.format_xdata = DateFormatter('%Y-%m-%d') - ax.format_ydata = price - - -2004-09-17 Total rewrite of dates handling to use python datetime with - num2date, date2num and drange. pytz for timezone handling, - dateutils for spohisticated ticking. date ranges from - 0001-9999 are supported. rrules allow arbitrary date - ticking. examples/date_demo*.py converted to show new - usage. new example examples/date_demo_rrule.py shows how - to use rrules in date plots. The date locators are much - more general and almost all of them have different - constructors. See matplotlib.dates for more info. - -2004-09-15 Applied Fernando's backend __init__ patch to support easier - backend maintenance. Added his numutils to mlab. JDH - -2004-09-16 Re-designated all files in matplotlib/images as binary and - w/o keyword substitution using "cvs admin -kb \*.svg ...". - See binary files in "info cvs" under Linux. This was messing - up builds from CVS on windows since CVS was doing lf -> cr/lf - and keyword substitution on the bitmaps. - JTM - -2004-09-15 Modified setup to build array-package-specific extensions - for those extensions which are array-aware. Setup builds - extensions automatically for either Numeric, numarray, or - both, depending on what you have installed. Python proxy - modules for the array-aware extensions import the version - optimized for numarray or Numeric determined by numerix. - - JTM - -2004-09-15 Moved definitions of infinity from mlab to numerix to avoid - divide by zero warnings for numarray - JTM - -2004-09-09 Added axhline, axvline, axhspan and axvspan +2004-09-28 + Released 0.63.0 + +2004-09-28 + Added save to file object for agg - see examples/print_stdout.py + +2004-09-24 + Reorganized all py code to lib subdir + +2004-09-24 + Fixed axes resize image edge effects on interpolation - required upgrade to + agg22 which fixed an agg bug related to this problem + +2004-09-20 + Added toolbar2 message display for backend_tkagg. JTM + +2004-09-17 + Added coords formatter attributes. These must be callable, and return a + string for the x or y data. These will be used to format the x and y data + for the coords box. Default is the axis major formatter. e.g.:: + + # format the coords message box + def price(x): return '$%1.2f'%x + ax.format_xdata = DateFormatter('%Y-%m-%d') + ax.format_ydata = price + +2004-09-17 + Total rewrite of dates handling to use python datetime with num2date, + date2num and drange. pytz for timezone handling, dateutils for + spohisticated ticking. date ranges from 0001-9999 are supported. rrules + allow arbitrary date ticking. examples/date_demo*.py converted to show new + usage. new example examples/date_demo_rrule.py shows how to use rrules in + date plots. The date locators are much more general and almost all of them + have different constructors. See matplotlib.dates for more info. + +2004-09-15 + Applied Fernando's backend __init__ patch to support easier backend + maintenance. Added his numutils to mlab. JDH + +2004-09-16 + Re-designated all files in matplotlib/images as binary and w/o keyword + substitution using "cvs admin -kb \*.svg ...". See binary files in "info + cvs" under Linux. This was messing up builds from CVS on windows since CVS + was doing lf -> cr/lf and keyword substitution on the bitmaps. - JTM + +2004-09-15 + Modified setup to build array-package-specific extensions for those + extensions which are array-aware. Setup builds extensions automatically + for either Numeric, numarray, or both, depending on what you have + installed. Python proxy modules for the array-aware extensions import the + version optimized for numarray or Numeric determined by numerix. - JTM + +2004-09-15 + Moved definitions of infinity from mlab to numerix to avoid divide by zero + warnings for numarray - JTM + +2004-09-09 + Added axhline, axvline, axhspan and axvspan ------------------------------- -2004-08-30 matplotlib 0.62.4 released +0.62.4 (2004-08-30) +------------------- -2004-08-30 Fixed a multiple images with different extent bug, - Fixed markerfacecolor as RGB tuple +2004-08-30 + Fixed a multiple images with different extent bug, Fixed markerfacecolor as + RGB tuple -2004-08-27 Mathtext now more than 5x faster. Thanks to Paul Mcguire - for fixes both to pyparsing and to the matplotlib grammar! - mathtext broken on python2.2 +2004-08-27 + Mathtext now more than 5x faster. Thanks to Paul Mcguire for fixes both to + pyparsing and to the matplotlib grammar! mathtext broken on python2.2 -2004-08-25 Exposed Darren's and Greg's log ticking and formatting - options to semilogx and friends +2004-08-25 + Exposed Darren's and Greg's log ticking and formatting options to semilogx + and friends -2004-08-23 Fixed grid w/o args to toggle grid state - JDH +2004-08-23 + Fixed grid w/o args to toggle grid state - JDH -2004-08-11 Added Gregory's log patches for major and minor ticking +2004-08-11 + Added Gregory's log patches for major and minor ticking -2004-08-18 Some pixel edge effects fixes for images +2004-08-18 + Some pixel edge effects fixes for images -2004-08-18 Fixed TTF files reads in backend_ps on win32. +2004-08-18 + Fixed TTF files reads in backend_ps on win32. -2004-08-18 Added base and subs properties for logscale plots, user - modifiable using - set_[x,y]scale('log',base=b,subs=[mt1,mt2,...]) - GL +2004-08-18 + Added base and subs properties for logscale plots, user modifiable using + set_[x,y]scale('log',base=b,subs=[mt1,mt2,...]) - GL -2004-08-18 fixed a bug exposed by trying to find the HOME dir on win32 - thanks to Alan Issac for pointing to the light - JDH +2004-08-18 + fixed a bug exposed by trying to find the HOME dir on win32 thanks to Alan + Issac for pointing to the light - JDH -2004-08-18 fixed errorbar bug in setting ecolor - JDH +2004-08-18 + fixed errorbar bug in setting ecolor - JDH -2004-08-12 Added Darren Dale's exponential ticking patch +2004-08-12 + Added Darren Dale's exponential ticking patch -2004-08-11 Added Gregory's fltkagg backend +2004-08-11 + Added Gregory's fltkagg backend ------------------------------ -2004-08-09 matplotlib-0.61.0 released +0.61.0 (2004-08-09) +------------------- -2004-08-08 backend_gtk.py: get rid of the final PyGTK deprecation warning by - replacing gtkOptionMenu with gtkMenu in the 2.4 version of the - classic toolbar. +2004-08-08 + backend_gtk.py: get rid of the final PyGTK deprecation warning by replacing + gtkOptionMenu with gtkMenu in the 2.4 version of the classic toolbar. -2004-08-06 Added Tk zoom to rect rectangle, proper idle drawing, and - keybinding - JDH +2004-08-06 + Added Tk zoom to rect rectangle, proper idle drawing, and keybinding - JDH -2004-08-05 Updated installing.html and INSTALL - JDH +2004-08-05 + Updated installing.html and INSTALL - JDH -2004-08-01 backend_gtk.py: move all drawing code into the expose_event() +2004-08-01 + backend_gtk.py: move all drawing code into the expose_event() -2004-07-28 Added Greg's toolbar2 and backend_*agg patches - JDH +2004-07-28 + Added Greg's toolbar2 and backend_*agg patches - JDH -2004-07-28 Added image.imread with support for loading png into - numerix arrays +2004-07-28 + Added image.imread with support for loading png into numerix arrays -2004-07-28 Added key modifiers to events - implemented dynamic updates - and rubber banding for interactive pan/zoom - JDH +2004-07-28 + Added key modifiers to events - implemented dynamic updates and rubber + banding for interactive pan/zoom - JDH -2004-07-27 did a readthrough of SVG, replacing all the string - additions with string interps for efficiency, fixed some - layout problems, added font and image support (through - external pngs) - JDH +2004-07-27 + did a readthrough of SVG, replacing all the string additions with string + interps for efficiency, fixed some layout problems, added font and image + support (through external pngs) - JDH -2004-07-25 backend_gtk.py: modify toolbar2 to make it easier to support GTK+ - 2.4. Add GTK+ 2.4 toolbar support. - SC +2004-07-25 + backend_gtk.py: modify toolbar2 to make it easier to support GTK+ 2.4. Add + GTK+ 2.4 toolbar support. - SC -2004-07-24 backend_gtk.py: Simplified classic toolbar creation - SC +2004-07-24 + backend_gtk.py: Simplified classic toolbar creation - SC -2004-07-24 Added images/matplotlib.svg to be used when GTK+ windows are - minimised - SC +2004-07-24 + Added images/matplotlib.svg to be used when GTK+ windows are minimised - SC -2004-07-22 Added right mouse click zoom for NavigationToolbar2 panning - mode. - JTM +2004-07-22 + Added right mouse click zoom for NavigationToolbar2 panning mode. - JTM -2004-07-22 Added NavigationToolbar2 support to backend_tkagg. - Minor tweak to backend_bases. - JTM +2004-07-22 + Added NavigationToolbar2 support to backend_tkagg. Minor tweak to + backend_bases. - JTM -2004-07-22 Incorporated Gergory's renderer cache and buffer object - cache - JDH +2004-07-22 + Incorporated Gergory's renderer cache and buffer object cache - JDH -2004-07-22 Backend_gtk.py: Added support for GtkFileChooser, changed - FileSelection/FileChooser so that only one instance pops up, - and made them both modal. - SC +2004-07-22 + Backend_gtk.py: Added support for GtkFileChooser, changed + FileSelection/FileChooser so that only one instance pops up, and made them + both modal. - SC -2004-07-21 Applied backend_agg memory leak patch from hayden - - jocallo@online.no. Found and fixed a leak in binary - operations on transforms. Moral of the story: never incref - where you meant to decref! Fixed several leaks in ft2font: - moral of story: almost always return Py::asObject over - Py::Object - JDH +2004-07-21 + Applied backend_agg memory leak patch from hayden - jocallo@online.no. + Found and fixed a leak in binary operations on transforms. Moral of the + story: never incref where you meant to decref! Fixed several leaks in + ft2font: moral of story: almost always return Py::asObject over Py::Object + - JDH -2004-07-21 Fixed a to string memory allocation bug in agg and image - modules - JDH +2004-07-21 + Fixed a to string memory allocation bug in agg and image modules - JDH -2004-07-21 Added mpl_connect and mpl_disconnect to matlab interface - - JDH +2004-07-21 + Added mpl_connect and mpl_disconnect to matlab interface - JDH -2004-07-21 Added beginnings of users_guide to CVS - JDH +2004-07-21 + Added beginnings of users_guide to CVS - JDH -2004-07-20 ported toolbar2 to wx +2004-07-20 + ported toolbar2 to wx -2004-07-20 upgraded to agg21 - JDH +2004-07-20 + upgraded to agg21 - JDH -2004-07-20 Added new icons for toolbar2 - JDH +2004-07-20 + Added new icons for toolbar2 - JDH -2004-07-19 Added vertical mathtext for \*Agg and GTK - thanks Jim - Benson! - JDH +2004-07-19 + Added vertical mathtext for \*Agg and GTK - thanks Jim Benson! - JDH -2004-07-16 Added ps/eps/svg savefig options to wx and gtk JDH +2004-07-16 + Added ps/eps/svg savefig options to wx and gtk JDH -2004-07-15 Fixed python framework tk finder in setupext.py - JDH +2004-07-15 + Fixed python framework tk finder in setupext.py - JDH -2004-07-14 Fixed layer images demo which was broken by the 07/12 image - extent fixes - JDH +2004-07-14 + Fixed layer images demo which was broken by the 07/12 image extent fixes - + JDH -2004-07-13 Modified line collections to handle arbitrary length - segments for each line segment. - JDH +2004-07-13 + Modified line collections to handle arbitrary length segments for each line + segment. - JDH -2004-07-13 Fixed problems with image extent and origin - - set_image_extent deprecated. Use imshow(blah, blah, - extent=(xmin, xmax, ymin, ymax) instead - JDH +2004-07-13 + Fixed problems with image extent and origin - set_image_extent deprecated. + Use imshow(blah, blah, extent=(xmin, xmax, ymin, ymax) instead - JDH -2004-07-12 Added prototype for new nav bar with codifed event - handling. Use mpl_connect rather than connect for - matplotlib event handling. toolbar style determined by rc - toolbar param. backend status: gtk: prototype, wx: in - progress, tk: not started - JDH +2004-07-12 + Added prototype for new nav bar with codified event handling. Use + mpl_connect rather than connect for matplotlib event handling. toolbar + style determined by rc toolbar param. backend status: gtk: prototype, wx: + in progress, tk: not started - JDH -2004-07-11 backend_gtk.py: use builtin round() instead of redefining it. - - SC +2004-07-11 + backend_gtk.py: use builtin round() instead of redefining it. - SC -2004-07-10 Added embedding_in_wx3 example - ADS +2004-07-10 + Added embedding_in_wx3 example - ADS -2004-07-09 Added dynamic_image_wxagg to examples - ADS +2004-07-09 + Added dynamic_image_wxagg to examples - ADS -2004-07-09 added support for embedding TrueType fonts in PS files - PEB +2004-07-09 + added support for embedding TrueType fonts in PS files - PEB -2004-07-09 fixed a sfnt bug exposed if font cache is not built +2004-07-09 + fixed a sfnt bug exposed if font cache is not built -2004-07-09 added default arg None to matplotlib.matlab grid command to - toggle current grid state +2004-07-09 + added default arg None to matplotlib.matlab grid command to toggle current + grid state --------------------- -2004-07-08 0.60.2 released +0.60.2 (2004-07-08) +------------------- -2004-07-08 fixed a mathtext bug for '6' +2004-07-08 + fixed a mathtext bug for '6' -2004-07-08 added some numarray bug workarounds +2004-07-08 + added some numarray bug workarounds -------------------------- -2004-07-07 0.60 released - -2004-07-07 Fixed a bug in dynamic_demo_wx - +0.60 (2004-07-07) +----------------- -2004-07-07 backend_gtk.py: raise SystemExit immediately if - 'import pygtk' fails - SC +2004-07-07 + Fixed a bug in dynamic_demo_wx -2004-07-05 Added new mathtext commands \over{sym1}{sym2} and - \under{sym1}{sym2} +2004-07-07 + backend_gtk.py: raise SystemExit immediately if 'import pygtk' fails - SC -2004-07-05 Unified image and patch collections colormapping and - scaling args. Updated docstrings for all - JDH +2004-07-05 + Added new mathtext commands \over{sym1}{sym2} and \under{sym1}{sym2} -2004-07-05 Fixed a figure legend bug and added - examples/figlegend_demo.py - JDH +2004-07-05 + Unified image and patch collections colormapping and scaling args. Updated + docstrings for all - JDH -2004-07-01 Fixed a memory leak in image and agg to string methods +2004-07-05 + Fixed a figure legend bug and added examples/figlegend_demo.py - JDH -2004-06-25 Fixed fonts_demo spacing problems and added a kwargs - version of the fonts_demo fonts_demo_kw.py - JDH +2004-07-01 + Fixed a memory leak in image and agg to string methods -2004-06-25 finance.py: handle case when urlopen() fails - SC +2004-06-25 + Fixed fonts_demo spacing problems and added a kwargs version of the + fonts_demo fonts_demo_kw.py - JDH -2004-06-24 Support for multiple images on axes and figure, with - blending. Support for upper and lower image origins. - clim, jet and gray functions in matlab interface operate on - current image - JDH +2004-06-25 + finance.py: handle case when urlopen() fails - SC -2004-06-23 ported code to Perry's new colormap and norm scheme. Added - new rc attributes image.aspect, image.interpolation, - image.cmap, image.lut, image.origin +2004-06-24 + Support for multiple images on axes and figure, with blending. Support for + upper and lower image origins. clim, jet and gray functions in matlab + interface operate on current image - JDH -2004-06-20 backend_gtk.py: replace gtk.TRUE/FALSE with True/False. - simplified _make_axis_menu(). - SC +2004-06-23 + ported code to Perry's new colormap and norm scheme. Added new rc + attributes image.aspect, image.interpolation, image.cmap, image.lut, + image.origin -2004-06-19 anim_tk.py: Updated to use TkAgg by default (not GTK) - backend_gtk_py: Added '_' in front of private widget - creation functions - SC +2004-06-20 + backend_gtk.py: replace gtk.TRUE/FALSE with True/False. simplified + _make_axis_menu(). - SC -2004-06-17 backend_gtk.py: Create a GC once in realise(), not every - time draw() is called. - SC +2004-06-19 + anim_tk.py: Updated to use TkAgg by default (not GTK) backend_gtk_py: Added + '_' in front of private widget creation functions - SC -2004-06-16 Added new py2exe FAQ entry and added frozen support in - get_data_path for py2exe - JDH +2004-06-17 + backend_gtk.py: Create a GC once in realise(), not every time draw() is + called. - SC -2004-06-16 Removed GTKGD, which was always just a proof-of-concept - backend - JDH +2004-06-16 + Added new py2exe FAQ entry and added frozen support in get_data_path for + py2exe - JDH -2004-06-16 backend_gtk.py updates to replace deprecated functions - gtk.mainquit(), gtk.mainloop(). - Update NavigationToolbar to use the new GtkToolbar API - SC +2004-06-16 + Removed GTKGD, which was always just a proof-of-concept backend - JDH -2004-06-15 removed set_default_font from font_manager to unify font - customization using the new function rc. See API_CHANGES - for more info. The examples fonts_demo.py and - fonts_demo_kw.py are ported to the new API - JDH +2004-06-16 + backend_gtk.py updates to replace deprecated functions gtk.mainquit(), + gtk.mainloop(). Update NavigationToolbar to use the new GtkToolbar API - + SC -2004-06-15 Improved (yet again!) axis scaling to properly handle - singleton plots - JDH +2004-06-15 + removed set_default_font from font_manager to unify font customization + using the new function rc. See API_CHANGES for more info. The examples + fonts_demo.py and fonts_demo_kw.py are ported to the new API - JDH -2004-06-15 Restored the old FigureCanvasGTK.draw() - SC +2004-06-15 + Improved (yet again!) axis scaling to properly handle singleton plots - JDH -2004-06-11 More memory leak fixes in transforms and ft2font - JDH +2004-06-15 + Restored the old FigureCanvasGTK.draw() - SC -2004-06-11 Eliminated numerix .numerix file and environment variable - NUMERIX. Fixed bug which prevented command line overrides: - --numarray or --numeric. - JTM +2004-06-11 + More memory leak fixes in transforms and ft2font - JDH -2004-06-10 Added rc configuration function rc; deferred all rc param - setting until object creation time; added new rc attrs: - lines.markerfacecolor, lines.markeredgecolor, - lines.markeredgewidth, patch.linewidth, patch.facecolor, - patch.edgecolor, patch.antialiased; see - examples/customize_rc.py for usage - JDH +2004-06-11 + Eliminated numerix .numerix file and environment variable NUMERIX. Fixed + bug which prevented command line overrides: --numarray or --numeric. - JTM +2004-06-10 + Added rc configuration function rc; deferred all rc param setting until + object creation time; added new rc attrs: lines.markerfacecolor, + lines.markeredgecolor, lines.markeredgewidth, patch.linewidth, + patch.facecolor, patch.edgecolor, patch.antialiased; see + examples/customize_rc.py for usage - JDH --------------------------------------------------------------- -2004-06-09 0.54.2 released +0.54.2 (2004-06-09) +------------------- -2004-06-08 Rewrote ft2font using CXX as part of general memory leak - fixes; also fixed transform memory leaks - JDH +2004-06-08 + Rewrote ft2font using CXX as part of general memory leak fixes; also fixed + transform memory leaks - JDH -2004-06-07 Fixed several problems with log ticks and scaling - JDH +2004-06-07 + Fixed several problems with log ticks and scaling - JDH -2004-06-07 Fixed width/height issues for images - JDH +2004-06-07 + Fixed width/height issues for images - JDH -2004-06-03 Fixed draw_if_interactive bug for semilogx; +2004-06-03 + Fixed draw_if_interactive bug for semilogx; -2004-06-02 Fixed text clipping to clip to axes - JDH +2004-06-02 + Fixed text clipping to clip to axes - JDH -2004-06-02 Fixed leading newline text and multiple newline text - JDH +2004-06-02 + Fixed leading newline text and multiple newline text - JDH -2004-06-02 Fixed plot_date to return lines - JDH +2004-06-02 + Fixed plot_date to return lines - JDH -2004-06-01 Fixed plot to work with x or y having shape N,1 or 1,N - JDH +2004-06-01 + Fixed plot to work with x or y having shape N,1 or 1,N - JDH -2004-05-31 Added renderer markeredgewidth attribute of Line2D. - ADS +2004-05-31 + Added renderer markeredgewidth attribute of Line2D. - ADS -2004-05-29 Fixed tick label clipping to work with navigation. +2004-05-29 + Fixed tick label clipping to work with navigation. -2004-05-28 Added renderer grouping commands to support groups in +2004-05-28 + Added renderer grouping commands to support groups in SVG/PS. - JDH -2004-05-28 Fixed, this time I really mean it, the singleton plot - plot([0]) scaling bug; Fixed Flavio's shape = N,1 bug - JDH +2004-05-28 + Fixed, this time I really mean it, the singleton plot plot([0]) scaling + bug; Fixed Flavio's shape = N,1 bug - JDH -2004-05-28 added colorbar - JDH +2004-05-28 + added colorbar - JDH -2004-05-28 Made some changes to the matplotlib.colors.Colormap to - properly support clim - JDH +2004-05-28 + Made some changes to the matplotlib.colors.Colormap to properly support + clim - JDH ----------------------------------------------------------------- -2004-05-27 0.54.1 released +0.54.1 (2004-05-27) +------------------- -2004-05-27 Lots of small bug fixes: rotated text at negative angles, - errorbar capsize and autoscaling, right tick label - position, gtkagg on win98, alpha of figure background, - singleton plots - JDH +2004-05-27 + Lots of small bug fixes: rotated text at negative angles, errorbar capsize + and autoscaling, right tick label position, gtkagg on win98, alpha of + figure background, singleton plots - JDH -2004-05-26 Added Gary's errorbar stuff and made some fixes for length - one plots and constant data plots - JDH +2004-05-26 + Added Gary's errorbar stuff and made some fixes for length one plots and + constant data plots - JDH -2004-05-25 Tweaked TkAgg backend so that canvas.draw() works - more like the other backends. Fixed a bug resulting - in 2 draws per figure manager show(). - JTM +2004-05-25 + Tweaked TkAgg backend so that canvas.draw() works more like the other + backends. Fixed a bug resulting in 2 draws per figure manager show(). + - JTM ------------------------------------------------------------ -2004-05-19 0.54 released +0.54 (2004-05-19) +----------------- -2004-05-18 Added newline separated text with rotations to text.Text - layout - JDH +2004-05-18 + Added newline separated text with rotations to text.Text layout - JDH -2004-05-16 Added fast pcolor using PolyCollections. - JDH +2004-05-16 + Added fast pcolor using PolyCollections. - JDH -2004-05-14 Added fast polygon collections - changed scatter to use - them. Added multiple symbols to scatter. 10x speedup on - large scatters using \*Agg and 5X speedup for ps. - JDH +2004-05-14 + Added fast polygon collections - changed scatter to use them. Added + multiple symbols to scatter. 10x speedup on large scatters using \*Agg and + 5X speedup for ps. - JDH -2004-05-14 On second thought... created an "nx" namespace in - in numerix which maps type names onto typecodes - the same way for both numarray and Numeric. This - undoes my previous change immediately below. To get a - typename for Int16 usable in a Numeric extension: - say nx.Int16. - JTM +2004-05-14 + On second thought... created an "nx" namespace in in numerix which maps + type names onto typecodes the same way for both numarray and Numeric. This + undoes my previous change immediately below. To get a typename for Int16 + usable in a Numeric extension: say nx.Int16. - JTM -2004-05-15 Rewrote transformation class in extension code, simplified - all the artist constructors - JDH +2004-05-15 + Rewrote transformation class in extension code, simplified all the artist + constructors - JDH -2004-05-14 Modified the type definitions in the numarray side of - numerix so that they are Numeric typecodes and can be - used with Numeric compilex extensions. The original - numarray types were renamed to type. - JTM +2004-05-14 + Modified the type definitions in the numarray side of numerix so that they + are Numeric typecodes and can be used with Numeric compilex extensions. + The original numarray types were renamed to type. - JTM -2004-05-06 Gary Ruben sent me a bevy of new plot symbols and markers. - See matplotlib.matlab.plot - JDH +2004-05-06 + Gary Ruben sent me a bevy of new plot symbols and markers. See + matplotlib.matlab.plot - JDH -2004-05-06 Total rewrite of mathtext - factored ft2font stuff out of - layout engine and defined abstract class for font handling - to lay groundwork for ps mathtext. Rewrote parser and made - layout engine much more precise. Fixed all the layout - hacks. Added spacing commands \/ and \hspace. Added - composite chars and defined angstrom. - JDH +2004-05-06 + Total rewrite of mathtext - factored ft2font stuff out of layout engine and + defined abstract class for font handling to lay groundwork for ps mathtext. + Rewrote parser and made layout engine much more precise. Fixed all the + layout hacks. Added spacing commands \/ and \hspace. Added composite + chars and defined angstrom. - JDH -2004-05-05 Refactored text instances out of backend; aligned - text with arbitrary rotations is now supported - JDH +2004-05-05 + Refactored text instances out of backend; aligned text with arbitrary + rotations is now supported - JDH -2004-05-05 Added a Matrix capability for numarray to numerix. JTM +2004-05-05 + Added a Matrix capability for numarray to numerix. JTM -2004-05-04 Updated whats_new.html.template to use dictionary and - template loop, added anchors for all versions and items; - updated goals.txt to use those for links. PG +2004-05-04 + Updated whats_new.html.template to use dictionary and template loop, added + anchors for all versions and items; updated goals.txt to use those for + links. PG -2004-05-04 Added fonts_demo.py to backend_driver, and AFM and TTF font - caches to font_manager.py - PEB +2004-05-04 + Added fonts_demo.py to backend_driver, and AFM and TTF font caches to + font_manager.py - PEB -2004-05-03 Redid goals.html.template to use a goals.txt file that - has a pseudo restructured text organization. PG +2004-05-03 + Redid goals.html.template to use a goals.txt file that has a pseudo + restructured text organization. PG -2004-05-03 Removed the close buttons on all GUIs and added the python - #! bang line to the examples following Steve Chaplin's - advice on matplotlib dev +2004-05-03 + Removed the close buttons on all GUIs and added the python #! bang line to + the examples following Steve Chaplin's advice on matplotlib dev -2004-04-29 Added CXX and rewrote backend_agg using it; tracked down - and fixed agg memory leak - JDH +2004-04-29 + Added CXX and rewrote backend_agg using it; tracked down and fixed agg + memory leak - JDH -2004-04-29 Added stem plot command - JDH +2004-04-29 + Added stem plot command - JDH -2004-04-28 Fixed PS scaling and centering bug - JDH +2004-04-28 + Fixed PS scaling and centering bug - JDH -2004-04-26 Fixed errorbar autoscale problem - JDH +2004-04-26 + Fixed errorbar autoscale problem - JDH -2004-04-22 Fixed copy tick attribute bug, fixed singular datalim - ticker bug; fixed mathtext fontsize interactive bug. - JDH +2004-04-22 + Fixed copy tick attribute bug, fixed singular datalim ticker bug; fixed + mathtext fontsize interactive bug. - JDH -2004-04-21 Added calls to draw_if_interactive to axes(), legend(), - and pcolor(). Deleted duplicate pcolor(). - JTM +2004-04-21 + Added calls to draw_if_interactive to axes(), legend(), and pcolor(). + Deleted duplicate pcolor(). - JTM ------------------------------------------------------------ -2004-04-21 matplotlib 0.53 release +2004-04-21 + matplotlib 0.53 release -2004-04-19 Fixed vertical alignment bug in PS backend - JDH +2004-04-19 + Fixed vertical alignment bug in PS backend - JDH -2004-04-17 Added support for two scales on the "same axes" with tick - different ticking and labeling left right or top bottom. - See examples/two_scales.py - JDH +2004-04-17 + Added support for two scales on the "same axes" with tick different ticking + and labeling left right or top bottom. See examples/two_scales.py - JDH -2004-04-17 Added default dirs as list rather than single dir in - setupext.py - JDH +2004-04-17 + Added default dirs as list rather than single dir in setupext.py - JDH -2004-04-16 Fixed wx exception swallowing bug (and there was much - rejoicing!) - JDH +2004-04-16 + Fixed wx exception swallowing bug (and there was much rejoicing!) - JDH -2004-04-16 Added new ticker locator a formatter, fixed default font - return - JDH +2004-04-16 + Added new ticker locator a formatter, fixed default font return - JDH -2004-04-16 Added get_name method to FontProperties class. Fixed font lookup - in GTK and WX backends. - PEB +2004-04-16 + Added get_name method to FontProperties class. Fixed font lookup in GTK and + WX backends. - PEB -2004-04-16 Added get- and set_fontstyle msethods. - PEB +2004-04-16 + Added get- and set_fontstyle methods. - PEB -2004-04-10 Mathtext fixes: scaling with dpi, - JDH +2004-04-10 + Mathtext fixes: scaling with dpi, - JDH -2004-04-09 Improved font detection algorithm. - PEB +2004-04-09 + Improved font detection algorithm. - PEB -2004-04-09 Move deprecation warnings from text.py to __init__.py - PEB +2004-04-09 + Move deprecation warnings from text.py to __init__.py - PEB -2004-04-09 Added default font customization - JDH +2004-04-09 + Added default font customization - JDH -2004-04-08 Fixed viewlim set problem on axes and axis. - JDH +2004-04-08 + Fixed viewlim set problem on axes and axis. - JDH -2004-04-07 Added validate_comma_sep_str and font properties parameters to - __init__. Removed font families and added rcParams to - FontProperties __init__ arguments in font_manager. Added - default font property parameters to .matplotlibrc file with - descriptions. Added deprecation warnings to the get\_ - and - set_fontXXX methods of the Text object. - PEB +2004-04-07 + Added validate_comma_sep_str and font properties parameters to __init__. + Removed font families and added rcParams to FontProperties __init__ + arguments in font_manager. Added default font property parameters to + .matplotlibrc file with descriptions. Added deprecation warnings to the + get\_ - and set_fontXXX methods of the Text object. - PEB -2004-04-06 Added load and save commands for ASCII data - JDH +2004-04-06 + Added load and save commands for ASCII data - JDH -2004-04-05 Improved font caching by not reading AFM fonts until needed. - Added better documentation. Changed the behaviour of the - get_family, set_family, and set_name methods of FontProperties. - - PEB +2004-04-05 + Improved font caching by not reading AFM fonts until needed. Added better + documentation. Changed the behaviour of the get_family, set_family, and + set_name methods of FontProperties. - PEB -2004-04-05 Added WXAgg backend - JDH +2004-04-05 + Added WXAgg backend - JDH -2004-04-04 Improved font caching in backend_agg with changes to - font_manager - JDH +2004-04-04 + Improved font caching in backend_agg with changes to font_manager - JDH -2004-03-29 Fixed fontdicts and kwargs to work with new font manager - - JDH +2004-03-29 + Fixed fontdicts and kwargs to work with new font manager - JDH -------------------------------------------- This is the Old, stale, never used changelog -2002-12-10 - Added a TODO file and CHANGELOG. Lots to do -- get - crackin'! +2002-12-10 + - Added a TODO file and CHANGELOG. Lots to do -- get crackin'! - - Fixed y zoom tool bug + - Fixed y zoom tool bug - - Adopted a compromise fix for the y data clipping problem. - The problem was that for solid lines, the y data clipping - (as opposed to the gc clipping) caused artifactual - horizontal solid lines near the ylim boundaries. I did a - 5% offset hack in Axes set_ylim functions which helped, - but didn't cure the problem for very high gain y zooms. - So I disabled y data clipping for connected lines . If - you need extensive y clipping, either plot(y,x) because x - data clipping is always enabled, or change the _set_clip - code to 'if 1' as indicated in the lines.py src. See - _set_clip in lines.py and set_ylim in figure.py for more - information. + - Adopted a compromise fix for the y data clipping problem. The problem + was that for solid lines, the y data clipping (as opposed to the gc + clipping) caused artifactual horizontal solid lines near the ylim + boundaries. I did a 5% offset hack in Axes set_ylim functions which + helped, but didn't cure the problem for very high gain y zooms. So I + disabled y data clipping for connected lines . If you need extensive y + clipping, either plot(y,x) because x data clipping is always enabled, or + change the _set_clip code to 'if 1' as indicated in the lines.py src. + See _set_clip in lines.py and set_ylim in figure.py for more information. +2002-12-11 + - Added a measurement dialog to the figure window to measure axes position + and the delta x delta y with a left mouse drag. These defaults can be + overridden by deriving from Figure and overriding button_press_event, + button_release_event, and motion_notify_event, and _dialog_measure_tool. -2002-12-11 - Added a measurement dialog to the figure window to - measure axes position and the delta x delta y with a left - mouse drag. These defaults can be overridden by deriving - from Figure and overriding button_press_event, - button_release_event, and motion_notify_event, - and _dialog_measure_tool. + - fixed the navigation dialog so you can check the axes the navigation + buttons apply to. - - fixed the navigation dialog so you can check the axes the - navigation buttons apply to. +2003-04-23 + Released matplotlib v0.1 +2003-04-24 + Added a new line style PixelLine2D which is the plots the markers as pixels + (as small as possible) with format symbol ',' + Added a new class Patch with derived classes Rectangle, RegularPolygon and + Circle -2003-04-23 Released matplotlib v0.1 - -2003-04-24 Added a new line style PixelLine2D which is the plots the - markers as pixels (as small as possible) with format - symbol ',' - - Added a new class Patch with derived classes Rectangle, - RegularPolygon and Circle - -2003-04-25 Implemented new functions errorbar, scatter and hist - - Added a new line type '|' which is a vline. syntax is - plot(x, Y, '|') where y.shape = len(x),2 and each row gives - the ymin,ymax for the respective values of x. Previously I - had implemented vlines as a list of lines, but I needed the - efficientcy of the numeric clipping for large numbers of - vlines outside the viewport, so I wrote a dedicated class - Vline2D which derives from Line2D +2003-04-25 + Implemented new functions errorbar, scatter and hist + Added a new line type '|' which is a vline. syntax is plot(x, Y, '|') + where y.shape = len(x),2 and each row gives the ymin,ymax for the + respective values of x. Previously I had implemented vlines as a list of + lines, but I needed the efficiency of the numeric clipping for large + numbers of vlines outside the viewport, so I wrote a dedicated class + Vline2D which derives from Line2D 2003-05-01 - - Fixed ytick bug where grid and tick show outside axis viewport with gc clip + Fixed ytick bug where grid and tick show outside axis viewport with gc clip 2003-05-14 - - Added new ways to specify colors 1) matlab format string 2) - html-style hex string, 3) rgb tuple. See examples/color_demo.py + Added new ways to specify colors 1) matlab format string 2) html-style hex + string, 3) rgb tuple. See examples/color_demo.py 2003-05-28 - - Changed figure rendering to draw form a pixmap to reduce flicker. - See examples/system_monitor.py for an example where the plot is - continusouly updated w/o flicker. This example is meant to - simulate a system monitor that shows free CPU, RAM, etc... + Changed figure rendering to draw form a pixmap to reduce flicker. See + examples/system_monitor.py for an example where the plot is continuously + updated w/o flicker. This example is meant to simulate a system monitor + that shows free CPU, RAM, etc... 2003-08-04 - - Added Jon Anderson's GTK shell, which doesn't require pygtk to - have threading built-in and looks nice! + Added Jon Anderson's GTK shell, which doesn't require pygtk to have + threading built-in and looks nice! 2003-08-25 - - Fixed deprecation warnings for python2.3 and pygtk-1.99.18 + Fixed deprecation warnings for python2.3 and pygtk-1.99.18 2003-08-26 - - Added figure text with new example examples/figtext.py - + Added figure text with new example examples/figtext.py 2003-08-27 + Fixed bugs in figure text with font override dictionaries and fig text that + was placed outside the window bounding box - Fixed bugs i figure text with font override dictionairies and fig - text that was placed outside the window bounding box - -2003-09-1 through 2003-09-15 - - Added a postscript and a GD module backend +2003-09-01 through 2003-09-15 + Added a postscript and a GD module backend 2003-09-16 - - Fixed font scaling and point scaling so circles, squares, etc on - lines will scale with DPI as will fonts. Font scaling is not fully - implemented on the gtk backend because I have not figured out how - to scale fonts to arbitrary sizes with GTK + Fixed font scaling and point scaling so circles, squares, etc on lines will + scale with DPI as will fonts. Font scaling is not fully implemented on the + gtk backend because I have not figured out how to scale fonts to arbitrary + sizes with GTK 2003-09-17 + Fixed figure text bug which crashed X windows on long figure text extending + beyond display area. This was, I believe, due to the vestigial erase + functionality that was no longer needed since I began rendering to a pixmap - Fixed figure text bug which crashed X windows on long figure text - extending beyond display area. This was, I believe, due to the - vestigial erase functionality that was no longer needed since I - began rendering to a pixmap - -2003-09-30 Added legend +2003-09-30 + Added legend -2003-10-01 Fixed bug when colors are specified with rgb tuple or hex - string. +2003-10-01 + Fixed bug when colors are specified with rgb tuple or hex string. +2003-10-21 + Andrew Straw provided some legend code which I modified and incorporated. + Thanks Andrew! -2003-10-21 Andrew Straw provided some legend code which I modified - and incorporated. Thanks Andrew! +2003-10-27 + Fixed a bug in axis.get_view_distance that affected zoom in versus out with + interactive scrolling, and a bug in the axis text reset system that + prevented the text from being redrawn on a interactive gtk view lim set + with the widget -2003-10-27 Fixed a bug in axis.get_view_distance that affected zoom in - versus out with interactive scrolling, and a bug in the axis text - reset system that prevented the text from being redrawn on a - interactive gtk view lim set with the widget + Fixed a bug in that prevented the manual setting of ticklabel strings from + working properly - Fixed a bug in that prevented the manual setting of ticklabel - strings from working properly - -2003-11-02 - Do a nearest neighbor color pick on GD when - allocate fails +2003-11-02 + - Do a nearest neighbor color pick on GD when allocate fails 2003-11-02 - - Added pcolor plot - - Added MRI example - - Fixed bug that screwed up label position if xticks or yticks were - empty - - added nearest neighbor color picker when GD max colors exceeded - - fixed figure background color bug in GD backend + - Added pcolor plot + - Added MRI example + - Fixed bug that screwed up label position if xticks or yticks were empty + - added nearest neighbor color picker when GD max colors exceeded + - fixed figure background color bug in GD backend 2003-11-10 - 2003-11-11 - - major refactoring. - - * Ticks (with labels, lines and grid) handled by dedicated class - * Artist now know bounding box and dpi - * Bounding boxes and transforms handled by dedicated classes - * legend in dedicated class. Does a better job of alignment and - bordering. Can be initialized with specific line instances. - See examples/legend_demo2.py - - -2003-11-14 Fixed legend positioning bug and added new position args + major refactoring. -2003-11-16 Finished porting GD to new axes API + * Ticks (with labels, lines and grid) handled by dedicated class + * Artist now know bounding box and dpi + * Bounding boxes and transforms handled by dedicated classes + * legend in dedicated class. Does a better job of alignment and bordering. + Can be initialized with specific line instances. See + examples/legend_demo2.py +2003-11-14 + Fixed legend positioning bug and added new position args -2003-11-20 - add TM for matlab on website and in docs +2003-11-16 + Finished porting GD to new axes API +2003-11-20 + - add TM for matlab on website and in docs -2003-11-20 - make a nice errorbar and scatter screenshot +2003-11-20 + - make a nice errorbar and scatter screenshot -2003-11-20 - auto line style cycling for multiple line types - broken +2003-11-20 + - auto line style cycling for multiple line types broken -2003-11-18 (using inkrect) :logical rect too big on gtk backend +2003-11-18 + (using inkrect) :logical rect too big on gtk backend -2003-11-18 ticks don't reach edge of axes in gtk mode -- - rounding error? +2003-11-18 + ticks don't reach edge of axes in gtk mode -- rounding error? -2003-11-20 - port Gary's errorbar code to new API before 0.40 +2003-11-20 + - port Gary's errorbar code to new API before 0.40 -2003-11-20 - problem with stale _set_font. legend axes box - doesn't resize on save in GTK backend -- see htdocs legend_demo.py +2003-11-20 + - problem with stale _set_font. legend axes box doesn't resize on save in + GTK backend -- see htdocs legend_demo.py -2003-11-21 - make a dash-dot dict for the GC +2003-11-21 + - make a dash-dot dict for the GC -2003-12-15 - fix install path bug +2003-12-15 + - fix install path bug diff --git a/doc/users/dflt_style_changes.rst b/doc/users/prev_whats_new/dflt_style_changes.rst similarity index 99% rename from doc/users/dflt_style_changes.rst rename to doc/users/prev_whats_new/dflt_style_changes.rst index 5de399adb053..b36a1a343116 100644 --- a/doc/users/dflt_style_changes.rst +++ b/doc/users/prev_whats_new/dflt_style_changes.rst @@ -1,3 +1,5 @@ +.. redirect-from:: /users/dflt_style_changes + ============================== Changes to the default style ============================== diff --git a/doc/users/prev_whats_new/github_stats_3.0.0.rst b/doc/users/prev_whats_new/github_stats_3.0.0.rst new file mode 100644 index 000000000000..ab90e5e79e4e --- /dev/null +++ b/doc/users/prev_whats_new/github_stats_3.0.0.rst @@ -0,0 +1,1221 @@ +.. _github-stats-3-0-0: + +GitHub statistics for 3.0.0 (Sep 18, 2018) +========================================== + +GitHub statistics for 2017/01/17 (tag: v2.0.0) - 2018/09/18 + +These lists are automatically generated, and may be incomplete or contain duplicates. + +We closed 123 issues and merged 598 pull requests. +The full list can be seen `on GitHub `__ + +The following 478 authors contributed 9809 commits. + +* 816-8055 +* Aashil Patel +* AbdealiJK +* Adam +* Adam Williamson +* Adrian Price-Whelan +* Adrien Chardon +* Adrien F. Vincent +* ahed87 +* akrherz +* Akshay Nair +* Alan Bernstein +* Alberto +* alcinos +* Aleksey Bilogur +* Alex Rothberg +* Alexander Buchkovsky +* Alexander Harnisch +* AlexCav +* Alexis Bienvenüe +* Ali Uneri +* Allan Haldane +* Allen Downey +* Alvaro Sanchez +* alvarosg +* AndersonDaniel +* Andras Deak +* Andreas Gustafsson +* Andreas Hilboll +* Andreas Mayer +* Andreas Mueller +* Andrew Nelson +* Andy Mastbaum +* aneda +* Anthony Scopatz +* Anton Akhmerov +* Antony Lee +* aparamon +* apodemus +* Arthur Paulino +* Arvind +* as691454 +* ash13 +* Atharva Khare +* Avinash Sharma +* Bastian Bechtold +* bduick +* Ben +* Ben Root +* Benedikt Daurer +* Benjamin Berg +* Benjamin Congdon +* Bernhard M. Wiedemann +* BHT +* Bianca Gibson +* Björn Dahlgren +* Blaise Thompson +* Boaz Mohar +* Brendan Zhang +* Brennan Magee +* Bruno Zohreh +* BTWS +* buefox +* Cameron Davidson-Pilon +* Cameron Fackler +* cclauss +* ch3rn0v +* Charles Ruan +* chelseatroy +* Chen Karako +* Chris Holdgraf +* Christoph Deil +* Christoph Gohlke +* Cimarron Mittelsteadt +* CJ Carey +* cknd +* cldssty +* clintval +* Cody Scot +* Colin +* Conner R. Phillips +* Craig Citro +* DaCoEx +* dahlbaek +* Dakota Blair +* Damian +* Dan Hickstein +* Dana +* Daniel C. Marcu +* Daniel Laidig +* danielballan +* Danny Hermes +* daronjp +* DaveL17 +* David A +* David Brooks +* David Kent +* David Stansby +* deeenes +* deepyaman +* Derek Kim +* Derek Tropf +* Devashish Deshpande +* Diego Mora Cespedes +* Dietmar Schwertberger +* Dietrich Brunn +* Divyam Madaan +* dlmccaffrey +* Dmitry Shachnev +* Dora Fraeman +* DoriekeMG +* Dorota Jarecka +* Doug Blank +* Drew J. Sonne +* Duncan Macleod +* Dylan Evans +* E. G. Patrick Bos +* Egor Panfilov +* Elijah Schutz +* Elizabeth Seiver +* Elliott Sales de Andrade +* Elvis Stansvik +* Emlyn Price +* endolith +* Eric Dill +* Eric Firing +* Eric Galloway +* Eric Larson +* Eric Wang (Mac) +* Eric Wieser +* Erik M. Bray +* Erin Pintozzi +* et2010 +* Ethan Ligon +* Eugene Yurtsev +* Fabian Kloosterman +* Fabian-Robert Stöter +* FedeMiorelli +* Federico Ariza +* Felix +* Felix Kohlgrüber +* Felix Yan +* Filip Dimitrovski +* Florencia Noriega +* Florian Le Bourdais +* Franco Vaccari +* Francoise Provencher +* Frank Yu +* fredrik-1 +* fuzzythecat +* Gabe +* Gabriel Munteanu +* Gauravjeet +* Gaute Hope +* gcallah +* Geoffrey Spear +* gnaggnoyil +* goldstarwebs +* Graeme Smecher +* greg-roper +* gregorybchris +* Grillard +* Guillermo Breto +* Gustavo Goretkin +* Hajoon Choi +* Hakan Kucukdereli +* hannah +* Hans Moritz Günther +* Harnesser +* Harshal Prakash Patankar +* Harshit Patni +* Hassan Kibirige +* Hastings Greer +* Heath Henley +* Heiko Oberdiek +* Helder +* helmiriawan +* Henning Pohl +* Herbert Kruitbosch +* HHest +* Hubert Holin +* Ian Thomas +* Ida Hjorth +* Ildar Akhmetgaleev +* ilivni +* Ilya Flyamer +* ImportanceOfBeingErnest +* ImSoErgodic +* Isa Hassen +* Isaac Schwabacher +* Isaac Slavitt +* Ismo Toijala +* J Alammar +* J. Goutin +* Jaap Versteegh +* Jacob McDonald +* jacob-on-github +* Jae-Joon Lee +* Jake Vanderplas +* James A. Bednar +* Jamie Nunez +* Jan Koehler +* Jan Schlüter +* Jan Schulz +* Jarrod Millman +* Jason King +* Jason Neal +* Jason Zheng +* jbhopkins +* jdollichon +* Jeffrey Hokanson @ Loki +* JelsB +* Jens Hedegaard Nielsen +* Jerry Lui +* jerrylui803 +* jhelie +* jli +* Jody Klymak +* joelostblom +* Johannes Wienke +* John Hoffman +* John Vandenberg +* Johnny Gill +* JojoBoulix +* jonchar +* Joseph Albert +* Joseph Fox-Rabinovitz +* Joseph Jon Booker +* Joseph Martinot-Lagarde +* Jouni K. Seppänen +* Juan Nunez-Iglesias +* Julia Sprenger +* Julian Mehne +* Julian V. Modesto +* Julien Lhermitte +* Julien Schueller +* Jun Tan +* Justin Cai +* Jörg Dietrich +* Kacper Kowalik (Xarthisius) +* Kanchana Ranasinghe +* Katrin Leinweber +* Keerysanth Sribaskaran +* keithbriggs +* Kenneth Ma +* Kevin Davies +* Kevin Ji +* Kevin Keating +* Kevin Rose +* Kexuan Sun +* khyox +* Kieran Ramos +* Kjartan Myrdal +* Kjell Le +* Klara Gerlei +* klaus +* klonuo +* Kristen M. Thyng +* kshramt +* Kyle Bridgemohansingh +* Kyle Sunden +* Kyler Brown +* Laptop11_ASPP2016 +* lboogaard +* legitz7 +* Leo Singer +* Leon Yin +* Levi Kilcher +* Liam Brannigan +* Lionel Miller +* lspvic +* Luca Verginer +* Luis Pedro Coelho +* luz.paz +* lzkelley +* Maarten Baert +* Magnus Nord +* mamrehn +* Manish Devgan +* Manuel Jung +* Mark Harfouche +* Martin Fitzpatrick +* Martin Spacek +* Massimo Santini +* Matt Hancock +* Matt Newville +* Matthew Bell +* Matthew Brett +* Matthias Bussonnier +* Matthias Lüthi +* Matti Picus +* Maximilian Albert +* Maximilian Maahn +* Maximilian Nöthe +* mcquin +* Mher Kazandjian +* Michael Droettboom +* Michael Scott Cuthbert +* Michael Seifert +* Michiel de Hoon +* Mike Henninger +* Mike Jarvis +* MinRK +* Mitar +* mitch +* mlub +* mobando +* Molly Rossow +* Moritz Boehle +* muahah +* Mudit Surana +* myyc +* Naoya Kanai +* Nathan Goldbaum +* Nathan Musoke +* Nathaniel M. Beaver +* navdeep rana +* nbrunett +* Nelle Varoquaux +* nemanja +* neok-m4700 +* nepix32 +* Nick Forrington +* Nick Garvey +* Nick Papior +* Nico Schlömer +* Nicolas P. Rougier +* Nicolas Tessore +* Nik Quibin +* Nikita Kniazev +* Nils Werner +* Ninad Bhat +* nmartensen +* Norman Fomferra +* ob +* OceanWolf +* Olivier +* Orso Meneghini +* Osarumwense +* Pankaj Pandey +* Paramonov Andrey +* Pastafarianist +* Paul Ganssle +* Paul Hobson +* Paul Ivanov +* Paul Kirow +* Paul Romano +* Paul Seyfert +* Pavol Juhas +* pdubcali +* Pete Huang +* Pete Peterson +* Peter Mackenzie-Helnwein +* Peter Mortensen +* Peter Würtz +* Petr Danecek +* pharshalp +* Phil Elson +* Phil Ruffwind +* Pierre de Buyl +* Pierre Haessig +* Pranav Garg +* productivememberofsociety666 +* PrzemysÅ‚aw DÄ…bek +* Qingpeng "Q.P." Zhang +* RAKOTOARISON Herilalaina +* Ramiro Gómez +* Randy Olson +* rebot +* Richard Gowers +* Rishikesh +* Rob Harrigan +* Robin Dunn +* Robin Neatherway +* Robin Wilson +* Ronald Hartley-Davies +* Roy Smith +* Rui Lopes +* ruin +* rvhbooth +* Ryan +* Ryan May +* Ryan Morshead +* RyanPan +* s0vereign +* Saket Choudhary +* Salganos +* Salil Vanvari +* Salinder Sidhu +* Sam Vaughan +* Samson +* Samuel St-Jean +* Sander +* scls19fr +* Scott Howard +* Scott Lasley +* scott-vsi +* Sean Farley +* Sebastian Raschka +* Sebastián Vanrell +* Seraphim Alvanides +* Sergey B Kirpichev +* serv-inc +* settheory +* shaunwbell +* Simon Gibbons +* simonpf +* sindunuragarp +* Sourav Singh +* Stefan Pfenninger +* Stephan Erb +* Sterling Smith +* Steven Silvester +* Steven Tilley +* stone +* stonebig +* Tadeo Corradi +* Taehoon Lee +* Tanuj +* Taras +* Taras Kuzyo +* TD22057 +* Ted Petrou +* terranjp +* Terrence J. Katzenbaer +* Terrence Katzenbaer +* The Gitter Badger +* Thomas A Caswell +* Thomas Hisch +* Thomas Levine +* Thomas Mansencal +* Thomas Robitaille +* Thomas Spura +* Thomas VINCENT +* Thorsten Liebig +* thuvejan +* Tian Xia +* Till Stensitzki +* Tim Hoffmann +* tmdavison +* Tobias Froehlich +* Tobias Megies +* Tom +* Tom Augspurger +* Tom Dupré la Tour +* tomoemon +* tonyyli +* Trish Gillett-Kawamoto +* Truong Pham +* Tuan Dung Tran +* u55 +* ultra-andy +* V. R +* vab9 +* Valentin Schmidt +* Vedant Nanda +* Vidur Satija +* vraelvrangr +* Víctor Zabalza +* WANG Aiyong +* Warren Weckesser +* watkinrt +* Wieland Hoffmann +* Will Silva +* William Granados +* William Mallard +* Xufeng Wang +* y1thof +* Yao-Yuan Mao +* Yuval Langer +* Zac Hatfield-Dodds +* Zbigniew JÄ™drzejewski-Szmek +* zhangeugenia +* ZhaoZhonglun1991 +* zhoubecky +* ZWL +* Élie Gouzien +* Ðндрей Парамонов + +GitHub issues and pull requests: + +Pull Requests (598): + +* :ghpull:`12145`: Doc final 3.0 docs +* :ghpull:`12143`: Backport PR #12142 on branch v3.0.x (Unbreak formlayout for image edits.) +* :ghpull:`12142`: Unbreak formlayout for image edits. +* :ghpull:`12135`: Backport PR #12131 on branch v3.0.x (Fixes currently release version of cartopy) +* :ghpull:`12131`: Fixes currently release version of cartopy +* :ghpull:`12129`: Backports for 3.0 +* :ghpull:`12132`: Backport PR #12130 on branch v3.0.x (Mention colorbar.minorticks_on/off in references) +* :ghpull:`12130`: Mention colorbar.minorticks_on/off in references +* :ghpull:`12099`: FIX: make sure all ticks show up for colorbar minor tick +* :ghpull:`11962`: Propagate changes to backend loading to setup/setupext. +* :ghpull:`12128`: Unbreak the Sphinx 1.8 build by renaming :math: to :mathmpl:. +* :ghpull:`12126`: Backport PR #12117 on branch v3.0.x (Fix Agg extent calculations for empty draws) +* :ghpull:`12113`: Backport PR #12112 on branch v3.0.x (Reword the LockDraw docstring.) +* :ghpull:`12112`: Reword the LockDraw docstring. +* :ghpull:`12110`: Backport PR #12109 on branch v3.0.x (Pin to sphinx<1.8; unremove sphinxext.mathmpl.) +* :ghpull:`12084`: DOC: link palettable +* :ghpull:`12096`: Backport PR #12092 on branch v3.0.x (Update backend_qt5agg to fix PySide2 mem issues) +* :ghpull:`12083`: Backport PR #12012 on branch v3.0.x (FIX: fallback text renderer to fig._cachedRenderer, if none found) +* :ghpull:`12081`: Backport PR #12037 on branch v3.0.x (Fix ArtistInspector.get_aliases.) +* :ghpull:`12080`: Backport PR #12053 on branch v3.0.x (Fix up some OSX backend issues) +* :ghpull:`12037`: Fix ArtistInspector.get_aliases. +* :ghpull:`12053`: Fix up some OSX backend issues +* :ghpull:`12064`: Backport PR #11971 on branch v3.0.x (FIX: use cached renderer on Legend.get_window_extent) +* :ghpull:`12063`: Backport PR #12036 on branch v3.0.x (Interactive tests update) +* :ghpull:`11928`: Update doc/conf.py to avoid warnings with (future) sphinx 1.8. +* :ghpull:`12048`: Backport PR #12047 on branch v3.0.x (Remove asserting about current backend at the end of mpl_test_settings.) +* :ghpull:`11971`: FIX: use cached renderer on Legend.get_window_extent +* :ghpull:`12036`: Interactive tests update +* :ghpull:`12029`: Backport PR #12022 on branch v3.0.x (Remove intent to deprecate rcParams["backend_fallback"].) +* :ghpull:`12047`: Remove asserting about current backend at the end of mpl_test_settings. +* :ghpull:`12020`: Backport PR #12019 on branch v3.0.x (typo: s/unmultipled/unmultiplied) +* :ghpull:`12022`: Remove intent to deprecate rcParams["backend_fallback"]. +* :ghpull:`12028`: Backport PR #12023 on branch v3.0.x (Fix deprecation check in wx Timer.) +* :ghpull:`12023`: Fix deprecation check in wx Timer. +* :ghpull:`12019`: typo: s/unmultipled/unmultiplied +* :ghpull:`12017`: Backport PR #12016 on branch v3.0.x (Fix AttributeError in GTK3Agg backend) +* :ghpull:`12016`: Fix AttributeError in GTK3Agg backend +* :ghpull:`11991`: Backport PR #11988 on branch v3.0.x +* :ghpull:`11978`: Backport PR #11973 on branch v3.0.x +* :ghpull:`11968`: Backport PR #11963 on branch v3.0.x +* :ghpull:`11967`: Backport PR #11961 on branch v3.0.x +* :ghpull:`11969`: Fix an invalid escape sequence. +* :ghpull:`11963`: Fix some lgtm convention alerts +* :ghpull:`11961`: Downgrade backend_version log to DEBUG level. +* :ghpull:`11953`: Backport PR #11896 on branch v3.0.x +* :ghpull:`11896`: Resolve backend in rcParams.__getitem__("backend"). +* :ghpull:`11950`: Backport PR #11934 on branch v3.0.x +* :ghpull:`11952`: Backport PR #11949 on branch v3.0.x +* :ghpull:`11949`: Remove test2.png from examples. +* :ghpull:`11934`: Suppress the "non-GUI backend" warning from the .. plot:: directive... +* :ghpull:`11918`: Backport PR #11917 on branch v3.0.x +* :ghpull:`11916`: Backport PR #11897 on branch v3.0.x +* :ghpull:`11915`: Backport PR #11591 on branch v3.0.x +* :ghpull:`11897`: HTMLWriter, put initialisation of frames in setup +* :ghpull:`11591`: BUG: correct the scaling in the floating-point slop test. +* :ghpull:`11910`: Backport PR #11907 on branch v3.0.x +* :ghpull:`11907`: Move TOC back to top in axes documentation +* :ghpull:`11904`: Backport PR #11900 on branch v3.0.x +* :ghpull:`11900`: Allow args to pass through _allow_super_init +* :ghpull:`11889`: Backport PR #11847 on branch v3.0.x +* :ghpull:`11890`: Backport PR #11850 on branch v3.0.x +* :ghpull:`11850`: FIX: macosx framework check +* :ghpull:`11883`: Backport PR #11862 on branch v3.0.x +* :ghpull:`11882`: Backport PR #11876 on branch v3.0.x +* :ghpull:`11876`: MAINT Better error message for number of colors versus number of data… +* :ghpull:`11862`: Fix NumPy FutureWarning for non-tuple indexing. +* :ghpull:`11845`: Use Format_ARGB32_Premultiplied instead of RGBA8888 for Qt backends. +* :ghpull:`11843`: Remove unnecessary use of nose. +* :ghpull:`11600`: backend switching -- don't create a public fallback API +* :ghpull:`11833`: adding show inheritance to autosummary template +* :ghpull:`11828`: changed warning in animation +* :ghpull:`11829`: func animation warning changes +* :ghpull:`11826`: DOC documented more of the gridspec options +* :ghpull:`11818`: Merge v2.2.x +* :ghpull:`11821`: DOC: remove multicolumns from examples +* :ghpull:`11819`: DOC: fix minor typo in figure example +* :ghpull:`11722`: Remove unnecessary hacks from setup.py. +* :ghpull:`11802`: gridspec tutorial edits +* :ghpull:`11801`: update annotations +* :ghpull:`11734`: Small cleanups to backend_agg. +* :ghpull:`11785`: Add missing API changes +* :ghpull:`11788`: Fix DeprecationWarning on LocatableAxes +* :ghpull:`11558`: Added xkcd Style for Markers (plot only) +* :ghpull:`11755`: Add description for metadata argument of savefig +* :ghpull:`11703`: FIX: make update-from also set the original face/edgecolor +* :ghpull:`11765`: DOC: reorder examples and fix top level heading +* :ghpull:`11724`: Fix cairo's image inversion and alpha misapplication. +* :ghpull:`11726`: Consolidate agg-buffer examples. +* :ghpull:`11754`: FIX: update spine positions before get extents +* :ghpull:`11779`: Remove unused attribute in tests. +* :ghpull:`11770`: Correct errors in documentation +* :ghpull:`11778`: Unpin pandas in the CI. +* :ghpull:`11772`: Clarifying an error message +* :ghpull:`11760`: Switch grid documentation to numpydoc style +* :ghpull:`11705`: Suppress/fix some test warnings. +* :ghpull:`11763`: Pin OSX CI to numpy<1.15 to unbreak the build. +* :ghpull:`11767`: Add tolerance to csd frequency test +* :ghpull:`11757`: PGF backend output text color even if black +* :ghpull:`11751`: Remove the unused 'verbose' option from setupext. +* :ghpull:`9084`: Require calling a _BoundMethodProxy to get the underlying callable. +* :ghpull:`11752`: Fix section level of Previous Whats New +* :ghpull:`10513`: Replace most uses of getfilesystemencoding by os.fs{en,de}code. +* :ghpull:`11739`: fix tight_layout bug #11737 +* :ghpull:`11744`: minor doc update on axes_grid1's inset_axes +* :ghpull:`11729`: Pass 'figure' as kwarg to FigureCanvasQt5Agg super __init__. +* :ghpull:`11736`: Remove unused needs_sphinx marker; move importorskip to toplevel. +* :ghpull:`11731`: Directly get the size of the renderer buffer from the renderer. +* :ghpull:`11717`: DOC: fix broken link in inset-locator example +* :ghpull:`11723`: Start work on making colormaps picklable. +* :ghpull:`11721`: Remove some references to colorConverter. +* :ghpull:`11713`: Don't assume cwd in test_ipynb. +* :ghpull:`11026`: ENH add an inset_axes to the axes class +* :ghpull:`11712`: Fix drawing on qt+retina. +* :ghpull:`11714`: docstring for Figure.tight_layout don't include renderer parameter +* :ghpull:`8951`: Let QPaintEvent tell us what region to repaint. +* :ghpull:`11234`: Add fig.add_artist method +* :ghpull:`11706`: Remove unused private method. +* :ghpull:`11637`: Split API changes into individual pages +* :ghpull:`10403`: Deprecate LocatableAxes from toolkits +* :ghpull:`11699`: Dedent overindented rst bullet lists. +* :ghpull:`11701`: Use skipif instead of xfail when test dependencies are missing. +* :ghpull:`11700`: Don't use pytest -rw now that pytest-warnings is builtin. +* :ghpull:`11696`: Don't force backend in toolmanager example. +* :ghpull:`11690`: Avoid using private APIs in examples. +* :ghpull:`11684`: Style +* :ghpull:`11666`: TESTS: Increase tolerance for aarch64 tests +* :ghpull:`11680`: Boring style fixes. +* :ghpull:`11678`: Use super() instead of manually fetching supermethods for parasite axes. +* :ghpull:`11679`: Remove pointless draw() at the end of static examples. +* :ghpull:`11676`: Remove unused C++ code. +* :ghpull:`11010`: ENH: Add gridspec method to figure, and subplotspecs +* :ghpull:`11672`: Add comment re: use of lru_cache in PsfontsMap. +* :ghpull:`11674`: Boring style fixes. +* :ghpull:`10954`: Cache various dviread constructs globally. +* :ghpull:`9150`: Don't update style-blacklisted rcparams in rc_* functions +* :ghpull:`10936`: Simplify tkagg C extension. +* :ghpull:`11378`: SVG Backend gouraud_triangle Correction +* :ghpull:`11383`: FIX: Improve *c* (color) kwarg checking in scatter and the related exceptions +* :ghpull:`11627`: FIX: CL avoid fully collapsed axes +* :ghpull:`11504`: Bump pgi requirement to 0.0.11.2. +* :ghpull:`11640`: Fix barplot color if none and alpha is set +* :ghpull:`11443`: changed paths in kwdocs +* :ghpull:`11626`: Minor docstring fixes +* :ghpull:`11631`: DOC: better tight_layout error handling +* :ghpull:`11651`: Remove unused imports in examples +* :ghpull:`11633`: Clean up next api_changes +* :ghpull:`11643`: Fix deprecation messages. +* :ghpull:`9223`: Set norm to log if bins=='log' in hexbin +* :ghpull:`11622`: FIX: be forgiving about the event for enterEvent not having a pos +* :ghpull:`11581`: backend switching. +* :ghpull:`11616`: Fix some doctest issues +* :ghpull:`10872`: Cleanup _plot_args_replacer logic +* :ghpull:`11617`: Clean up what's new +* :ghpull:`11610`: FIX: let colorbar extends work for PowerNorm +* :ghpull:`11615`: Revert glyph warnings +* :ghpull:`11614`: CI: don't run tox to test pytz +* :ghpull:`11603`: Doc merge up +* :ghpull:`11613`: Make flake8 exceptions explicit +* :ghpull:`11611`: Fix css for parameter types +* :ghpull:`10001`: MAINT/BUG: Don't use 5-sided quadrilaterals in Axes3D.plot_surface +* :ghpull:`10234`: PowerNorm: do not clip negative values +* :ghpull:`11398`: Simplify retrieval of cache and config directories +* :ghpull:`10682`: ENH have ax.get_tightbbox have a bbox around all artists attached to axes. +* :ghpull:`11590`: Don't associate Wx timers with the parent frame. +* :ghpull:`10245`: Cache paths of fonts shipped with mpl relative to the mpl data path. +* :ghpull:`11381`: Deprecate text.latex.unicode. +* :ghpull:`11601`: FIX: subplots don't mutate kwargs passed by user. +* :ghpull:`11609`: Remove _macosx.NavigationToolbar. +* :ghpull:`11608`: Remove some conditional branches in examples for wx<4. +* :ghpull:`11604`: TST: Place animation files in a temp dir. +* :ghpull:`11605`: Suppress a spurious missing-glyph warning with ft2font. +* :ghpull:`11360`: Pytzectomy +* :ghpull:`10885`: Move GTK3 setupext checks to within the process. +* :ghpull:`11081`: Help tool for Wx backends +* :ghpull:`10851`: Wx Toolbar for ToolManager +* :ghpull:`11247`: Remove mplDeprecation +* :ghpull:`9795`: Backend switching +* :ghpull:`9426`: Don't mark a patch transform as set if the parent transform is not set. +* :ghpull:`9175`: Warn on freetype missing glyphs. +* :ghpull:`11412`: Make contour and contourf color assignments consistent. +* :ghpull:`11477`: Enable flake8 and re-enable it everywhere +* :ghpull:`11165`: Fix figure window icon +* :ghpull:`11584`: ENH: fix colorbar bad minor ticks +* :ghpull:`11438`: ENH: add get_gridspec convenience method to subplots +* :ghpull:`11451`: Cleanup Matplotlib API docs +* :ghpull:`11579`: DOC update some examples to use constrained_layout=True +* :ghpull:`11594`: Some more docstring cleanups. +* :ghpull:`11593`: Skip wx interactive tests on OSX. +* :ghpull:`11592`: Remove some extra spaces in docstrings/comments. +* :ghpull:`11585`: Some doc cleanup of Triangulation +* :ghpull:`10474`: Use TemporaryDirectory instead of mkdtemp in a few places. +* :ghpull:`11240`: Deprecate the examples.directory rcParam. +* :ghpull:`11370`: Sorting drawn artists by their zorder when blitting using FuncAnimation +* :ghpull:`11576`: Add parameter doc to save_diff_image +* :ghpull:`11573`: Inline setup_external_compile into setupext. +* :ghpull:`11571`: Cleanup stix_fonts_demo example. +* :ghpull:`11563`: Use explicit signature in pyplot.close() +* :ghpull:`9801`: ENH: Change default Autodatelocator *interval_multiples* +* :ghpull:`11570`: More simplifications to FreeType setup on Windows. +* :ghpull:`11401`: Some py3fications. +* :ghpull:`11566`: Cleanups. +* :ghpull:`11520`: Add private API retrieving the current event loop and backend GUI info. +* :ghpull:`11544`: Restore axes sharedness when unpickling. +* :ghpull:`11568`: Figure.text changes +* :ghpull:`11248`: Simplify FreeType Windows build. +* :ghpull:`11556`: Fix colorbar bad ticks +* :ghpull:`11494`: Fix CI install of wxpython. +* :ghpull:`11564`: triinterpolate cleanups. +* :ghpull:`11548`: Use numpydoc-style parameter lists for choices +* :ghpull:`9583`: Add edgecolors kwarg to contourf +* :ghpull:`10275`: Update contour.py and widget.py +* :ghpull:`11547`: Fix example links +* :ghpull:`11555`: Fix spelling in title +* :ghpull:`11404`: FIX: don't include text at -inf in bbox +* :ghpull:`11455`: Fixing the issue where right column and top row generate wrong stream… +* :ghpull:`11297`: Prefer warn_deprecated instead of warnings.warn. +* :ghpull:`11495`: Update the documentation guidelines +* :ghpull:`11545`: Doc: fix x(filled) marker image +* :ghpull:`11287`: Maintain artist addition order in Axes.mouseover_set. +* :ghpull:`11530`: FIX: Ensuring both x and y attrs of LocationEvent are int +* :ghpull:`10336`: Use Integral and Real in typechecks rather than explicit types. +* :ghpull:`10298`: Apply gtk3 background. +* :ghpull:`10297`: Fix gtk3agg alpha channel. +* :ghpull:`9094`: axisbelow should just set zorder. +* :ghpull:`11542`: Documentation polar grids +* :ghpull:`11459`: Doc changes in add_subplot and add_axes +* :ghpull:`10908`: Make draggable callbacks check that artist has not been removed. +* :ghpull:`11522`: Small cleanups. +* :ghpull:`11539`: DOC: talk about sticky edges in Axes.margins +* :ghpull:`11540`: adding axes to module list +* :ghpull:`11537`: Fix invalid value warning when autoscaling with no data limits +* :ghpull:`11512`: Skip 3D rotation example in sphinx gallery +* :ghpull:`11538`: Re-enable pep8 on examples folder +* :ghpull:`11136`: Move remaining examples from api/ +* :ghpull:`11519`: Raise ImportError on failure to import backends. +* :ghpull:`11529`: add documentation for quality in savefig +* :ghpull:`11528`: Replace an unnecessary zip() in mplot3d by numpy ops. +* :ghpull:`11492`: add __repr__ to GridSpecBase +* :ghpull:`11521`: Add missing ``.`` to rcParam +* :ghpull:`11491`: Fixed the source path on windows in rcparam_role +* :ghpull:`11514`: Remove embedding_in_tk_canvas, which demonstrated a private API. +* :ghpull:`11507`: Fix embedding_in_tk_canvas example. +* :ghpull:`11513`: Changed docstrings in Text +* :ghpull:`11503`: Remove various mentions of the now removed GTK(2) backend. +* :ghpull:`11493`: Update a test to a figure-equality test. +* :ghpull:`11501`: Treat empty $MPLBACKEND as an unset value. +* :ghpull:`11395`: Various fixes to deprecated and warn_deprecated. +* :ghpull:`11408`: Figure equality-based tests. +* :ghpull:`11461`: Fixed bug in rendering font property kwargs list +* :ghpull:`11397`: Replace ACCEPTS by standard numpydoc params table. +* :ghpull:`11483`: Use pip requirements files for travis build +* :ghpull:`11481`: remove more pylab references +* :ghpull:`10940`: Run flake8 instead of pep8 on Python 3.6 +* :ghpull:`11476`: Remove pylab references +* :ghpull:`11448`: Link rcParams role to docs +* :ghpull:`11424`: DOC: point align-ylabel demo to new align-label functions +* :ghpull:`11454`: add subplots to axes documentation +* :ghpull:`11470`: Hyperlink DOIs against preferred resolver +* :ghpull:`11421`: DOC: make signature background grey +* :ghpull:`11457`: Search $CPATH for include directories +* :ghpull:`11456`: DOC: fix minor typo in figaspect +* :ghpull:`11293`: Lim parameter naming +* :ghpull:`11447`: Do not use class attributes as defaults for instance attributes +* :ghpull:`11449`: Slightly improve doc sidebar layout +* :ghpull:`11224`: Add deprecation messages for unused kwargs in FancyArrowPatch +* :ghpull:`11437`: Doc markersupdate +* :ghpull:`11417`: FIX: better default spine path (for logit) +* :ghpull:`11406`: Backport PR #11403 on branch v2.2.2-doc +* :ghpull:`11427`: FIX: pathlib in nbagg +* :ghpull:`11428`: Doc: Remove huge note box from examples. +* :ghpull:`11392`: Deprecate the ``verts`` kwarg to ``scatter``. +* :ghpull:`8834`: WIP: Contour log extension +* :ghpull:`11402`: Remove unnecessary str calls. +* :ghpull:`11399`: Autogenerate credits.rst +* :ghpull:`11382`: plt.subplots and plt.figure docstring changes +* :ghpull:`11388`: DOC: Constrained layout tutorial improvements +* :ghpull:`11400`: Correct docstring for axvspan() +* :ghpull:`11396`: Remove some (minor) comments regarding Py2. +* :ghpull:`11210`: FIX: don't pad axes for ticks if they aren't visible or axis off +* :ghpull:`11362`: Fix tox configuration +* :ghpull:`11366`: Improve docstring of Axes.spy +* :ghpull:`11289`: io.open and codecs.open are redundant with open on Py3. +* :ghpull:`11213`: MNT: deprecate patches.YAArrow +* :ghpull:`11352`: Catch a couple of test warnings +* :ghpull:`11292`: Simplify cleanup decorator implementation. +* :ghpull:`11349`: Remove non-existent files from MANIFEST.IN +* :ghpull:`8774`: Git issue #7216 - Add a "ruler" tool to the plot UI +* :ghpull:`11348`: Make OSX's blit() have a consistent signature with other backends. +* :ghpull:`11345`: Revert "Deprecate text.latex.unicode." +* :ghpull:`11250`: [WIP] Add tutorial for LogScale +* :ghpull:`11223`: Add an arrow tutorial +* :ghpull:`10212`: Categorical refactor +* :ghpull:`11339`: Convert Ellipse docstring to numpydoc +* :ghpull:`11255`: Deprecate text.latex.unicode. +* :ghpull:`11338`: Fix typos +* :ghpull:`11332`: Let plt.rc = matplotlib.rc, instead of being a trivial wrapper. +* :ghpull:`11331`: multiprocessing.set_start_method() --> mp.set_start_method() +* :ghpull:`9948`: Add ``ealpha`` option to ``errorbar`` +* :ghpull:`11329`: Minor docstring update of thumbnail +* :ghpull:`9551`: Refactor backend loading +* :ghpull:`11328`: Undeprecate Polygon.xy from #11299 +* :ghpull:`11318`: Improve docstring of imread() and imsave() +* :ghpull:`11311`: Simplify image.thumbnail. +* :ghpull:`11225`: Add stacklevel=2 to some more warnings.warn() calls +* :ghpull:`11313`: Add changelog entry for removal of proprietary sphinx directives. +* :ghpull:`11323`: Fix infinite loop for connectionstyle + add some tests +* :ghpull:`11314`: API changes: use the heading format defined in README.txt +* :ghpull:`11320`: Py3fy multiprocess example. +* :ghpull:`6254`: adds two new cyclic color schemes +* :ghpull:`11268`: DOC: Sanitize some internal documentation links +* :ghpull:`11300`: Start replacing ACCEPTS table by parsing numpydoc. +* :ghpull:`11298`: Automagically set the stacklevel on warnings. +* :ghpull:`11277`: Avoid using MacRoman encoding. +* :ghpull:`11295`: Use sphinx builtin only directive instead of custom one. +* :ghpull:`11305`: Reuse the noninteractivity warning from Figure.show in _Backend.show. +* :ghpull:`11307`: Avoid recursion for subclasses of str that are also "PathLike" in to_filehandle() +* :ghpull:`11304`: Re-remove six from INSTALL.rst. +* :ghpull:`11299`: Fix a bunch of doc/comment typos in patches.py. +* :ghpull:`11301`: Undefined name: cbook --> matplotlib.cbook +* :ghpull:`11254`: Update INSTALL.rst. +* :ghpull:`11267`: FIX: allow nan values in data for plt.hist +* :ghpull:`11271`: Better argspecs for Axes.stem +* :ghpull:`11272`: Remove commented-out code, unused imports +* :ghpull:`11280`: Trivial cleanups +* :ghpull:`10514`: Cleanup/update cairo + gtk compatibility matrix. +* :ghpull:`11282`: Reduce the use of C++ exceptions +* :ghpull:`11263`: Fail gracefully if can't decode font names +* :ghpull:`11278`: Remove conditional path for sphinx <1.3 in plot_directive. +* :ghpull:`11273`: Include template matplotlibrc in package_data. +* :ghpull:`11265`: Minor cleanups. +* :ghpull:`11249`: Simplify FreeType build. +* :ghpull:`11158`: Remove dependency on six - we're Py3 only now! +* :ghpull:`10050`: Update Legend draggable API +* :ghpull:`11206`: More cleanups +* :ghpull:`11001`: DOC: improve legend bbox_to_anchor description +* :ghpull:`11258`: Removed comment in AGG backend that is no longer applicable +* :ghpull:`11062`: FIX: call constrained_layout twice +* :ghpull:`11251`: Re-run boilerplate.py. +* :ghpull:`11228`: Don't bother checking luatex's version. +* :ghpull:`11207`: Update venv gui docs wrt availability of PySide2. +* :ghpull:`11236`: Minor cleanups to setupext. +* :ghpull:`11239`: Reword the timeout error message in cbook._lock_path. +* :ghpull:`11204`: Test that boilerplate.py is correctly run. +* :ghpull:`11172`: ENH add rcparam to legend_title +* :ghpull:`11229`: Simplify lookup of animation external commands. +* :ghpull:`9086`: Add SVG animation. +* :ghpull:`11212`: Fix CirclePolygon __str__ + adding tests +* :ghpull:`6737`: Ternary +* :ghpull:`11216`: Yet another set of simplifications. +* :ghpull:`11056`: Simplify travis setup a bit. +* :ghpull:`11211`: Revert explicit linestyle kwarg on step() +* :ghpull:`11205`: Minor cleanups to pyplot. +* :ghpull:`11174`: Replace numeric loc by position string +* :ghpull:`11208`: Don't crash qt figure options on unknown marker styles. +* :ghpull:`11195`: Some unrelated cleanups. +* :ghpull:`11192`: Don't use deprecated get_texcommand in backend_pgf. +* :ghpull:`11197`: Simplify demo_ribbon_box.py. +* :ghpull:`11137`: Convert ``**kwargs`` to named arguments for a clearer API +* :ghpull:`10982`: Improve docstring of Axes.imshow +* :ghpull:`11182`: Use GLib.MainLoop() instead of deprecated GObject.MainLoop() +* :ghpull:`11185`: Fix undefined name error in backend_pgf. +* :ghpull:`10321`: Ability to scale axis by a fixed factor +* :ghpull:`8787`: Faster path drawing for the cairo backend (cairocffi only) +* :ghpull:`4559`: tight_layout: Use a different default gridspec +* :ghpull:`11179`: Convert internal tk focus helper to a context manager +* :ghpull:`11176`: Allow creating empty closed paths +* :ghpull:`10339`: Pass explicit font paths to fontspec in backend_pgf. +* :ghpull:`9832`: Minor cleanup to Text class. +* :ghpull:`11141`: Remove mpl_examples symlink. +* :ghpull:`10715`: ENH: add title_fontsize to legend +* :ghpull:`11166`: Set stacklevel to 2 for backend_wx +* :ghpull:`10934`: Autogenerate (via boilerplate) more of pyplot. +* :ghpull:`9298`: Cleanup blocking_input. +* :ghpull:`6329`: Set _text to '' if Text.set_text argument is None +* :ghpull:`11157`: Fix contour return link +* :ghpull:`11146`: Explicit args and refactor Axes.margins +* :ghpull:`11145`: Use kwonlyargs instead of popping from kwargs +* :ghpull:`11119`: PGF: Get unitless positions from Text elements (fix #11116) +* :ghpull:`9078`: New anchored direction arrows +* :ghpull:`11144`: Remove toplevel unit/ directory. +* :ghpull:`11148`: remove use of subprocess compatibility shim +* :ghpull:`11143`: Use debug level for debugging messages +* :ghpull:`11142`: Finish removing future imports. +* :ghpull:`11130`: Don't include the postscript title if it is not latin-1 encodable. +* :ghpull:`11093`: DOC: Fixup to AnchoredArtist examples in the gallery +* :ghpull:`11132`: pillow-dependency update +* :ghpull:`10446`: implementation of the copy canvas tool +* :ghpull:`9131`: FIX: prevent the canvas from jump sizes due to DPI changes +* :ghpull:`9454`: Batch ghostscript converter. +* :ghpull:`10545`: Change manual kwargs popping to kwonly arguments. +* :ghpull:`10950`: Actually ignore invalid log-axis limit setting +* :ghpull:`11096`: Remove support for bar(left=...) (as opposed to bar(x=...)). +* :ghpull:`11106`: py3fy art3d. +* :ghpull:`11085`: Use GtkShortcutsWindow for Help tool. +* :ghpull:`11099`: Deprecate certain marker styles that have simpler synonyms. +* :ghpull:`11100`: Some more deprecations of old, old stuff. +* :ghpull:`11098`: Make Marker.get_snap_threshold() always return a scalar. +* :ghpull:`11097`: Schedule a removal date for passing normed (instead of density) to hist. +* :ghpull:`9706`: Masking invalid x and/or weights in hist +* :ghpull:`11080`: Py3fy backend_qt5 + other cleanups to the backend. +* :ghpull:`10967`: updated the pyplot fill_between example to elucidate the premise;maki… +* :ghpull:`11075`: Drop alpha channel when saving comparison failure diff image. +* :ghpull:`9022`: Help tool +* :ghpull:`11045`: Help tool. +* :ghpull:`11076`: Don't create texput.{aux,log} in rootdir everytime tests are run. +* :ghpull:`11073`: py3fication of some tests. +* :ghpull:`11074`: bytes % args is back since py3.5 +* :ghpull:`11066`: Use chained comparisons where reasonable. +* :ghpull:`11061`: Changed tight_layout doc strings +* :ghpull:`11064`: Minor docstring format cleanup +* :ghpull:`11055`: Remove setup_tests_only.py. +* :ghpull:`11057`: Update Ellipse position with ellipse.center +* :ghpull:`10435`: Pathlibify font_manager (only internally, doesn't change the API). +* :ghpull:`10442`: Make the filternorm prop of Images a boolean rather than a {0,1} scalar. +* :ghpull:`9855`: ENH: make ax.get_position apply aspect +* :ghpull:`9987`: MNT: hist2d now uses pcolormesh instead of pcolorfast +* :ghpull:`11014`: Merge v2.2.x into master +* :ghpull:`11000`: FIX: improve Text repr to not error if non-float x and y. +* :ghpull:`10910`: FIX: return proper legend window extent +* :ghpull:`10915`: FIX: tight_layout having negative width axes +* :ghpull:`10408`: Factor out common code in _process_unit_info +* :ghpull:`10960`: Added share_tickers parameter to axes._AxesBase.twinx/y +* :ghpull:`10971`: Skip pillow animation test if pillow not importable +* :ghpull:`10970`: Simplify/fix some manual manipulation of len(args). +* :ghpull:`10958`: Simplify the grouper implementation. +* :ghpull:`10508`: Deprecate FigureCanvasQT.keyAutoRepeat. +* :ghpull:`10607`: Move notify_axes_change to FigureManagerBase class. +* :ghpull:`10215`: Test timers and (a bit) key_press_event for interactive backends. +* :ghpull:`10955`: Py3fy cbook, compare_backend_driver_results +* :ghpull:`10680`: Rewrite the tk C blitting code +* :ghpull:`9498`: Move title up if x-axis is on the top of the figure +* :ghpull:`10942`: Make active param in CheckBottons optional, default false +* :ghpull:`10943`: Allow pie textprops to take alignment and rotation arguments +* :ghpull:`10780`: Fix scaling of RadioButtons +* :ghpull:`10938`: Fix two undefined names +* :ghpull:`10685`: fix plt.show doesn't warn if a non-GUI backend +* :ghpull:`10689`: Declare global variables that are created elsewhere +* :ghpull:`10845`: WIP: first draft at replacing linkcheker +* :ghpull:`10898`: Replace "matplotlibrc" by "rcParams" in the docs where applicable. +* :ghpull:`10926`: Some more removals of deprecated APIs. +* :ghpull:`9173`: dynamically generate pyplot functions +* :ghpull:`10918`: Use function signatures in boilerplate.py. +* :ghpull:`10914`: Changed pie charts default shape to circle and added tests +* :ghpull:`10864`: ENH: Stop mangling default figure file name if file exists +* :ghpull:`10562`: Remove deprecated code in image.py +* :ghpull:`10798`: FIX: axes limits reverting to automatic when sharing +* :ghpull:`10485`: Remove the 'hold' kwarg from codebase +* :ghpull:`10571`: Use np.full{,_like} where appropriate. [requires numpy>=1.12] +* :ghpull:`10913`: Rely a bit more on rc_context. +* :ghpull:`10299`: Invalidate texmanager cache when any text.latex.* rc changes. +* :ghpull:`10906`: Deprecate ImageComparisonTest. +* :ghpull:`10904`: Improve docstring of clabel() +* :ghpull:`10912`: remove unused matplotlib.testing import +* :ghpull:`10876`: [wip] Replace _remove_method by _on_remove list of callbacks +* :ghpull:`10692`: Update afm docs and internal data structures +* :ghpull:`10896`: Update INSTALL.rst. +* :ghpull:`10905`: Inline knownfailureif. +* :ghpull:`10907`: No need to mark (unicode) strings as u"foo" anymore. +* :ghpull:`10903`: Py3fy testing machinery. +* :ghpull:`10901`: Remove Py2/3 portable code guide. +* :ghpull:`10900`: Remove some APIs deprecated in mpl2.1. +* :ghpull:`10902`: Kill some Py2 docs. +* :ghpull:`10887`: Added feature (Make pie charts circular by default #10789) +* :ghpull:`10884`: Style fixes to setupext.py. +* :ghpull:`10879`: Deprecate two-args for cycler() and set_prop_cycle() +* :ghpull:`10865`: DOC: use OO-ish interface in image, contour, field examples +* :ghpull:`8479`: FIX markerfacecolor / mfc not in rcparams +* :ghpull:`10314`: setattr context manager. +* :ghpull:`10013`: Allow rasterization for 3D plots +* :ghpull:`10158`: Allow mplot3d rasterization; adjacent cleanups. +* :ghpull:`10871`: Rely on rglob support rather than os.walk. +* :ghpull:`10878`: Change hardcoded brackets for Toolbar message +* :ghpull:`10708`: Py3fy webagg/nbagg. +* :ghpull:`10862`: py3ify table.py and correct some docstrings +* :ghpull:`10810`: Fix for plt.plot() does not support structured arrays as data= kwarg +* :ghpull:`10861`: More python3 cleanup +* :ghpull:`9903`: ENH: adjustable colorbar ticks +* :ghpull:`10831`: Minor docstring updates on binning related plot functions +* :ghpull:`9571`: Remove LaTeX checking in setup.py. +* :ghpull:`10097`: Reset extents in RectangleSelector when not interactive on press. +* :ghpull:`10686`: fix BboxConnectorPatch does not show facecolor +* :ghpull:`10801`: Fix undefined name. Add animation tests. +* :ghpull:`10857`: FIX: ioerror font cache, second try +* :ghpull:`10796`: Added descriptions for line bars and markers examples +* :ghpull:`10846`: Unsixification +* :ghpull:`10852`: Update docs re: pygobject in venv. +* :ghpull:`10847`: Py3fy axis.py. +* :ghpull:`10834`: Minor docstring updates on spectral plot functions +* :ghpull:`10778`: wx_compat is no more. +* :ghpull:`10609`: More wx cleanup. +* :ghpull:`10826`: Py3fy dates.py. +* :ghpull:`10837`: Correctly display error when running setup.py test. +* :ghpull:`10838`: Don't use private attribute in tk example. Fix Toolbar class rename. +* :ghpull:`10835`: DOC: Make colorbar tutorial examples look like colorbars. +* :ghpull:`10823`: Add some basic smoketesting for webagg (and wx). +* :ghpull:`10828`: Add print_rgba to backend_cairo. +* :ghpull:`10830`: Make function signatures more explicit +* :ghpull:`10829`: Use long color names for default rcParams +* :ghpull:`9776`: WIP: Lockout new converters Part 2 +* :ghpull:`10799`: DOC: make legend docstring interpolated +* :ghpull:`10818`: Deprecate vestigial Annotation.arrow. +* :ghpull:`10817`: Add test to imread from url. +* :ghpull:`10696`: Simplify venv docs. +* :ghpull:`10724`: Py3fication of unicode. +* :ghpull:`10815`: API: shift deprecation of TempCache class to 3.0 +* :ghpull:`10725`: FIX/TST constrained_layout remove test8 duplication +* :ghpull:`10705`: FIX: enable extend kwargs with log scale colorbar +* :ghpull:`10400`: numpydoc-ify art3d docstrings +* :ghpull:`10723`: repr style fixes. +* :ghpull:`10592`: Rely on generalized * and ** unpackings where possible. +* :ghpull:`9475`: Declare property aliases in a single place +* :ghpull:`10793`: A hodgepodge of Py3 & style fixes. +* :ghpull:`10794`: fixed comment typo +* :ghpull:`10768`: Fix crash when imshow encounters longdouble data +* :ghpull:`10774`: Remove dead wx testing code. +* :ghpull:`10756`: Fixes png showing inconsistent inset_axes position +* :ghpull:`10773`: Consider alpha channel from RGBA color of text for SVG backend text opacity rendering +* :ghpull:`10772`: API: check locator and formatter args when passed +* :ghpull:`10713`: Implemented support for 'markevery' in prop_cycle +* :ghpull:`10751`: make centre_baseline legal for Text.set_verticalalignment +* :ghpull:`10771`: FIX/TST OS X builds +* :ghpull:`10742`: FIX: reorder linewidth setting before linestyle +* :ghpull:`10714`: sys.platform is normalized to "linux" on Py3. +* :ghpull:`10542`: Minor cleanup: PEP8, PEP257 +* :ghpull:`10636`: Remove some wx version checks. +* :ghpull:`9731`: Make legend title fontsize obey fontsize kwarg by default +* :ghpull:`10697`: Remove special-casing of _remove_method when pickling. +* :ghpull:`10701`: Autoadd removal version to deprecation message. +* :ghpull:`10699`: Remove incorrect warning in gca(). +* :ghpull:`10674`: Fix getting polar axes in plt.polar() +* :ghpull:`10564`: Nested classes and instancemethods are directly picklable on Py3.5+. +* :ghpull:`10107`: Fix stay_span to reset onclick in SpanSelector. +* :ghpull:`10693`: Make markerfacecolor work for 3d scatterplots +* :ghpull:`10596`: Switch to per-file locking. +* :ghpull:`10532`: Py3fy backend_pgf. +* :ghpull:`10618`: Fixes #10501. python3 support and pep8 in jpl_units +* :ghpull:`10652`: Some py3fication for matplotlib/__init__, setupext. +* :ghpull:`10522`: Py3fy font_manager. +* :ghpull:`10666`: More figure-related doc updates +* :ghpull:`10507`: Remove Python 2 code from C extensions +* :ghpull:`10679`: Small fixes to gtk3 examples. +* :ghpull:`10426`: Delete deprecated backends +* :ghpull:`10488`: Bug Fix - Polar plot rectangle patch not transformed correctly (#8521) +* :ghpull:`9814`: figure_enter_event uses now LocationEvent instead of Event. Fix issue #9812. +* :ghpull:`9918`: Remove old nose testing code +* :ghpull:`10672`: Deprecation fixes. +* :ghpull:`10608`: Remove most APIs deprecated in 2.1. +* :ghpull:`10653`: Mock is in stdlib in Py3. +* :ghpull:`10603`: Remove workarounds for numpy<1.10. +* :ghpull:`10660`: Work towards removing reuse-of-axes-on-collision. +* :ghpull:`10661`: Homebrew python is now python 3 +* :ghpull:`10656`: Minor fixes to event handling docs. +* :ghpull:`10635`: Simplify setupext by using globs. +* :ghpull:`10632`: Support markers from Paths that consist of one line segment +* :ghpull:`10558`: Remove if six.PY2 code paths from boilerplate.py +* :ghpull:`10640`: Fix extra and missing spaces in constrainedlayout warning. +* :ghpull:`10624`: Some trivial py3fications. +* :ghpull:`10548`: Implement PdfPages for backend pgf +* :ghpull:`10614`: Use np.stack instead of list(zip()) in colorbar.py. +* :ghpull:`10621`: Cleanup and py3fy backend_gtk3. +* :ghpull:`10615`: More style fixes. +* :ghpull:`10604`: Minor style fixes. +* :ghpull:`10565`: Strip python 2 code from subprocess.py +* :ghpull:`10605`: Bump a tolerance in test_axisartist_floating_axes. +* :ghpull:`7853`: Use exact types for Py_BuildValue. +* :ghpull:`10591`: Switch to @-matrix multiplication. +* :ghpull:`10570`: Fix check_shared in test_subplots. +* :ghpull:`10569`: Various style fixes. +* :ghpull:`10593`: Use 'yield from' where appropriate. +* :ghpull:`10577`: Minor simplification to Figure.__getstate__ logic. +* :ghpull:`10549`: Source typos +* :ghpull:`10525`: Convert six.moves.xrange() to range() for Python 3 +* :ghpull:`10541`: More argumentless (py3) super() +* :ghpull:`10539`: TST: Replace assert_equal with plain asserts. +* :ghpull:`10534`: Modernize cbook.get_realpath_and_stat. +* :ghpull:`10524`: Remove unused private _StringFuncParser. +* :ghpull:`10470`: Remove Python 2 code from setup +* :ghpull:`10528`: py3fy examples +* :ghpull:`10520`: Py3fy mathtext.py. +* :ghpull:`10527`: Switch to argumentless (py3) super(). +* :ghpull:`10523`: The current master branch is now python 3 only. +* :ghpull:`10515`: Use feature detection instead of version detection +* :ghpull:`10432`: Use some new Python3 types +* :ghpull:`10475`: Use HTTP Secure for matplotlib.org +* :ghpull:`10383`: Fix some C++ warnings +* :ghpull:`10498`: Tell the lgtm checker that the project is Python 3 only +* :ghpull:`10505`: Remove backport of which() +* :ghpull:`10483`: Remove backports.functools_lru_cache +* :ghpull:`10492`: Avoid UnboundLocalError in drag_pan. +* :ghpull:`10491`: Simplify Mac builds on Travis +* :ghpull:`10481`: Remove python 2 compatibility code from dviread +* :ghpull:`10447`: Remove Python 2 compatibility code from backend_pdf.py +* :ghpull:`10468`: Replace is_numlike by isinstance(..., numbers.Number). +* :ghpull:`10439`: mkdir is in the stdlib in Py3. +* :ghpull:`10392`: FIX: make set_text(None) keep string empty instead of "None" +* :ghpull:`10425`: API: only support python 3.5+ +* :ghpull:`10316`: TST FIX pyqt5 5.9 +* :ghpull:`4625`: hist2d() is now using pcolormesh instead of pcolorfast + +Issues (123): + +* :ghissue:`12133`: Streamplot does not work for 29x29 grid +* :ghissue:`4429`: Error calculating scaling for radiobutton widget. +* :ghissue:`3293`: markerfacecolor / mfc not in rcparams +* :ghissue:`8109`: Cannot set the markeredgecolor by default +* :ghissue:`7942`: Extend keyword doesn't work with log scale. +* :ghissue:`5571`: Finish reorganizing examples +* :ghissue:`8307`: Colorbar with imshow(logNorm) shows unexpected minor ticks +* :ghissue:`6992`: plt.hist fails when data contains nan values +* :ghissue:`6483`: Range determination for data with NaNs +* :ghissue:`8059`: BboxConnectorPatch does not show facecolor +* :ghissue:`12134`: tight_layout flips images when making plots without displaying them +* :ghissue:`6739`: Make matplotlib fail more gracefully in headless environments +* :ghissue:`3679`: Runtime detection for default backend +* :ghissue:`11966`: CartoPy code gives attribute error +* :ghissue:`11844`: Backend related issues with matplotlib 3.0.0rc1 +* :ghissue:`12095`: colorbar minorticks (possibly release critical for 3.0) +* :ghissue:`12108`: Broken doc build with sphinx 1.8 +* :ghissue:`7366`: handle repaint requests better it qtAgg +* :ghissue:`11985`: Single shot timer not working correctly with MacOSX backend +* :ghissue:`10948`: OSX backend raises deprecation warning for enter_notify_event +* :ghissue:`11970`: Legend.get_window_extent now requires a renderer +* :ghissue:`8293`: investigate whether using a single instance of ghostscript for ps->png conversion can speed up the Windows build +* :ghissue:`7707`: Replace pep8 by pycodestyle for style checking +* :ghissue:`9135`: rcdefaults, rc_file_defaults, rc_file should not update backend if it has already been selected +* :ghissue:`12015`: AttributeError with GTK3Agg backend +* :ghissue:`11913`: plt.contour levels parameter don't work as intended if receive a single int +* :ghissue:`11846`: macosx backend won't load +* :ghissue:`11792`: Newer versions of ImageMagickWriter not found on windows +* :ghissue:`11858`: Adding "pie of pie" and "bar of pie" functionality +* :ghissue:`11852`: get_backend() backward compatibility +* :ghissue:`11629`: Importing qt_compat when no Qt binding is installed fails with NameError instead of ImportError +* :ghissue:`11842`: Failed nose import in test_annotation_update +* :ghissue:`11252`: Some API removals not documented +* :ghissue:`9404`: Drop support for python 2 +* :ghissue:`2625`: Markers in XKCD style +* :ghissue:`11749`: metadata kwarg to savefig is not documented +* :ghissue:`11702`: Setting alpha on legend handle changes patch color +* :ghissue:`8798`: gtk3cairo draw_image does not respect origin and mishandles alpha +* :ghissue:`11737`: Bug in tight_layout +* :ghissue:`11373`: Passing an incorrectly sized colour list to scatter should raise a relevant error +* :ghissue:`11756`: pgf backend doesn't set color of text when the color is black +* :ghissue:`11766`: test_axes.py::test_csd_freqs failing with numpy 1.15.0 on macOS +* :ghissue:`11750`: previous whats new is overindented on "what's new in mpl3.0 page" +* :ghissue:`11728`: Qt5 Segfaults on window resize +* :ghissue:`11709`: Repaint region is wrong on Retina display with Qt5 +* :ghissue:`11578`: wx segfaulting on OSX travis tests +* :ghissue:`11628`: edgecolor argument not working in matplotlib.pyplot.bar +* :ghissue:`11625`: plt.tight_layout() does not work with plt.subplot2grid +* :ghissue:`4993`: Version ~/.cache/matplotlib +* :ghissue:`7842`: If hexbin has logarithmic bins, use log formatter for colorbar +* :ghissue:`11607`: AttributeError: 'QEvent' object has no attribute 'pos' +* :ghissue:`11486`: Colorbar does not render with PowerNorm and min extend when using imshow +* :ghissue:`11582`: wx segfault +* :ghissue:`11515`: using 'sharex' once in 'subplots' function can affect subsequent calles to 'subplots' +* :ghissue:`10269`: input() blocks any rendering and event handling +* :ghissue:`10345`: Python 3.4 with Matplotlib 1.5 vs Python 3.6 with Matplotlib 2.1 +* :ghissue:`10443`: Drop use of pytz dependency in next major release +* :ghissue:`10572`: contour and contourf treat levels differently +* :ghissue:`11123`: Crash when interactively adding a number of subplots +* :ghissue:`11550`: Undefined names: 'obj_type' and 'cbook' +* :ghissue:`11138`: Only the first figure window has mpl icon, all other figures have default tk icon. +* :ghissue:`11510`: extra minor-ticks on the colorbar when used with the extend option +* :ghissue:`11369`: zorder of Artists not being respected when blitting with FuncAnimation +* :ghissue:`11452`: Streamplot ignores rightmost column and topmost row of velocity data +* :ghissue:`11284`: imshow of multiple images produces old pixel values printed in status bar +* :ghissue:`11496`: MouseEvent.x and .y have different types +* :ghissue:`11534`: Cross-reference margins and sticky edges +* :ghissue:`8556`: Add images of markers to the list of markers +* :ghissue:`11386`: Logit scale doesn't position x/ylabel correctly first draw +* :ghissue:`11384`: Undefined name 'Path' in backend_nbagg.py +* :ghissue:`11426`: nbagg broken on master. 'Path' is not defined... +* :ghissue:`11390`: Internal use of deprecated code +* :ghissue:`11203`: tight_layout reserves tick space even if disabled +* :ghissue:`11361`: Tox.ini does not work out of the box +* :ghissue:`11253`: Problem while changing current figure size in Jupyter notebook +* :ghissue:`11219`: Write an arrow tutorial +* :ghissue:`11322`: Really deprecate Patches.xy? +* :ghissue:`11294`: ConnectionStyle Angle3 hangs with specific parameters +* :ghissue:`9518`: Some ConnectionStyle not working +* :ghissue:`11306`: savefig and path.py +* :ghissue:`11077`: Font "DejaVu Sans" can only be used through fallback +* :ghissue:`10717`: Failure to find matplotlibrc when testing installed distribution +* :ghissue:`9912`: Cleaning up variable argument signatures +* :ghissue:`3701`: unit tests should compare pyplot.py with output from boilerplate.py +* :ghissue:`11183`: Undefined name 'system_fonts' in backend_pgf.py +* :ghissue:`11101`: Crash on empty patches +* :ghissue:`11124`: [Bug] savefig cannot save file with a Unicode name +* :ghissue:`7733`: Trying to set_ylim(bottom=0) on a log scaled axis changes plot +* :ghissue:`10319`: TST: pyqt 5.10 breaks pyqt5 interactive tests +* :ghissue:`10676`: Add source code to documentation +* :ghissue:`9207`: axes has no method to return new position after box is adjusted due to aspect ratio... +* :ghissue:`4615`: hist2d with log xy axis +* :ghissue:`10996`: Plotting text with datetime axis causes warning +* :ghissue:`7582`: Report date and time of cursor position on a plot_date plot +* :ghissue:`10114`: Remove mlab from examples +* :ghissue:`10342`: imshow longdouble not truly supported +* :ghissue:`8062`: tight_layout + lots of subplots + long ylabels inverts yaxis +* :ghissue:`4413`: Long axis title alters xaxis length and direction with ``plt.tight_layout()`` +* :ghissue:`1415`: Plot title should be shifted up when xticks are set to the top of the plot +* :ghissue:`10789`: Make pie charts circular by default +* :ghissue:`10941`: Cannot set text alignment in pie chart +* :ghissue:`7908`: plt.show doesn't warn if a non-GUI backend is being used +* :ghissue:`10502`: 'FigureManager' is an undefined name in backend_wx.py +* :ghissue:`10062`: axes limits revert to automatic on sharing axes? +* :ghissue:`9246`: ENH: make default colorbar ticks adjust as nicely as axes ticks +* :ghissue:`8818`: plt.plot() does not support structured arrays as data= kwarg +* :ghissue:`10533`: Recognize pandas Timestamp objects for DateConverter? +* :ghissue:`8358`: Minor ticks on log-scale colorbar are not cleared +* :ghissue:`10075`: RectangleSelector does not work if start and end points are identical +* :ghissue:`8576`: support 'markevery' in prop_cycle +* :ghissue:`8874`: Crash in python setup.py test +* :ghissue:`3871`: replace use of _tkcanvas with get_tk_widget() +* :ghissue:`10550`: Use long color names for rc defaultParams +* :ghissue:`10722`: Duplicated test name in test_constrainedlayout +* :ghissue:`10419`: svg backend does not respect alpha channel of text *when passed as rgba* +* :ghissue:`10769`: DOC: set_major_locator could check that its getting a Locator (was EngFormatter broken?) +* :ghissue:`10719`: Need better type error checking for linewidth in ax.grid +* :ghissue:`7776`: tex cache lockfile retries should be configurable +* :ghissue:`10556`: Special conversions of xrange() +* :ghissue:`10501`: cmp() is an undefined name in Python 3 +* :ghissue:`9812`: figure_enter_event generates base Event and not LocationEvent +* :ghissue:`10602`: Random image failures with test_curvelinear4 +* :ghissue:`7795`: Incorrect uses of is_numlike diff --git a/doc/users/prev_whats_new/github_stats_3.0.1.rst b/doc/users/prev_whats_new/github_stats_3.0.1.rst new file mode 100644 index 000000000000..95e899d1a9de --- /dev/null +++ b/doc/users/prev_whats_new/github_stats_3.0.1.rst @@ -0,0 +1,203 @@ +.. _github-stats-3-0-1: + +GitHub statistics for 3.0.1 (Oct 25, 2018) +========================================== + +GitHub statistics for 2018/09/18 (tag: v3.0.0) - 2018/10/25 + +These lists are automatically generated, and may be incomplete or contain duplicates. + +We closed 31 issues and merged 127 pull requests. +The full list can be seen `on GitHub `__ + +The following 23 authors contributed 227 commits. + +* Abhinuv Nitin Pitale +* Antony Lee +* Anubhav Shrimal +* Ben Root +* Colin +* Daniele Nicolodi +* David Haberthür +* David Stansby +* Elan Ernest +* Elliott Sales de Andrade +* Eric Firing +* ImportanceOfBeingErnest +* Jody Klymak +* Kai Muehlbauer +* Kevin Rose +* Marcel Martin +* MeeseeksMachine +* Nelle Varoquaux +* Nikita Kniazev +* Ryan May +* teresy +* Thomas A Caswell +* Tim Hoffmann + +GitHub issues and pull requests: + +Pull Requests (127): + +* :ghpull:`12595`: Backport PR #12569 on branch v3.0.x (Don't confuse uintptr_t and Py_ssize_t.) +* :ghpull:`12623`: Backport PR #12285 on branch v3.0.x (FIX: Don't apply tight_layout if axes collapse) +* :ghpull:`12285`: FIX: Don't apply tight_layout if axes collapse +* :ghpull:`12622`: FIX: flake8errors 3.0.x mergeup +* :ghpull:`12619`: Backport PR #12548 on branch v3.0.x (undef _XOPEN_SOURCE breaks the build in AIX) +* :ghpull:`12621`: Backport PR #12607 on branch v3.0.x (STY: fix whitespace and escaping) +* :ghpull:`12616`: Backport PR #12615 on branch v3.0.x (Fix travis OSX build) +* :ghpull:`12594`: Backport PR #12572 on branch v3.0.x (Fix singleton hist labels) +* :ghpull:`12615`: Fix travis OSX build +* :ghpull:`12607`: STY: fix whitespace and escaping +* :ghpull:`12605`: Backport PR #12603 on branch v3.0.x (FIX: don't import macosx to check if eventloop running) +* :ghpull:`12604`: FIX: over-ride 'copy' on RcParams +* :ghpull:`12603`: FIX: don't import macosx to check if eventloop running +* :ghpull:`12602`: Backport PR #12599 on branch v3.0.x (Fix formatting of docstring) +* :ghpull:`12599`: Fix formatting of docstring +* :ghpull:`12593`: Backport PR #12581 on branch v3.0.x (Fix hist() error message) +* :ghpull:`12569`: Don't confuse uintptr_t and Py_ssize_t. +* :ghpull:`12572`: Fix singleton hist labels +* :ghpull:`12581`: Fix hist() error message +* :ghpull:`12575`: Backport PR #12573 on branch v3.0.x (BUG: mplot3d: Don't crash if azim or elev are non-integral) +* :ghpull:`12558`: Backport PR #12555 on branch v3.0.x (Clarify horizontalalignment and verticalalignment in suptitle) +* :ghpull:`12544`: Backport PR #12159 on branch v3.0.x (FIX: colorbar re-check norm before draw for autolabels) +* :ghpull:`12159`: FIX: colorbar re-check norm before draw for autolabels +* :ghpull:`12540`: Backport PR #12501 on branch v3.0.x (Rectified plot error) +* :ghpull:`12531`: Backport PR #12431 on branch v3.0.x (FIX: allow single-string color for scatter) +* :ghpull:`12431`: FIX: allow single-string color for scatter +* :ghpull:`12529`: Backport PR #12216 on branch v3.0.x (Doc: Fix search for sphinx >=1.8) +* :ghpull:`12527`: Backport PR #12461 on branch v3.0.x (FIX: make add_lines work with new colorbar) +* :ghpull:`12461`: FIX: make add_lines work with new colorbar +* :ghpull:`12522`: Backport PR #12241 on branch v3.0.x (FIX: make unused spines invisible) +* :ghpull:`12241`: FIX: make unused spines invisible +* :ghpull:`12519`: Backport PR #12504 on branch v3.0.x (DOC: clarify min supported version wording) +* :ghpull:`12517`: Backport PR #12507 on branch v3.0.x (FIX: make minor ticks formatted with science formatter as well) +* :ghpull:`12507`: FIX: make minor ticks formatted with science formatter as well +* :ghpull:`12512`: Backport PR #12363 on branch v3.0.x +* :ghpull:`12511`: Backport PR #12366 on branch v2.2.x (TST: Update test images for new Ghostscript.) +* :ghpull:`12509`: Backport PR #12478 on branch v3.0.x (MAINT: numpy deprecates asscalar in 1.16) +* :ghpull:`12363`: FIX: errors in get_position changes +* :ghpull:`12497`: Backport PR #12495 on branch v3.0.x (Fix duplicate condition in pathpatch3d example) +* :ghpull:`12490`: Backport PR #12489 on branch v3.0.x (Fix typo in documentation of ylim) +* :ghpull:`12485`: Fix font_manager.OSXInstalledFonts() +* :ghpull:`12484`: Backport PR #12448 on branch v3.0.x (Don't error if some font directories are not readable.) +* :ghpull:`12421`: Backport PR #12360 on branch v3.0.x (Replace axes_grid by axes_grid1 in test) +* :ghpull:`12448`: Don't error if some font directories are not readable. +* :ghpull:`12471`: Backport PR #12468 on branch v3.0.x (Fix ``set_ylim`` unit handling) +* :ghpull:`12475`: Backport PR #12469 on branch v3.0.x (Clarify documentation of offsetbox.AnchoredText's prop kw argument) +* :ghpull:`12468`: Fix ``set_ylim`` unit handling +* :ghpull:`12464`: Backport PR #12457 on branch v3.0.x (Fix tutorial typos.) +* :ghpull:`12432`: Backport PR #12277: FIX: datetime64 now recognized if in a list +* :ghpull:`12277`: FIX: datetime64 now recognized if in a list +* :ghpull:`12426`: Backport PR #12293 on branch v3.0.x (Make pyplot more tolerant wrt. 3rd-party subclasses.) +* :ghpull:`12293`: Make pyplot more tolerant wrt. 3rd-party subclasses. +* :ghpull:`12360`: Replace axes_grid by axes_grid1 in test +* :ghpull:`12412`: Backport PR #12394 on branch v3.0.x (DOC: fix CL tutorial to give same output from saved file and example) +* :ghpull:`12410`: Backport PR #12408 on branch v3.0.x (Don't crash on invalid registry font entries on Windows.) +* :ghpull:`12411`: Backport PR #12366 on branch v3.0.0-doc (TST: Update test images for new Ghostscript.) +* :ghpull:`12408`: Don't crash on invalid registry font entries on Windows. +* :ghpull:`12403`: Backport PR #12149 on branch v3.0.x (Mathtext tutorial fixes) +* :ghpull:`12400`: Backport PR #12257 on branch v3.0.x (Document standard backends in matplotlib.use()) +* :ghpull:`12257`: Document standard backends in matplotlib.use() +* :ghpull:`12399`: Backport PR #12383 on branch v3.0.x (Revert change of parameter name in annotate()) +* :ghpull:`12383`: Revert change of parameter name in annotate() +* :ghpull:`12390`: Backport PR #12385 on branch v3.0.x (CI: Added Appveyor Python 3.7 build) +* :ghpull:`12385`: CI: Added Appveyor Python 3.7 build +* :ghpull:`12381`: Backport PR #12353 on branch v3.0.x (Doc: clarify default parameters in scatter docs) +* :ghpull:`12378`: Backport PR #12366 on branch v3.0.x (TST: Update test images for new Ghostscript.) +* :ghpull:`12375`: Backport PR #11648 on branch v3.0.x (FIX: colorbar placement in constrained layout) +* :ghpull:`11648`: FIX: colorbar placement in constrained layout +* :ghpull:`12350`: Backport PR #12214 on branch v3.0.x +* :ghpull:`12348`: Backport PR #12347 on branch v3.0.x (DOC: add_child_axes to axes_api.rst) +* :ghpull:`12214`: Improve docstring of Annotation +* :ghpull:`12344`: Backport PR #12321 on branch v3.0.x (maint: setupext.py for freetype had a Catch case for missing ft2build.h) +* :ghpull:`12342`: Backport PR #12334 on branch v3.0.x (Improve selection of inset indicator connectors.) +* :ghpull:`12334`: Improve selection of inset indicator connectors. +* :ghpull:`12339`: Backport PR #12297 on branch v3.0.x (Remove some pytest parameterising warnings) +* :ghpull:`12338`: Backport PR #12268 on branch v3.0.x (FIX: remove unnecessary ``self`` in ``super_``-calls, fixes #12265) +* :ghpull:`12336`: Backport PR #12212 on branch v3.0.x (font_manager: Fixed problems with Path(...).suffix) +* :ghpull:`12268`: FIX: remove unnecessary ``self`` in ``super_``-calls, fixes #12265 +* :ghpull:`12212`: font_manager: Fixed problems with Path(...).suffix +* :ghpull:`12331`: Backport PR #12322 on branch v3.0.x (Fix the docs build.) +* :ghpull:`12327`: Backport PR #12326 on branch v3.0.x (fixed minor spelling error in docstring) +* :ghpull:`12320`: Backport PR #12319 on branch v3.0.x (Fix Travis 3.6 builds) +* :ghpull:`12315`: Backport PR #12313 on branch v3.0.x (BUG: Fix typo in view_limits() for MultipleLocator) +* :ghpull:`12313`: BUG: Fix typo in view_limits() for MultipleLocator +* :ghpull:`12305`: Backport PR #12274 on branch v3.0.x (MNT: put back ``_hold`` as read-only attribute on AxesBase) +* :ghpull:`12274`: MNT: put back ``_hold`` as read-only attribute on AxesBase +* :ghpull:`12303`: Backport PR #12163 on branch v3.0.x (TST: Defer loading Qt framework until test is run.) +* :ghpull:`12299`: Backport PR #12294 on branch v3.0.x (Fix expand_dims warnings in triinterpolate) +* :ghpull:`12163`: TST: Defer loading Qt framework until test is run. +* :ghpull:`12301`: Ghostscript 9.0 requirement revisited +* :ghpull:`12294`: Fix expand_dims warnings in triinterpolate +* :ghpull:`12297`: Remove some pytest parameterising warnings +* :ghpull:`12295`: Backport PR #12261 on branch v3.0.x (FIX: parasite axis2 demo) +* :ghpull:`12289`: Backport PR #12278 on branch v3.0.x (Document inheriting docstrings) +* :ghpull:`12287`: Backport PR #12262 on branch v3.0.x (Simplify empty-rasterized pdf test.) +* :ghpull:`12280`: Backport PR #12269 on branch v3.0.x (Add some param docs to BlockingInput methods) +* :ghpull:`12266`: Backport PR #12254 on branch v3.0.x (Improve docstrings of Animations) +* :ghpull:`12262`: Simplify empty-rasterized pdf test. +* :ghpull:`12254`: Improve docstrings of Animations +* :ghpull:`12263`: Backport PR #12258 on branch v3.0.x (Fix CSS for module-level data) +* :ghpull:`12250`: Backport PR #12209 on branch v3.0.x (Doc: Sort named colors example by palette) +* :ghpull:`12248`: Backport PR #12237 on branch v3.0.x (Use (float, float) as parameter type for 2D positions in docstrings) +* :ghpull:`12240`: Backport PR #12236 on branch v3.0.x +* :ghpull:`12237`: Use (float, float) as parameter type for 2D positions in docstrings +* :ghpull:`12242`: Backport PR #12238 on branch v3.0.x (Typo in docs) +* :ghpull:`12236`: Make boilerplate-generated pyplot.py flake8 compliant +* :ghpull:`12234`: Backport PR #12228 on branch v3.0.x (Fix trivial typo in docs.) +* :ghpull:`12230`: Backport PR #12213 on branch v3.0.x (Change win32InstalledFonts return value) +* :ghpull:`12213`: Change win32InstalledFonts return value +* :ghpull:`12223`: Backport PR #11688 on branch v3.0.x (Don't draw axis (spines, ticks, labels) twice when using parasite axes.) +* :ghpull:`12224`: Backport PR #12207 on branch v3.0.x (FIX: dont' check for interactive framework if none required) +* :ghpull:`12207`: FIX: don't check for interactive framework if none required +* :ghpull:`11688`: Don't draw axis (spines, ticks, labels) twice when using parasite axes. +* :ghpull:`12205`: Backport PR #12186 on branch v3.0.x (DOC: fix API note about get_tightbbox) +* :ghpull:`12204`: Backport PR #12203 on branch v3.0.x (Document legend best slowness) +* :ghpull:`12203`: Document legend's slowness when "best" location is used +* :ghpull:`12194`: Backport PR #12164 on branch v3.0.x (Fix Annotation.contains.) +* :ghpull:`12193`: Backport PR #12177 on branch v3.0.x (FIX: remove cwd from mac font path search) +* :ghpull:`12164`: Fix Annotation.contains. +* :ghpull:`12177`: FIX: remove cwd from mac font path search +* :ghpull:`12185`: Backport PR #12183 on branch v3.0.x (Doc: Don't use Sphinx 1.8) +* :ghpull:`12183`: Doc: Don't use Sphinx 1.8 +* :ghpull:`12172`: Backport PR #12157 on branch v3.0.x (Properly declare the interactive framework for the qt4foo backends.) +* :ghpull:`12167`: Backport PR #12166 on branch v3.0.x (Document preference order for backend auto selection) +* :ghpull:`12166`: Document preference order for backend auto selection +* :ghpull:`12157`: Properly declare the interactive framework for the qt4foo backends. +* :ghpull:`12153`: Backport PR #12148 on branch v3.0.x (BLD: pragmatic fix for building basic_unit example on py37) + +Issues (31): + +* :ghissue:`12626`: AttributeError: module 'matplotlib' has no attribute 'artist' +* :ghissue:`12613`: transiently linked interactivity of unshared pair of axes generated with make_axes_locatable +* :ghissue:`12601`: Can't import matplotlib +* :ghissue:`12580`: Incorrect hist error message with bad color size +* :ghissue:`12567`: Calling pyplot.show() with TkAgg backend on x86 machine raises OverflowError. +* :ghissue:`12556`: Matplotlib 3.0.0 import hangs in clean environment +* :ghissue:`12550`: colorbar resizes in animation +* :ghissue:`12155`: Incorrect placement of Colorbar ticks using LogNorm +* :ghissue:`12438`: Scatter doesn't accept a list of strings as color spec. +* :ghissue:`12429`: scatter() does not accept gray strings anymore +* :ghissue:`12458`: add_lines misses lines for matplotlib.colorbar.ColorbarBase +* :ghissue:`12239`: 3d axes are collapsed by tight_layout +* :ghissue:`12488`: inconsistent colorbar tick labels for LogNorm +* :ghissue:`12515`: pyplot.step broken in 3.0.0? +* :ghissue:`12355`: Error for bbox_inches='tight' in savefig with make_axes_locatable +* :ghissue:`12505`: ImageGrid in 3.0 +* :ghissue:`12291`: Importing pyplot crashes on macOS due to missing fontlist-v300.json and then Permission denied: '/opt/local/share/fonts' +* :ghissue:`12288`: New function signatures in pyplot break Cartopy +* :ghissue:`12445`: Error on colorbar +* :ghissue:`12446`: Polar Contour - float() argument must be a string or a number, not 'AxesParasiteParasiteAuxTrans' +* :ghissue:`12271`: error with errorbar with datetime64 +* :ghissue:`12405`: plt.stackplot() does not work with 3.0.0 +* :ghissue:`12406`: Bug with font finding, and here is my fix as well. +* :ghissue:`12325`: Annotation change from "s" to "text" in 3.0- documentation +* :ghissue:`11641`: constrained_layout and colorbar for a subset of axes +* :ghissue:`12352`: TeX rendering broken on master with windows +* :ghissue:`12354`: Too many levels of symbolic links +* :ghissue:`12265`: ParasiteAxesAuxTrans pcolor/pcolormesh and contour/contourf broken +* :ghissue:`12173`: Cannot import pyplot +* :ghissue:`12120`: Default legend behavior (loc='best') very slow for large amounts of data. +* :ghissue:`12176`: import pyplot on MacOS without font cache will search entire subtree of current dir diff --git a/doc/users/prev_whats_new/github_stats_3.0.2.rst b/doc/users/prev_whats_new/github_stats_3.0.2.rst index edafac67e192..395f87acd534 100644 --- a/doc/users/prev_whats_new/github_stats_3.0.2.rst +++ b/doc/users/prev_whats_new/github_stats_3.0.2.rst @@ -1,13 +1,14 @@ .. _github-stats-3-0-2: -GitHub Stats for Matplotlib 3.0.2 -================================= +GitHub statistics for 3.0.2 (Nov 10, 2018) +========================================== -GitHub stats for 2018/09/18 - 2018/11/09 (tag: v3.0.0) +GitHub statistics for 2018/09/18 (tag: v3.0.0) - 2018/11/10 These lists are automatically generated, and may be incomplete or contain duplicates. We closed 170 issues and merged 224 pull requests. +The full list can be seen `on GitHub `__ The following 49 authors contributed 460 commits. diff --git a/doc/users/prev_whats_new/github_stats_3.0.3.rst b/doc/users/prev_whats_new/github_stats_3.0.3.rst new file mode 100644 index 000000000000..5c1271e52e4f --- /dev/null +++ b/doc/users/prev_whats_new/github_stats_3.0.3.rst @@ -0,0 +1,147 @@ +.. _github-stats-3-0-3: + +GitHub statistics for 3.0.3 (Feb 28, 2019) +========================================== + +GitHub statistics for 2018/11/10 (tag: v3.0.2) - 2019/02/28 + +These lists are automatically generated, and may be incomplete or contain duplicates. + +We closed 14 issues and merged 92 pull requests. +The full list can be seen `on GitHub `__ + +The following 19 authors contributed 157 commits. + +* Antony Lee +* Christer Jensen +* Christoph Gohlke +* David Stansby +* Elan Ernest +* Elliott Sales de Andrade +* ImportanceOfBeingErnest +* James Adams +* Jody Klymak +* Johannes H. Jensen +* Matthias Geier +* MeeseeksMachine +* Molly Rossow +* Nelle Varoquaux +* Paul Ivanov +* Pierre Thibault +* Thomas A Caswell +* Tim Hoffmann +* Tobia De Koninck + +GitHub issues and pull requests: + +Pull Requests (92): + +* :ghpull:`13493`: V3.0.3 prep +* :ghpull:`13491`: V3.0.x pi +* :ghpull:`13460`: Backport PR #13455 on branch v3.0.x (BLD: only try to get freetype src if src does not exist) +* :ghpull:`13461`: Backport PR #13426 on branch v3.0.x (FIX: bbox_inches='tight' with only non-finite bounding boxes) +* :ghpull:`13426`: FIX: bbox_inches='tight' with only non-finite bounding boxes +* :ghpull:`13453`: Backport PR #13451 on branch v3.0.x (MNT: fix logic error where we never try the second freetype URL) +* :ghpull:`13451`: MNT: fix logic error where we never try the second freetype URL +* :ghpull:`13446`: Merge pull request #11246 from anntzer/download-jquery +* :ghpull:`13437`: Backport PR #13436 on branch v3.0.x (Add get/set_in_layout to artist API docs.) +* :ghpull:`13436`: Add get/set_in_layout to artist API docs. +* :ghpull:`13432`: Really fix ArtistInspector.get_aliases +* :ghpull:`13416`: Backport PR #13405 on branch v3.0.x (Fix imshow()ing PIL-opened images.) +* :ghpull:`13418`: Backport PR #13412 and #13337 on branch v3.0.x +* :ghpull:`13412`: CI: add additional qt5 deb package on travis +* :ghpull:`13370`: Backport PR #13367 on branch v3.0.x (DOC: fix note of what version hold was deprecated in (2.0 not 2.1)) +* :ghpull:`13366`: Backport PR #13365 on branch v3.0.x (Fix gcc warning) +* :ghpull:`13365`: Fix gcc warning +* :ghpull:`13347`: Backport PR #13289 on branch v3.0.x (Fix unhandled C++ exception) +* :ghpull:`13349`: Backport PR #13234 on branch v3.0.x +* :ghpull:`13281`: MAINT install of pinned vers for travis +* :ghpull:`13289`: Fix unhandled C++ exception +* :ghpull:`13345`: Backport PR #13333 on branch v3.0.x (Fix possible leak of return of PySequence_GetItem.) +* :ghpull:`13333`: Fix possible leak of return of PySequence_GetItem. +* :ghpull:`13337`: Bump to flake8 3.7. +* :ghpull:`13340`: Backport PR #12398 on branch v3.0.x (CI: Don't run AppVeyor/Travis for doc backport branches.) +* :ghpull:`13317`: Backport PR #13316 on branch v3.0.x (Put correct version in constrained layout tutorial) +* :ghpull:`13308`: Backport PR #12678 on branch v3.0.x +* :ghpull:`12678`: FIX: properly set tz for YearLocator +* :ghpull:`13291`: Backport PR #13287 on branch v3.0.x (Fix unsafe use of NULL pointer) +* :ghpull:`13290`: Backport PR #13288 on branch v3.0.x (Fix potential memory leak) +* :ghpull:`13287`: Fix unsafe use of NULL pointer +* :ghpull:`13288`: Fix potential memory leak +* :ghpull:`13273`: Backport PR #13272 on branch v3.0.x (DOC Better description of inset locator and colorbar) +* :ghpull:`12812`: Backport PR #12809 on branch v3.0.x (Fix TypeError when calculating tick_values) +* :ghpull:`13245`: Backport PR #13244 on branch v3.0.x (Fix typo) +* :ghpull:`13176`: Backport PR #13047 on branch v3.0.x (Improve docs on contourf extend) +* :ghpull:`13215`: Backport PR #13212 on branch v3.0.x (Updated the docstring for pyplot.figure to list floats as the type for figsize argument) +* :ghpull:`13158`: Backport PR #13150 on branch v3.0.x (Remove unused add_dicts from example.) +* :ghpull:`13157`: Backport PR #13152 on branch v3.0.x (DOC: Add explanatory comment for colorbar with axes divider example) +* :ghpull:`13221`: Backport PR #13194 on branch v3.0.x (TST: Fix incorrect call to pytest.raises.) +* :ghpull:`13230`: Backport PR #13226 on branch v3.0.x (Avoid triggering warnings in mandelbrot example.) +* :ghpull:`13216`: Backport #13205 on branch v3.0.x (Add xvfb service to travis) +* :ghpull:`13194`: TST: Fix incorrect call to pytest.raises. +* :ghpull:`13212`: Updated the docstring for pyplot.figure to list floats as the type for figsize argument +* :ghpull:`13205`: Add xvfb service to travis +* :ghpull:`13204`: Add xvfb service to travis +* :ghpull:`13175`: Backport PR #13015 on branch v3.0.x (Enable local doc building without git installation) +* :ghpull:`13047`: Improve docs on contourf extend +* :ghpull:`13015`: Enable local doc building without git installation +* :ghpull:`13159`: Revert "Pin pytest to <3.8 (for 3.0.x)" +* :ghpull:`13150`: Remove unused add_dicts from example. +* :ghpull:`13152`: DOC: Add explanatory comment for colorbar with axes divider example +* :ghpull:`13085`: Backport PR #13081 on branch v3.0.x (DOC: forbid a buggy version of pillow for building docs) +* :ghpull:`13082`: Backport PR #13080 on branch v3.0.x (Pin pillow to < 5.4 to fix doc build) +* :ghpull:`13054`: Backport PR #13052 on branch v3.0.x (Small bug fix in image_slices_viewer) +* :ghpull:`13052`: Small bug fix in image_slices_viewer +* :ghpull:`13036`: Backport PR #12949 on branch v3.0.x (Update docstring of Axes3d.scatter) +* :ghpull:`12949`: Update docstring of Axes3d.scatter +* :ghpull:`13004`: Backport PR #13001: Update windows build instructions +* :ghpull:`13011`: Backport PR #13006 on branch v3.0.x (Add category module to docs) +* :ghpull:`13009`: Fix dependencies for travis build with python 3.5 +* :ghpull:`13006`: Add category module to docs +* :ghpull:`13001`: Update windows build instructions +* :ghpull:`12996`: Fix return type in 3D scatter docs +* :ghpull:`12972`: Backport PR #12929 on branch v3.0.x (FIX: skip gtk backend if gobject but not pygtk is installed) +* :ghpull:`12596`: Use sudo:true for nightly builds. +* :ghpull:`12929`: FIX: skip gtk backend if gobject but not pygtk is installed +* :ghpull:`12965`: Backport PR #12960 on branch v3.0.x (Remove animated=True from animation docs) +* :ghpull:`12964`: Backport PR #12938 on branch v3.0.x (Fix xtick.minor.visible only acting on the xaxis) +* :ghpull:`12938`: Fix xtick.minor.visible only acting on the xaxis +* :ghpull:`12937`: Backport PR #12914 on branch 3.0.x: Fix numpydoc formatting +* :ghpull:`12914`: Fix numpydoc formatting +* :ghpull:`12923`: Backport PR #12921 on branch v3.0.x (Fix documentation of vert parameter of Axes.bxp) +* :ghpull:`12921`: Fix documentation of vert parameter of Axes.bxp +* :ghpull:`12912`: Backport PR #12878 on branch v3.0.2-doc (Pin pytest to <3.8 (for 3.0.x)) +* :ghpull:`12906`: Backport PR #12774 on branch v3.0.x +* :ghpull:`12774`: Cairo backend: Fix alpha render of collections +* :ghpull:`12854`: Backport PR #12835 on branch v3.0.x (Don't fail tests if cairo dependency is not installed.) +* :ghpull:`12896`: Backport PR #12848 on branch v3.0.x (Fix spelling of the name Randall Munroe) +* :ghpull:`12894`: Backport PR #12890 on branch v3.0.x (Restrict postscript title to ascii.) +* :ghpull:`12838`: Backport PR #12795 on branch v3.0.x (Fix Bezier degree elevation formula in backend_cairo.) +* :ghpull:`12843`: Backport PR #12824 on branch v3.0.x +* :ghpull:`12890`: Restrict postscript title to ascii. +* :ghpull:`12878`: Pin pytest to <3.8 (for 3.0.x) +* :ghpull:`12870`: Backport PR #12869 on branch v3.0.x (Fix latin-1-ization of Title in eps.) +* :ghpull:`12869`: Fix latin-1-ization of Title in eps. +* :ghpull:`12835`: Don't fail tests if cairo dependency is not installed. +* :ghpull:`12848`: Fix spelling of the name Randall Munroe +* :ghpull:`12795`: Fix Bezier degree elevation formula in backend_cairo. +* :ghpull:`12824`: Add missing datestr2num to docs +* :ghpull:`12791`: Backport PR #12790 on branch v3.0.x (Remove ticks and titles from tight bbox tests.) +* :ghpull:`12790`: Remove ticks and titles from tight bbox tests. + +Issues (14): + +* :ghissue:`10360`: creating PathCollection proxy artist with %matplotlib inline raises ValueError: cannot convert float NaN to integer +* :ghissue:`13276`: calling annotate with nan values for the position still gives error after 3.0.2 +* :ghissue:`13450`: Issues with jquery download caching +* :ghissue:`13223`: label1On set to true when axis.tick_params(axis='both', which='major', length=5) +* :ghissue:`13311`: docs unclear on status of constraint layout +* :ghissue:`12675`: Off-by-one bug in annual axis labels when localized time crosses year boundary +* :ghissue:`13208`: Wrong argument type for figsize in documentation for figure +* :ghissue:`13201`: test_backend_qt tests failing +* :ghissue:`13013`: v3.0.2 local html docs "git describe" error +* :ghissue:`13051`: Missing self in image_slices_viewer +* :ghissue:`12920`: Incorrect return type in mplot3d documentation +* :ghissue:`12907`: Tiny typo in documentation of matplotlib.figure.Figure.colorbar +* :ghissue:`12892`: GTK3Cairo Backend Legend TypeError +* :ghissue:`12815`: DOC: matplotlib.dates datestr2num function documentation is missing diff --git a/doc/users/prev_whats_new/github_stats_3.1.0.rst b/doc/users/prev_whats_new/github_stats_3.1.0.rst index 704de6d09932..97bee1af56b8 100644 --- a/doc/users/prev_whats_new/github_stats_3.1.0.rst +++ b/doc/users/prev_whats_new/github_stats_3.1.0.rst @@ -1,14 +1,14 @@ .. _github-stats-3-1-0: -GitHub Stats for Matplotlib 3.1.0 -================================= +GitHub statistics for 3.1.0 (May 18, 2019) +========================================== -GitHub stats for 2018/09/18 - 2019/05/13 (tag: v3.0.0) +GitHub statistics for 2018/09/18 (tag: v3.0.0) - 2019/05/18 These lists are automatically generated, and may be incomplete or contain duplicates. We closed 161 issues and merged 918 pull requests. -The full list can be seen `on GitHub `__ +The full list can be seen `on GitHub `__ The following 150 authors contributed 3426 commits. diff --git a/doc/users/prev_whats_new/github_stats_3.1.1.rst b/doc/users/prev_whats_new/github_stats_3.1.1.rst index 5de8ba84ade8..3e552c371c55 100644 --- a/doc/users/prev_whats_new/github_stats_3.1.1.rst +++ b/doc/users/prev_whats_new/github_stats_3.1.1.rst @@ -1,9 +1,9 @@ .. _github-stats-3-1-1: -GitHub Stats for Matplotlib 3.1.1 -================================= +GitHub statistics for 3.1.1 (Jul 02, 2019) +========================================== -GitHub stats for 2019/05/18 - 2019/06/30 (tag: v3.1.0) +GitHub statistics for 2019/05/18 (tag: v3.1.0) - 2019/07/02 These lists are automatically generated, and may be incomplete or contain duplicates. diff --git a/doc/users/prev_whats_new/github_stats_3.1.2.rst b/doc/users/prev_whats_new/github_stats_3.1.2.rst index 9f11f34cb78a..e1ed84e26372 100644 --- a/doc/users/prev_whats_new/github_stats_3.1.2.rst +++ b/doc/users/prev_whats_new/github_stats_3.1.2.rst @@ -1,202 +1,186 @@ .. _github-stats-3-1-2: -GitHub Stats for Matplotlib 3.1.2 -================================= +GitHub statistics for 3.1.2 (Nov 21, 2019) +========================================== -GitHub stats for 2019/05/18 - 2019/06/30 (tag: v3.1.0) +GitHub statistics for 2019/07/01 (tag: v3.1.1) - 2019/11/21 These lists are automatically generated, and may be incomplete or contain duplicates. -We closed 30 issues and merged 120 pulnl requests. -The full list can be seen `on GitHub `__ +We closed 28 issues and merged 113 pull requests. +The full list can be seen `on GitHub `__ -The following 30 authors contributed 323 commits. +The following 23 authors contributed 192 commits. -* Adam Gomaa +* Alex Rudy * Antony Lee -* Ben Root -* Christer Jensen -* chuanzhu xu +* Bingyao Liu +* Cong Ma * David Stansby -* Deng Tian -* djdt -* Dora Fraeman Caswell -* Elan Ernest * Elliott Sales de Andrade -* Eric Firing -* Filipe Fernandes -* Ian Thomas +* hannah +* Hanno Rein * ImportanceOfBeingErnest +* joaonsg * Jody Klymak -* Johannes H. Jensen -* Jonas Camillus Jeppesen -* LeiSurrre -* Matt Adamson +* Matthias Bussonnier * MeeseeksMachine -* Molly Rossow -* Nathan Goldbaum +* miquelastein * Nelle Varoquaux +* Patrick Shriwise +* Paul Hoffman * Paul Ivanov -* RoryIAngus * Ryan May +* Samesh * Thomas A Caswell -* Thomas Robitaille * Tim Hoffmann +* Vincent L.M. Mazoyer GitHub issues and pull requests: -Pull Requests (120): +Pull Requests (113): -* :ghpull:`14636`: Don't capture stderr in _check_and_log_subprocess. -* :ghpull:`14655`: Backport PR #14649 on branch v3.1.x (Fix appveyor conda py37) -* :ghpull:`14649`: Fix appveyor conda py37 -* :ghpull:`14646`: Backport PR #14640 on branch v3.1.x (FIX: allow secondary axes to be non-linear) -* :ghpull:`14640`: FIX: allow secondary axes to be non-linear -* :ghpull:`14643`: Second attempt at fixing axis inversion (for mpl3.1). -* :ghpull:`14623`: Fix axis inversion with loglocator and logitlocator. -* :ghpull:`14619`: Backport PR #14598 on branch v3.1.x (Fix inversion of shared axes.) -* :ghpull:`14621`: Backport PR #14613 on branch v3.1.x (Cleanup DateFormatter docstring.) -* :ghpull:`14622`: Backport PR #14611 on branch v3.1.x (Update some axis docstrings.) -* :ghpull:`14611`: Update some axis docstrings. -* :ghpull:`14613`: Cleanup DateFormatter docstring. -* :ghpull:`14598`: Fix inversion of shared axes. -* :ghpull:`14610`: Backport PR #14579 on branch v3.1.x (Fix inversion of 3d axis.) -* :ghpull:`14579`: Fix inversion of 3d axis. -* :ghpull:`14600`: Backport PR #14599 on branch v3.1.x (DOC: Add numpngw to third party packages.) -* :ghpull:`14574`: Backport PR #14568 on branch v3.1.x (Don't assume tk canvas have a manager attached.) -* :ghpull:`14568`: Don't assume tk canvas have a manager attached. -* :ghpull:`14571`: Backport PR #14566 on branch v3.1.x (Move setting of AA_EnableHighDpiScaling before creating QApplication.) -* :ghpull:`14566`: Move setting of AA_EnableHighDpiScaling before creating QApplication. -* :ghpull:`14541`: Backport PR #14535 on branch v3.1.x (Invalidate FT2Font cache when fork()ing.) -* :ghpull:`14535`: Invalidate FT2Font cache when fork()ing. -* :ghpull:`14522`: Backport PR #14040 on branch v3.1.x (Gracefully handle non-finite z in tricontour (issue #10167)) -* :ghpull:`14434`: Backport PR #14296 on branch v3.1.x (Fix barbs to accept array of bool for ``flip_barb``) -* :ghpull:`14518`: Backport PR #14509 on branch v3.1.x (Fix too large icon spacing in Qt5 on non-HiDPI screens) -* :ghpull:`14509`: Fix too large icon spacing in Qt5 on non-HiDPI screens -* :ghpull:`14514`: Backport PR #14256 on branch v3.1.x (Improve docstring of Axes.barbs) -* :ghpull:`14256`: Improve docstring of Axes.barbs -* :ghpull:`14505`: Backport PR #14395 on branch v3.1.x (MAINT: work around non-zero exit status of "pdftops -v" command.) -* :ghpull:`14504`: Backport PR #14445 on branch v3.1.x (FIX: fastpath clipped artists) -* :ghpull:`14502`: Backport PR #14451 on branch v3.1.x (FIX: return points rather than path to fix regression) -* :ghpull:`14445`: FIX: fastpath clipped artists -* :ghpull:`14497`: Backport PR #14491 on branch v3.1.x (Fix uses of PyObject_IsTrue.) -* :ghpull:`14491`: Fix uses of PyObject_IsTrue. -* :ghpull:`14492`: Backport PR #14490 on branch v3.1.x (Fix links of parameter types) -* :ghpull:`14490`: Fix links of parameter types -* :ghpull:`14489`: Backport PR #14459 on branch v3.1.x (Cleanup docstring of DraggableBase.) -* :ghpull:`14459`: Cleanup docstring of DraggableBase. -* :ghpull:`14485`: Backport #14429 on v3.1.x -* :ghpull:`14486`: Backport #14403 on v3.1. -* :ghpull:`14429`: FIX: if the first elements of an array are masked keep checking -* :ghpull:`14481`: Backport PR #14475 on branch v3.1.x (change ginoput docstring to match behavior) -* :ghpull:`14482`: Backport PR #14464 on branch v3.1.x (Mention origin and extent tutorial in API docs for origin kwarg) -* :ghpull:`14464`: Mention origin and extent tutorial in API docs for origin kwarg -* :ghpull:`14468`: Backport PR #14449: Improve docs on gridspec -* :ghpull:`14475`: change ginoput docstring to match behavior -* :ghpull:`14477`: Backport PR #14461 on branch v3.1.x (Fix out of bounds read in backend_tk.) -* :ghpull:`14476`: Backport PR #14474 on branch v3.1.x (Fix default value in docstring of errorbar func) -* :ghpull:`14461`: Fix out of bounds read in backend_tk. -* :ghpull:`14474`: Fix default value in docstring of errorbar func -* :ghpull:`14473`: Backport PR #14472 on branch v3.1.x (Fix NameError in example code for setting label via method) -* :ghpull:`14472`: Fix NameError in example code for setting label via method -* :ghpull:`14449`: Improve docs on gridspec -* :ghpull:`14450`: Backport PR #14422 on branch v3.1.x (Fix ReST note in span selector example) -* :ghpull:`14446`: Backport PR #14438 on branch v3.1.x (Issue #14372 - Add degrees to documentation) -* :ghpull:`14438`: Issue #14372 - Add degrees to documentation -* :ghpull:`14437`: Backport PR #14387 on branch v3.1.x (Fix clearing rubberband on nbagg) -* :ghpull:`14387`: Fix clearing rubberband on nbagg -* :ghpull:`14435`: Backport PR #14425 on branch v3.1.x (Lic restore license paint) -* :ghpull:`14296`: Fix barbs to accept array of bool for ``flip_barb`` -* :ghpull:`14430`: Backport PR #14397 on branch v3.1.x (Correctly set clip_path on pcolorfast return artist.) -* :ghpull:`14397`: Correctly set clip_path on pcolorfast return artist. -* :ghpull:`14409`: Backport PR #14335 on branch v3.1.x (Add explanation of animation.embed_limit to matplotlibrc.template) -* :ghpull:`14335`: Add explanation of animation.embed_limit to matplotlibrc.template -* :ghpull:`14403`: Revert "Preserve whitespace in svg output." -* :ghpull:`14407`: Backport PR #14406 on branch v3.1.x (Remove extra \iint in math_symbol_table for document) -* :ghpull:`14398`: Backport PR #14394 on branch v3.1.x (Update link to "MathML torture test".) -* :ghpull:`14394`: Update link to "MathML torture test". -* :ghpull:`14389`: Backport PR #14388 on branch v3.1.x (Fixed one little spelling error) -* :ghpull:`14385`: Backport PR #14316 on branch v3.1.x (Improve error message for kiwisolver import error (DLL load failed)) -* :ghpull:`14388`: Fixed one little spelling error -* :ghpull:`14384`: Backport PR #14369 on branch v3.1.x (Don't use deprecated mathcircled in docs.) -* :ghpull:`14316`: Improve error message for kiwisolver import error (DLL load failed) -* :ghpull:`14369`: Don't use deprecated mathcircled in docs. -* :ghpull:`14375`: Backport PR #14374 on branch v3.1.x (Check that the figure patch is in bbox_artists before trying to remove.) -* :ghpull:`14374`: Check that the figure patch is in bbox_artists before trying to remove. -* :ghpull:`14040`: Gracefully handle non-finite z in tricontour (issue #10167) -* :ghpull:`14342`: Backport PR #14326 on branch v3.1.x (Correctly apply PNG palette when building ImageBase through Pillow.) -* :ghpull:`14326`: Correctly apply PNG palette when building ImageBase through Pillow. -* :ghpull:`14341`: Backport PR #14337 on branch v3.1.x (Docstring cleanup) -* :ghpull:`14337`: Docstring cleanup -* :ghpull:`14325`: Backport PR #14126 on branch v3.1.x (Simplify grouped bar chart example) -* :ghpull:`14324`: Backport PR #14139 on branch v3.1.x (TST: be more explicit about identifying qt4/qt5 imports) -* :ghpull:`14126`: Simplify grouped bar chart example -* :ghpull:`14323`: Backport PR #14290 on branch v3.1.x (Convert SymmetricalLogScale to numpydoc) -* :ghpull:`14139`: TST: be more explicit about identifying qt4/qt5 imports -* :ghpull:`14290`: Convert SymmetricalLogScale to numpydoc -* :ghpull:`14321`: Backport PR #14313 on branch v3.1.x -* :ghpull:`14313`: Support masked array inputs for to_rgba and to_rgba_array. -* :ghpull:`14320`: Backport PR #14319 on branch v3.1.x (Don't set missing history buttons.) -* :ghpull:`14319`: Don't set missing history buttons. -* :ghpull:`14317`: Backport PR #14295: Fix bug in SymmetricalLogTransform. -* :ghpull:`14302`: Backport PR #14255 on branch v3.1.x (Improve docsstring of Axes.streamplot) -* :ghpull:`14255`: Improve docsstring of Axes.streamplot -* :ghpull:`14295`: Fix bug in SymmetricalLogTransform. -* :ghpull:`14294`: Backport PR #14282 on branch v3.1.x (Fix toolmanager's destroy subplots in tk) -* :ghpull:`14282`: Fix toolmanager's destroy subplots in tk -* :ghpull:`14292`: Backport PR #14289 on branch v3.1.x (BUG: Fix performance regression when plotting values from Numpy array sub-classes) -* :ghpull:`14289`: BUG: Fix performance regression when plotting values from Numpy array sub-classes -* :ghpull:`14287`: Backport PR #14286 on branch v3.1.x (fix minor typo) -* :ghpull:`14284`: Backport PR #14279 on branch v3.1.x (In case fallback to Agg fails, let the exception propagate out.) -* :ghpull:`14254`: Merge up 30x -* :ghpull:`14279`: In case fallback to Agg fails, let the exception propagate out. -* :ghpull:`14268`: Backport PR #14261 on branch v3.1.x (Updated polar documentation) -* :ghpull:`14261`: Updated polar documentation -* :ghpull:`14264`: Backport PR #14260 on branch v3.1.x (Remove old OSX FAQ page) -* :ghpull:`14260`: Remove old OSX FAQ page -* :ghpull:`14249`: Backport PR #14243 on branch v3.1.x (Update docstring of makeMappingArray) -* :ghpull:`14250`: Backport PR #14149 on branch v3.1.x -* :ghpull:`14252`: Backport PR #14248 on branch v3.1.x (Fix TextBox not respecting eventson) -* :ghpull:`14253`: Backport PR #13596 on branch v3.1.x (Normalize properties passed to bxp().) -* :ghpull:`14251`: Backport PR #14241 on branch v3.1.x (Fix linear segmented colormap with one element) -* :ghpull:`13596`: Normalize properties passed to bxp(). -* :ghpull:`14248`: Fix TextBox not respecting eventson -* :ghpull:`14241`: Fix linear segmented colormap with one element -* :ghpull:`14243`: Update docstring of makeMappingArray -* :ghpull:`14238`: Backport PR #14164 on branch v3.1.x (Fix regexp for dvipng version detection) -* :ghpull:`14149`: Avoid using ``axis([xlo, xhi, ylo, yhi])`` in examples. -* :ghpull:`14164`: Fix regexp for dvipng version detection -* :ghpull:`13739`: Fix pressing tab breaks keymap in CanvasTk +* :ghpull:`15664`: Backport PR #15649 on branch v3.1.x (Fix searchindex.js loading when ajax fails (because e.g. CORS in embedded iframes)) +* :ghpull:`15722`: Backport PR #15718 on branch v3.1.x (Update donation link) +* :ghpull:`15667`: Backport PR #15654 on branch v3.1.x (Fix some broken links.) +* :ghpull:`15658`: Backport PR #15647 on branch v3.1.x (Update some links) +* :ghpull:`15582`: Backport PR #15512 on branch v3.1.x +* :ghpull:`15512`: FIX: do not consider webagg and nbagg "interactive" for fallback +* :ghpull:`15558`: Backport PR #15553 on branch v3.1.x (DOC: add cache-buster query string to css path) +* :ghpull:`15550`: Backport PR #15528 on branch v3.1.x (Declutter home page) +* :ghpull:`15547`: Backport PR #15516 on branch v3.1.x (Add logo like font) +* :ghpull:`15511`: DOC: fix nav location +* :ghpull:`15508`: Backport PR #15489 on branch v3.1.x (DOC: adding main nav to site) +* :ghpull:`15494`: Backport PR #15486 on branch v3.1.x (Fixes an error in the documentation of Ellipse) +* :ghpull:`15486`: Fixes an error in the documentation of Ellipse +* :ghpull:`15473`: Backport PR #15464 on branch v3.1.x (Remove unused code (remainder from #15453)) +* :ghpull:`15470`: Backport PR #15460 on branch v3.1.x (Fix incorrect value check in axes_grid.) +* :ghpull:`15464`: Remove unused code (remainder from #15453) +* :ghpull:`15455`: Backport PR #15453 on branch v3.1.x (Improve example for tick locators) +* :ghpull:`15453`: Improve example for tick locators +* :ghpull:`15443`: Backport PR #15439 on branch v3.1.x (DOC: mention discourse main page) +* :ghpull:`15424`: Backport PR #15422 on branch v3.1.x (FIX: typo in attribute lookup) +* :ghpull:`15322`: Backport PR #15297 on branch v3.1.x (Document How-to figure empty) +* :ghpull:`15298`: Backport PR #15296 on branch v3.1.x (Fix typo/bug from 18cecf7) +* :ghpull:`15296`: Fix typo/bug from 18cecf7 +* :ghpull:`15278`: Backport PR #15271 on branch v3.1.x (Fix font weight validation) +* :ghpull:`15271`: Fix font weight validation +* :ghpull:`15218`: Backport PR #15217 on branch v3.1.x (Doc: Add ``plt.show()`` to horizontal bar chart example) +* :ghpull:`15207`: Backport PR #15206: FIX: be more forgiving about expecting internal s… +* :ghpull:`15198`: Backport PR #15197 on branch v3.1.x (Remove mention of now-removed basedir setup option.) +* :ghpull:`15197`: Remove mention of now-removed basedir setup option. +* :ghpull:`15189`: Backport PR #14979: FIX: Don't enable IPython integration if not ente… +* :ghpull:`15190`: Backport PR #14683: For non-html output, let sphinx pick the best format +* :ghpull:`15187`: Backport PR #15140 on branch v3.1.x +* :ghpull:`15185`: Backport PR #15168 on branch v3.1.x (MNT: explicitly cast ``np.bool_`` -> bool to prevent deprecation warning) +* :ghpull:`15168`: MNT: explicitly cast ``np.bool_`` -> bool to prevent deprecation warning +* :ghpull:`15183`: Backport PR #15181 on branch v3.1.x (FIX: proper call to zero_formats) +* :ghpull:`15181`: FIX: proper call to zero_formats +* :ghpull:`15172`: Backport PR #15166 on branch v3.1.x +* :ghpull:`15166`: FIX: indexed pandas bar +* :ghpull:`15153`: Backport PR #14456 on branch v3.1.x (PyQT5 Backend Partial Redraw Fix) +* :ghpull:`14456`: PyQT5 Backend Partial Redraw Fix +* :ghpull:`15140`: Fix ScalarFormatter formatting of masked values +* :ghpull:`15135`: Backport PR #15132 on branch v3.1.x (Update documenting guide on rcParams) +* :ghpull:`15128`: Backport PR #15115 on branch v3.1.x (Doc: highlight rcparams) +* :ghpull:`15125`: Backport PR #15110 on branch v3.1.x (Add inheritance diagram to mpl.ticker docs) +* :ghpull:`15116`: Backport PR #15114 on branch v3.1.x (DOC: update language around NF) +* :ghpull:`15058`: Backport PR #15055 on branch v3.1.x (Remove mention of now-removed feature in docstring.) +* :ghpull:`15055`: Remove mention of now-removed feature in docstring. +* :ghpull:`15047`: Backport PR #14919 on branch v3.1.x (FIX constrained_layout w/ hidden axes) +* :ghpull:`14919`: FIX constrained_layout w/ hidden axes +* :ghpull:`15022`: Backport PR #15020 on branch v3.1.x (Let connectionpatch be drawn on figure level) +* :ghpull:`15020`: Let connectionpatch be drawn on figure level +* :ghpull:`15017`: Backport PR #15007 on branch v3.1.x (FIX: support pandas 0.25) +* :ghpull:`14979`: FIX: Don't enable IPython integration if not entering REPL. +* :ghpull:`14987`: Merge pull request #14915 from AWhetter/fix_14585 +* :ghpull:`14985`: Backport PR #14982 on branch v3.1.x (DOC: correct table docstring) +* :ghpull:`14982`: DOC: correct table docstring +* :ghpull:`14975`: Backport PR #14974 on branch v3.1.x (grammar) +* :ghpull:`14972`: Backport PR #14971 on branch v3.1.x (typo) +* :ghpull:`14965`: Fix typo in documentation of table +* :ghpull:`14951`: Backport PR #14934 on branch v3.1.x (DOC: update axes_demo to directly manipulate fig, ax) +* :ghpull:`14938`: Backport PR #14905 on branch v3.1.x (Gracefully handle encoding problems when querying external executables.) +* :ghpull:`14935`: Backport PR #14933 on branch v3.1.x (DOC: typo x2 costum -> custom) +* :ghpull:`14936`: Backport PR #14932 on branch v3.1.x (DOC: Update invert_example to directly manipulate axis.) +* :ghpull:`14905`: Gracefully handle encoding problems when querying external executables. +* :ghpull:`14933`: DOC: typo x2 costum -> custom +* :ghpull:`14910`: Backport PR #14901 on branch v3.1.x (Fix GH14900: numpy 1.17.0 breaks test_colors.) +* :ghpull:`14864`: Backport PR #14830 on branch v3.1.x (FIX: restore special casing of shift-enter in notebook) +* :ghpull:`14861`: Don't use pandas 0.25.0 for testing +* :ghpull:`14855`: Backport PR #14839 on branch v3.1.x +* :ghpull:`14839`: Improve docstring of Axes.hexbin +* :ghpull:`14837`: Backport PR #14757 on branch v3.1.x (Remove incorrect color/cmap docstring line in contour.py) +* :ghpull:`14836`: Backport PR #14764 on branch v3.1.x (DOC: Fixes the links in the see-also section of Axes.get_tightbbox) +* :ghpull:`14818`: Backport PR #14510 on branch v3.1.x (Improve example for fill_between) +* :ghpull:`14819`: Backport PR #14704 on branch v3.1.x (Small patches on Docs (Tutorials and FAQ)) +* :ghpull:`14820`: Backport PR #14765 on branch v3.1.x (DOC: Fix documentation location for patheffects) +* :ghpull:`14821`: Backport PR #14741 on branch v3.1.x (DOC: Update description of properties of Line2D in 'plot' documentation.) +* :ghpull:`14822`: Backport PR #14714 on branch v3.1.x (Point towards how to save output of non-interactive backends) +* :ghpull:`14823`: Backport PR #14784 on branch v3.1.x (Tiny docs/comments cleanups.) +* :ghpull:`14824`: Backport PR #14798 on branch v3.1.x (Cleanup dates.py module docstrings.) +* :ghpull:`14825`: Backport PR #14802 on branch v3.1.x (Fix some broken refs in the docs.) +* :ghpull:`14826`: Backport PR #14806 on branch v3.1.x (Remove unnecessary uses of transFigure from examples.) +* :ghpull:`14827`: Backport PR #14525 on branch v3.1.x (improve documentation of OffsetBox) +* :ghpull:`14828`: Backport PR #14548: Link to matplotlibrc of used version +* :ghpull:`14817`: Backport PR #14697 on branch v3.1.x (Fix NavigationToolbar2QT height) +* :ghpull:`14692`: Backport PR #14688 on branch v3.1.x (Revise the misleading title for subplots demo) +* :ghpull:`14816`: Backport PR #14677 on branch v3.1.x (Don't misclip axis when calling set_ticks on inverted axes.) +* :ghpull:`14815`: Backport PR #14658 on branch v3.1.x (Fix numpydoc formatting) +* :ghpull:`14813`: Backport PR #14488 on branch v3.1.x (Make sure EventCollection doesn't modify input in-place) +* :ghpull:`14806`: Remove unnecessary uses of transFigure from examples. +* :ghpull:`14802`: Fix some broken refs in the docs. +* :ghpull:`14798`: Cleanup dates.py module docstrings. +* :ghpull:`14784`: Tiny docs/comments cleanups. +* :ghpull:`14764`: DOC: Fixes the links in the see-also section of Axes.get_tightbbox +* :ghpull:`14777`: Backport PR #14775 on branch v3.1.x (DOC: Fix CircleCI builds) +* :ghpull:`14769`: Backport PR #14759 on branch v3.1.x (DOC: note about having to rebuild after switching to local freetype) +* :ghpull:`14714`: Point towards how to save output of non-interactive backends +* :ghpull:`14741`: DOC: Update description of properties of Line2D in 'plot' documentation. +* :ghpull:`14771`: Backport PR #14760 on branch v3.1.x (DOC: minor CoC wording change) +* :ghpull:`14765`: DOC: Fix documentation location for patheffects +* :ghpull:`14735`: Backport PR #14734 on branch v3.1.x (Add geoplot to third-party example libraries page.) +* :ghpull:`14711`: Backport PR #14706 on branch v3.1.x (Mention gr backend in docs.) +* :ghpull:`14704`: Small patches on Docs (Tutorials and FAQ) +* :ghpull:`14700`: Backport PR #14698 on branch v3.1.x (Make property name be consistent with rc parameter.) +* :ghpull:`14510`: Improve example for fill_between +* :ghpull:`14683`: For non-html output, let sphinx pick the best format. +* :ghpull:`14697`: Fix NavigationToolbar2QT height +* :ghpull:`14677`: Don't misclip axis when calling set_ticks on inverted axes. +* :ghpull:`14658`: Fix numpydoc formatting +* :ghpull:`14488`: Make sure EventCollection doesn't modify input in-place +* :ghpull:`14570`: Remove print statements +* :ghpull:`14525`: improve documentation of OffsetBox +* :ghpull:`14548`: Link to matplotlibrc of used version +* :ghpull:`14395`: MAINT: work around non-zero exit status of "pdftops -v" command. -Issues (30): +Issues (28): -* :ghissue:`14620`: Plotting on a log/logit scale overwrites axis inverting -* :ghissue:`14615`: Inverting an axis using its limits does not work for log scale -* :ghissue:`14577`: Calling invert_yaxis() on a 3D plot has either no effect or removes ticks -* :ghissue:`14602`: NavigationToolbar2Tk save_figure function bug -* :ghissue:`1219`: Show fails on figures created with the object-oriented system -* :ghissue:`10167`: Segmentation fault with tricontour -* :ghissue:`13723`: RuntimeError when saving PDFs via parallel processes (not threads!) -* :ghissue:`14315`: Improvement: Better error message if kiwisolver fails to import -* :ghissue:`14356`: matplotlib.units.ConversionError on scatter of dates with a NaN in the first position -* :ghissue:`14467`: Docs for plt.ginput() have the wrong default value for show_clicks keyword argument. -* :ghissue:`14225`: Matplotlib crashes on windows while maximizing plot window when using Multicursor -* :ghissue:`14458`: DOC: small inconsistency in errobar docstring -* :ghissue:`14372`: Document that view_init() arguments should be in degrees -* :ghissue:`12201`: issues clearing rubberband on nbagg at non-default browser zoom -* :ghissue:`13576`: pcolorfast misbehaves when changing axis limits -* :ghissue:`14303`: Unable to import matplotlib on Windows 10 v1903 -* :ghissue:`14283`: RendererSVG CSS 'white-space' property conflicts with default HTML CSS -* :ghissue:`14293`: imshow() producing "inverted" colors since 3.0.3 -* :ghissue:`14322`: Cannot import matplotlib with Python 3.7.x on Win10Pro -* :ghissue:`14137`: Qt5 test auto-skip is not working correctly -* :ghissue:`14301`: scatter() fails on nan-containing input when providing edgecolor -* :ghissue:`14318`: Don't try to set missing history buttons. -* :ghissue:`14265`: symlog looses some points since 3.1.0 (example given) -* :ghissue:`14274`: BUG: plotting with Numpy array subclasses is slow with Matplotlib 3.1.0 (regression) -* :ghissue:`14263`: import pyplot issue - -* :ghissue:`14227`: Update "working with Mpl on OSX" docs -* :ghissue:`13448`: boxplot doesn't normalize properties before applying them -* :ghissue:`14226`: Modify matplotlib TextBox value without triggering callback -* :ghissue:`14232`: LinearSegmentedColormap with N=1 gives confusing error message -* :ghissue:`10365`: Scatter plot with non-sequence ´c´ color should give a better Error message. +* :ghissue:`15295`: Can't install matplotlib with pip for Python 3.8b4 +* :ghissue:`15714`: Publish 3.8 wheels +* :ghissue:`15706`: Python 3.8 - Installation error: TypeError: stat: path should be string, bytes, os.PathLike or integer, not NoneType +* :ghissue:`15690`: Should xlim support single-entry arrays? +* :ghissue:`15608`: imshow rendering changed from 3.1.0 to 3.1.1 +* :ghissue:`14903`: 'MPLBACKEND=webagg' is overwritten by agg when $DISPLAY is not set on Linux +* :ghissue:`15351`: Bar width expands between subsequent bars +* :ghissue:`15240`: Can't specify integer ``font.weight`` in custom style sheet any more +* :ghissue:`15255`: ``imshow`` in ``v3.1.1``: y-axis chopped-off +* :ghissue:`15186`: 3D quiver plot fails when pivot = "middle" +* :ghissue:`14160`: PySide2/PyQt5: Graphics issues in QScrollArea for OSX +* :ghissue:`15178`: mdates.ConciseDateFormatter() doesn't work with zero_formats parameter +* :ghissue:`15179`: Patch 3.1.1 broke imshow() heatmaps: Tiles cut off on y-axis +* :ghissue:`15162`: axes.bar fails when x is int-indexed pandas.Series +* :ghissue:`15103`: Colorbar for imshow messes interactive cursor with masked data +* :ghissue:`8744`: ConnectionPatch hidden by plots +* :ghissue:`14950`: plt.ioff() not supressing figure generation +* :ghissue:`14959`: Typo in Docs +* :ghissue:`14902`: from matplotlib import animation UnicodeDecodeError +* :ghissue:`14897`: New yticks behavior in 3.1.1 vs 3.1.0 +* :ghissue:`14811`: How to save hexbin binned data in a text file. +* :ghissue:`14551`: Non functional API links break docs builds downstream +* :ghissue:`14720`: Line2D properties should state units +* :ghissue:`10891`: Toolbar icons too large in PyQt5 (Qt5Agg backend) +* :ghissue:`14675`: Heatmaps are being truncated when using with seaborn +* :ghissue:`14487`: eventplot sorts np.array positions, but not list positions +* :ghissue:`14547`: Changing mplstyle: axes.titlelocation causes Bad Key error +* :ghissue:`10410`: eventplot alters data in some cases diff --git a/doc/users/prev_whats_new/github_stats_3.1.3.rst b/doc/users/prev_whats_new/github_stats_3.1.3.rst new file mode 100644 index 000000000000..b4706569df02 --- /dev/null +++ b/doc/users/prev_whats_new/github_stats_3.1.3.rst @@ -0,0 +1,87 @@ +.. _github-stats-3-1-3: + +GitHub statistics for 3.1.3 (Feb 03, 2020) +========================================== + +GitHub statistics for 2019/11/05 (tag: v3.1.2) - 2020/02/03 + +These lists are automatically generated, and may be incomplete or contain duplicates. + +We closed 7 issues and merged 45 pull requests. +The full list can be seen `on GitHub `__ + +The following 13 authors contributed 125 commits. + +* Antony Lee +* David Stansby +* Elliott Sales de Andrade +* hannah +* Jody Klymak +* MeeseeksMachine +* Nelle Varoquaux +* Nikita Kniazev +* Paul Ivanov +* SamSchott +* Steven G. Johnson +* Thomas A Caswell +* Tim Hoffmann + +GitHub issues and pull requests: + +Pull Requests (45): + +* :ghpull:`16382`: Backport PR #16379 on branch v3.1.x (FIX: catch on message content, not module) +* :ghpull:`16362`: Backport PR #16347: FIX: catch warnings from pandas in cbook._check_1d +* :ghpull:`16356`: Backport PR #16330 on branch v3.1.x (Clearer signal handling) +* :ghpull:`16330`: Clearer signal handling +* :ghpull:`16348`: Backport PR #16255 on branch v3.1.x (Move version info to sidebar) +* :ghpull:`16345`: Backport PR #16298 on branch v3.1.x (Don't recursively call draw_idle when updating artists at draw time.) +* :ghpull:`16298`: Don't recursively call draw_idle when updating artists at draw time. +* :ghpull:`16322`: Backport PR #16250: Fix zerolen intersect +* :ghpull:`16320`: Backport PR #16311 on branch v3.1.x (don't override non-Python signal handlers) +* :ghpull:`16311`: don't override non-Python signal handlers +* :ghpull:`16250`: Fix zerolen intersect +* :ghpull:`16237`: Backport PR #16235 on branch v3.1.x (FIX: AttributeError in TimerBase.start) +* :ghpull:`16235`: FIX: AttributeError in TimerBase.start +* :ghpull:`16208`: Backport PR #15556 on branch v3.1.x (Fix test suite compat with ghostscript 9.50.) +* :ghpull:`16213`: Backport PR #15763 on branch v3.1.x (Skip webagg test if tornado is not available.) +* :ghpull:`16167`: Backport PR #16166 on branch v3.1.x (Add badge for citing 3.1.2) +* :ghpull:`16166`: Add badge for citing 3.1.2 +* :ghpull:`16144`: Backport PR #16053 on branch v3.1.x (Fix v_interval setter) +* :ghpull:`16053`: Fix v_interval setter +* :ghpull:`16136`: Backport PR #16112 on branch v3.1.x (CI: Fail when failed to install dependencies) +* :ghpull:`16131`: Backport PR #16126 on branch v3.1.x (TST: test_fork: Missing join) +* :ghpull:`16126`: TST: test_fork: Missing join +* :ghpull:`16091`: Backport PR #16086 on branch v3.1.x (FIX: use supported attribute to check pillow version) +* :ghpull:`16040`: Backport PR #16031 on branch v3.1.x (Fix docstring of hillshade().) +* :ghpull:`16032`: Backport PR #16028 on branch v3.1.x (Prevent FigureCanvasQT_draw_idle recursively calling itself.) +* :ghpull:`16028`: Prevent FigureCanvasQT_draw_idle recursively calling itself. +* :ghpull:`16020`: Backport PR #16007 on branch v3.1.x (Fix search on nested pages) +* :ghpull:`16018`: Backport PR #15735 on branch v3.1.x (Cleanup some mplot3d docstrings.) +* :ghpull:`16007`: Fix search on nested pages +* :ghpull:`15957`: Backport PR #15953 on branch v3.1.x (Update donation link) +* :ghpull:`15763`: Skip webagg test if tornado is not available. +* :ghpull:`15881`: Backport PR #15859 on branch v3.1.x (Doc: Move search field into nav bar) +* :ghpull:`15863`: Backport PR #15244 on branch v3.1.x: Change documentation format of rcParams defaults +* :ghpull:`15859`: Doc: Move search field into nav bar +* :ghpull:`15860`: Backport PR #15851 on branch v3.1.x (ffmpeg is available on default ubuntu packages now) +* :ghpull:`15851`: ffmpeg is available on default ubuntu packages now. +* :ghpull:`15843`: Backport PR #15737 on branch v3.1.x (Fix env override in WebAgg backend test.) +* :ghpull:`15760`: Backport PR #15752 on branch v3.1.x (Update boxplot/violinplot faq.) +* :ghpull:`15757`: Backport PR #15751 on branch v3.1.x (Modernize FAQ entry for plt.show().) +* :ghpull:`15735`: Cleanup some mplot3d docstrings. +* :ghpull:`15753`: Backport PR #15661 on branch v3.1.x (Document scope of 3D scatter depthshading.) +* :ghpull:`15741`: Backport PR #15729 on branch v3.1.x (Catch correct parse errror type for dateutil >= 2.8.1) +* :ghpull:`15729`: Catch correct parse errror type for dateutil >= 2.8.1 +* :ghpull:`15737`: Fix env override in WebAgg backend test. +* :ghpull:`15244`: Change documentation format of rcParams defaults + +Issues (7): + +* :ghissue:`16294`: BUG: Interactive mode slow +* :ghissue:`15842`: Path.intersects_path returns True when it shouldn't +* :ghissue:`16163`: libpng error: Read Error when using matplotlib after setting usetex=True +* :ghissue:`15960`: v3.1.2 - test suite "frozen" after it finishes +* :ghissue:`16083`: Pillow 7.0.0 Support +* :ghissue:`15481`: Recursion error +* :ghissue:`15717`: Move search field into nav bar diff --git a/doc/users/prev_whats_new/github_stats_3.2.0.rst b/doc/users/prev_whats_new/github_stats_3.2.0.rst index f5cee3ad245c..5fd75f7c57d0 100644 --- a/doc/users/prev_whats_new/github_stats_3.2.0.rst +++ b/doc/users/prev_whats_new/github_stats_3.2.0.rst @@ -1,9 +1,9 @@ .. _github-stats-3-2-0: -GitHub Stats for Matplotlib 3.2.0 -================================= +GitHub statistics for 3.2.0 (Mar 04, 2020) +========================================== -GitHub stats for 2019/05/18 - 2020/03/03 (tag: v3.1.0) +GitHub statistics for 2019/05/18 (tag: v3.1.0) - 2020/03/04 These lists are automatically generated, and may be incomplete or contain duplicates. diff --git a/doc/users/prev_whats_new/github_stats_3.2.1.rst b/doc/users/prev_whats_new/github_stats_3.2.1.rst index ec95cf4a7887..4f865dbb5429 100644 --- a/doc/users/prev_whats_new/github_stats_3.2.1.rst +++ b/doc/users/prev_whats_new/github_stats_3.2.1.rst @@ -1,9 +1,9 @@ .. _github-stats-3-2-1: -GitHub Stats for Matplotlib 3.2.1 -================================= +GitHub statistics for 3.2.1 (Mar 18, 2020) +========================================== -GitHub stats for 2020/03/03 - 2020/03/17 (tag: v3.2.0) +GitHub statistics for 2020/03/03 (tag: v3.2.0) - 2020/03/18 These lists are automatically generated, and may be incomplete or contain duplicates. diff --git a/doc/users/prev_whats_new/github_stats_3.2.2.rst b/doc/users/prev_whats_new/github_stats_3.2.2.rst index 8cd4e4eef1d7..9026d518ce4d 100644 --- a/doc/users/prev_whats_new/github_stats_3.2.2.rst +++ b/doc/users/prev_whats_new/github_stats_3.2.2.rst @@ -1,9 +1,9 @@ .. _github-stats-3-2-2: -GitHub Stats for Matplotlib 3.2.2 -================================= +GitHub statistics for 3.2.2 (Jun 17, 2020) +========================================== -GitHub stats for 2020/03/18 - 2020/06/17 (tag: v3.2.1) +GitHub statistics for 2020/03/18 (tag: v3.2.1) - 2020/06/17 These lists are automatically generated, and may be incomplete or contain duplicates. diff --git a/doc/users/prev_whats_new/github_stats_3.3.0.rst b/doc/users/prev_whats_new/github_stats_3.3.0.rst index c57d4cda8cba..c2e6cd132c2d 100644 --- a/doc/users/prev_whats_new/github_stats_3.3.0.rst +++ b/doc/users/prev_whats_new/github_stats_3.3.0.rst @@ -1,9 +1,9 @@ .. _github-stats-3-3-0: -GitHub Stats for Matplotlib 3.3.0 -================================= +GitHub statistics for 3.3.0 (Jul 16, 2020) +========================================== -GitHub stats for 2020/03/03 - 2020/07/16 (tag: v3.2.0) +GitHub statistics for 2020/03/03 (tag: v3.2.0) - 2020/07/16 These lists are automatically generated, and may be incomplete or contain duplicates. diff --git a/doc/users/prev_whats_new/github_stats_3.3.1.rst b/doc/users/prev_whats_new/github_stats_3.3.1.rst index ed6638638959..49212587a17a 100644 --- a/doc/users/prev_whats_new/github_stats_3.3.1.rst +++ b/doc/users/prev_whats_new/github_stats_3.3.1.rst @@ -1,9 +1,9 @@ .. _github-stats-3-3-1: -GitHub Stats for Matplotlib 3.3.1 -================================= +GitHub statistics for 3.3.1 (Aug 13, 2020) +========================================== -GitHub stats for 2020/07/16 - 2020/08/13 (tag: v3.3.0) +GitHub statistics for 2020/07/16 (tag: v3.3.0) - 2020/08/13 These lists are automatically generated, and may be incomplete or contain duplicates. diff --git a/doc/users/prev_whats_new/github_stats_3.3.2.rst b/doc/users/prev_whats_new/github_stats_3.3.2.rst index 8f9bb9a6eceb..0bc03cbc83ee 100644 --- a/doc/users/prev_whats_new/github_stats_3.3.2.rst +++ b/doc/users/prev_whats_new/github_stats_3.3.2.rst @@ -1,9 +1,9 @@ .. _github-stats-3-3-2: -GitHub Stats for Matplotlib 3.3.2 -================================= +GitHub statistics for 3.3.2 (Sep 15, 2020) +========================================== -GitHub stats for 2020/08/14 - 2020/09/15 (tag: v3.3.1) +GitHub statistics for 2020/08/14 - 2020/09/15 (tag: v3.3.1) These lists are automatically generated, and may be incomplete or contain duplicates. diff --git a/doc/users/prev_whats_new/github_stats_3.3.3.rst b/doc/users/prev_whats_new/github_stats_3.3.3.rst index d7ba592d7339..5475a5209eed 100644 --- a/doc/users/prev_whats_new/github_stats_3.3.3.rst +++ b/doc/users/prev_whats_new/github_stats_3.3.3.rst @@ -1,9 +1,9 @@ .. _github-stats-3-3-3: -GitHub Stats for Matplotlib 3.3.3 -================================= +GitHub statistics for 3.3.3 (Nov 11, 2020) +========================================== -GitHub stats for 2020/09/15 - 2020/11/11 (tag: v3.3.2) +GitHub statistics for 2020/09/15 (tag: v3.3.2) - 2020/11/11 These lists are automatically generated, and may be incomplete or contain duplicates. diff --git a/doc/users/prev_whats_new/github_stats_3.3.4.rst b/doc/users/prev_whats_new/github_stats_3.3.4.rst index e61309836034..afff8b384b8e 100644 --- a/doc/users/prev_whats_new/github_stats_3.3.4.rst +++ b/doc/users/prev_whats_new/github_stats_3.3.4.rst @@ -1,9 +1,9 @@ .. _github-stats-3-3-4: -GitHub Stats for Matplotlib 3.3.4 -================================= +GitHub statistics for 3.3.4 (Jan 28, 2021) +========================================== -GitHub stats for 2020/11/12 - 2021/01/28 (tag: v3.3.3) +GitHub statistics for 2020/11/12 (tag: v3.3.3) - 2021/01/28 These lists are automatically generated, and may be incomplete or contain duplicates. diff --git a/doc/users/prev_whats_new/github_stats_3.4.0.rst b/doc/users/prev_whats_new/github_stats_3.4.0.rst new file mode 100644 index 000000000000..fe49e673a660 --- /dev/null +++ b/doc/users/prev_whats_new/github_stats_3.4.0.rst @@ -0,0 +1,1174 @@ +.. _github-stats-3-4-0: + +GitHub statistics for 3.4.0 (Mar 26, 2021) +========================================== + +GitHub statistics for 2020/07/16 (tag: v3.3.0) - 2021/03/26 + +These lists are automatically generated, and may be incomplete or contain duplicates. + +We closed 204 issues and merged 772 pull requests. +The full list can be seen `on GitHub `__ + +The following 177 authors contributed 3852 commits. + +* A N U S H +* Adam Brown +* Aditya Malhotra +* aflah02 +* Aitik Gupta +* Alejandro García +* Alex Henrie +* Alexander Schlüter +* Alexis de Almeida Coutinho +* Andreas C Mueller +* andrzejnovak +* Antony Lee +* Arthur Milchior +* bakes +* BAKEZQ +* BaoGiang HoangVu +* Ben Root +* BH4 +* Bradley Dice +* Braxton Lamey +* Brian McFee +* Bruno Beltran +* Bryan Kok +* Byron Boulton +* Carsten Schelp +* ceelo777 +* Charles +* CharlesHe16 +* Christian Baumann +* Contextualist +* DangoMelon +* Daniel +* Daniel Ingram +* David Meyer +* David Stansby +* David Young +* deep-jkl +* Diego Leal +* Dr. Thomas A Caswell +* Dylan Cutler +* Eben Pendleton +* EBenkler +* ebenp +* ecotner +* Elliott Sales de Andrade +* Emily FY +* Eric Firing +* Eric Larson +* Eric Prestat +* Erik Benkler +* Evan Berkowitz +* Ewan Sutherland +* Federico Ariza +* Forrest +* Frank Sauerburger +* FrankTheCodeMonkey +* Greg Lucas +* hannah +* Harry Knight +* Harsh Sharma +* Hassan Kibirige +* Hugo van Kemenade +* Iain-S +* Ian Hunt-Isaak +* Ian Thomas +* ianhi +* Ilya V. Schurov +* ImportanceOfBeingErnest +* Isuru Fernando +* ItsRLuo +* J. Scott Berg +* Jae-Joon Lee +* Jakub Klus +* Janakarajan Natarajan +* Jann Paul Mattern +* jbhopkins +* jeetvora331 +* Jerome F. Villegas +* Jerome Villegas +* jfbu +* Jirka Hladky +* Jody Klymak +* Johan von Forstner +* johan12345 +* john imperial +* John Losito +* John Peloquin +* johnthagen +* Jouni K. Seppänen +* Kate Perkins +* kate-perkins +* katrielester +* kolibril13 +* kwgchi +* Lee Johnston +* Leo Singer +* linchiwei123 +* Lucy Liu +* luz paz +* luzpaz +* Léonard Gérard +* majorwitty +* mansoor96g +* Maria Ilie +* Maria-Alexandra Ilie +* Marianne Corvellec +* Mark Harfouche +* Martin Spacek +* Mary Chris Go +* Matthew Petroff +* Matthias Bussonnier +* Matthias Geier +* Max Chen +* McToel +* Michael Grupp +* Michaël Defferrard +* Mihai Anton +* Mohammad Aflah Khan +* Neilzon Viloria +* neok-m4700 +* Nora Moseman +* Pamela Wu +* pankajchetry1168 +* Petar Mlinarić +* Peter Williams +* Phil Nagel +* philip-sparks +* Philipp Arras +* Philipp Nagel +* Pratyush Raj +* Péter Leéh +* rajpratyush +* Randall Ung +* reshamas +* Rezangyal +* Richard Sheridan +* richardsheridan +* Rob McDonald +* Rohit Rawat +* Ruben Verweij +* Ruth Comer +* Ryan May +* Sam Tygier +* shawnchen +* shawnchen1996 +* ShawnChen1996 +* Sidharth Bansal +* Srihitha Maryada +* Stephen Sinclair +* Struan Murray +* Theodor Athanasiadis +* Thomas A Caswell +* Thorvald Johannessen +* Tim Gates +* Tim Hoffmann +* Tobias Hangleiter +* tohc1 +* Tom Charrett +* Tom Neep +* Tomas Fiers +* ulijh +* Ulrich J. Herter +* Utkarshp1 +* Uwe F. Mayer +* Valentin Valls +* Vincent Cuenca +* Vineyard +* Vlas Sokolov +* Xianxiang Li +* xlilos +* Ye Chang +* Yichao Yu +* yozhikoff +* Yun Liu +* z0rgy +* zitorelova + +GitHub issues and pull requests: + +Pull Requests (772): + +* :ghpull:`19775`: Fix deprecation for imread on URLs. +* :ghpull:`19772`: Backport PR #19535 on branch v3.4.x (Fix example's BasicUnit array conversion.) +* :ghpull:`19771`: Backport PR #19757 on branch v3.4.x (Fixed python -mpip typo) +* :ghpull:`19770`: Backport PR #19739 on branch v3.4.x (Changed 'python -mpip' to 'python -m pip' for consistency) +* :ghpull:`19535`: Fix example's BasicUnit array conversion. +* :ghpull:`19767`: Backport PR #19766 on branch v3.4.x (Set colormap modification removal to 3.6.) +* :ghpull:`19766`: Set colormap modification removal to 3.6. +* :ghpull:`19764`: Backport PR #19762 on branch v3.4.x (FIX: do not report that webagg supports blitting) +* :ghpull:`19762`: FIX: do not report that webagg supports blitting +* :ghpull:`19689`: Prepare API docs for v3.4.0 +* :ghpull:`19761`: Backport PR #19746 on branch v3.4.x (Fix resizing in nbAgg.) +* :ghpull:`19746`: Fix resizing in nbAgg. +* :ghpull:`19757`: Fixed python -mpip typo +* :ghpull:`19739`: Changed 'python -mpip' to 'python -m pip' for consistency +* :ghpull:`19713`: DOC: Prepare What's new page for 3.4.0. +* :ghpull:`19742`: Backport PR #19741 on branch v3.4.x (Only override pickradius when picker is not a bool.) +* :ghpull:`19741`: Only override pickradius when picker is not a bool. +* :ghpull:`19726`: Backport PR #19505 on branch v3.4.x (Move some advanced documentation away from Installation Guide) +* :ghpull:`19505`: Move some advanced documentation away from Installation Guide +* :ghpull:`19712`: Backport PR #19707 on branch v3.4.x (DOC: fix dx in Arrow guide) +* :ghpull:`19711`: Backport PR #19709 on branch v3.4.x (Fix arrow_guide.py typo) +* :ghpull:`19709`: Fix arrow_guide.py typo +* :ghpull:`19707`: DOC: fix dx in Arrow guide +* :ghpull:`19699`: Backport PR #19695 on branch v3.4.x (DOC: Increase size of headings) +* :ghpull:`19695`: DOC: Increase size of headings +* :ghpull:`19697`: Backport PR #19690 on branch v3.4.x (Only warn about existing redirects if content differs.) +* :ghpull:`19690`: Only warn about existing redirects if content differs. +* :ghpull:`19696`: Backport PR #19665 on branch v3.4.x (Changed FormatStrFormatter documentation to include how to get unicode minus) +* :ghpull:`19680`: Backport PR #19402 on branch v3.4.x (Build aarch64 wheels) +* :ghpull:`19678`: Backport PR #19671 on branch v3.4.x (Fix crash in early window raise in gtk3.) +* :ghpull:`19671`: Fix crash in early window raise in gtk3. +* :ghpull:`19665`: Changed FormatStrFormatter documentation to include how to get unicode minus +* :ghpull:`19402`: Build aarch64 wheels +* :ghpull:`19669`: Backport PR #19661 on branch v3.4.x (Fix CoC link) +* :ghpull:`19668`: Backport PR #19663 on branch v3.4.x (ENH: add a copy method to colormaps) +* :ghpull:`19663`: ENH: add a copy method to colormaps +* :ghpull:`19661`: Fix CoC link +* :ghpull:`19652`: Backport PR #19649 on branch v3.4.x (Use globals() instead of locals() for adding colormaps as names to cm module) +* :ghpull:`19649`: Use globals() instead of locals() for adding colormaps as names to cm module +* :ghpull:`19651`: Backport PR #19618 on branch v3.4.x (FIX: make the cache in font_manager._get_font keyed by thread id) +* :ghpull:`19650`: Backport PR #19625 on branch v3.4.x (Restore _AxesStack to track a Figure's Axes order.) +* :ghpull:`19647`: Backport PR #19645 on branch v3.4.x (Fix comment in RectangleSelector) +* :ghpull:`19618`: FIX: make the cache in font_manager._get_font keyed by thread id +* :ghpull:`19648`: Backport PR #19643 on branch v3.4.x (Don't turn check_for_pgf into public API.) +* :ghpull:`19625`: Restore _AxesStack to track a Figure's Axes order. +* :ghpull:`19643`: Don't turn check_for_pgf into public API. +* :ghpull:`19645`: Fix comment in RectangleSelector +* :ghpull:`19644`: Backport PR #19611 on branch v3.4.x (Fix double picks.) +* :ghpull:`19611`: Fix double picks. +* :ghpull:`19640`: Backport PR #19639 on branch v3.4.x (FIX: do not allow single element list of str in subplot_mosaic) +* :ghpull:`19639`: FIX: do not allow single element list of str in subplot_mosaic +* :ghpull:`19638`: Backport PR #19632 on branch v3.4.x (Fix handling of warn keyword in Figure.show.) +* :ghpull:`19637`: Backport PR #19582 on branch v3.4.x (Add kerning to single-byte strings in PDFs) +* :ghpull:`19632`: Fix handling of warn keyword in Figure.show. +* :ghpull:`19582`: Add kerning to single-byte strings in PDFs +* :ghpull:`19629`: Backport PR #19548 on branch v3.4.x (Increase tolerances for other arches.) +* :ghpull:`19630`: Backport PR #19596 on branch v3.4.x (Fix for issue 17769: wx interactive figure close cause crash) +* :ghpull:`19596`: Fix for issue 17769: wx interactive figure close cause crash +* :ghpull:`19548`: Increase tolerances for other arches. +* :ghpull:`19616`: Backport PR #19577 on branch v3.4.x (Fix "return"->"enter" mapping in key names.) +* :ghpull:`19617`: Backport PR #19571 on branch v3.4.x (Fail early when setting Text color to a non-colorlike.) +* :ghpull:`19615`: Backport PR #19583 on branch v3.4.x (FIX: check for a set during color conversion) +* :ghpull:`19614`: Backport PR #19597 on branch v3.4.x (Fix IPython import issue) +* :ghpull:`19613`: Backport PR #19546 on branch v3.4.x (Move unrendered README.wx to thirdpartypackages/index.rst.) +* :ghpull:`19583`: FIX: check for a set during color conversion +* :ghpull:`19597`: Fix IPython import issue +* :ghpull:`19571`: Fail early when setting Text color to a non-colorlike. +* :ghpull:`19595`: Backport PR #19589 on branch v3.4.x (Changes linestyle parameter of flierprops) +* :ghpull:`19577`: Fix "return"->"enter" mapping in key names. +* :ghpull:`19589`: Changes linestyle parameter of flierprops +* :ghpull:`19592`: Backport PR #19587 on branch v3.4.x (DOC: fix plot_date doc) +* :ghpull:`19587`: DOC: fix plot_date doc +* :ghpull:`19580`: Backport PR #19456 on branch v3.4.x (Doc implement reredirects) +* :ghpull:`19579`: Backport PR #19567 on branch v3.4.x (DOC: fix typos) +* :ghpull:`19456`: Doc implement reredirects +* :ghpull:`19567`: DOC: fix typos +* :ghpull:`19542`: Backport PR #19532 on branch v3.4.x (Add note on interaction between text wrapping and bbox_inches='tight') +* :ghpull:`19549`: Backport PR #19545 on branch v3.4.x (Replace references to pygtk by pygobject in docs.) +* :ghpull:`19546`: Move unrendered README.wx to thirdpartypackages/index.rst. +* :ghpull:`19545`: Replace references to pygtk by pygobject in docs. +* :ghpull:`19532`: Add note on interaction between text wrapping and bbox_inches='tight' +* :ghpull:`19541`: MAINT: fix typo from #19438 +* :ghpull:`19480`: Fix CallbackRegistry memory leak +* :ghpull:`19539`: In scatter, fix single rgb edgecolors handling +* :ghpull:`19438`: FIX: restore creating new axes via plt.subplot with different kwargs +* :ghpull:`18436`: Sync 3D errorbar with 2D +* :ghpull:`19472`: Fix default label visibility for top-or-left-labeled shared subplots(). +* :ghpull:`19496`: MNT: Restore auto-adding Axes3D to their parent figure on init +* :ghpull:`19533`: Clarify the animated property and reword blitting tutorial a bit +* :ghpull:`19146`: Fix #19128: webagg reports incorrect values for non-alphanumeric key events on non-qwerty keyboards +* :ghpull:`18068`: Add note on writing binary formats to stdout using savefig() +* :ghpull:`19507`: FIX: ensure we import when the user cwd does not exist +* :ghpull:`19413`: FIX: allow add option for Axes3D(fig) +* :ghpull:`19498`: Dedupe implementations of {XAxis,YAxis}._get_tick_boxes_siblings. +* :ghpull:`19502`: Prefer projection="polar" over polar=True. +* :ghpull:`18480`: Clarify color priorities in collections +* :ghpull:`19501`: Fix text position with usetex and xcolor +* :ghpull:`19460`: Implement angles for bracket arrow styles. +* :ghpull:`18408`: FIX/API: ``fig.canvas.draw`` always updates internal state +* :ghpull:`19504`: Remove remaining references to Travis CI +* :ghpull:`13358`: 3D margins consistency for mplot3d (isometric projection) +* :ghpull:`19529`: Simplify checking for tex packages. +* :ghpull:`19516`: Ignore files from annotate coverage reports +* :ghpull:`19500`: Remove workaround for numpy<1.16, and update version check. +* :ghpull:`19518`: Skip setting up a tmpdir in tests that don't need one. +* :ghpull:`19514`: DOC: add fixed-aspect colorbar examples +* :ghpull:`19511`: Clarify axes.autolimit_mode rcParam. +* :ghpull:`19503`: Fix tight_layout() on "canvasless" figures. +* :ghpull:`19410`: Set the GTK background color to white. +* :ghpull:`19497`: Add overset/underset whatsnew entry +* :ghpull:`19490`: Fix error message in plt.close(). +* :ghpull:`19461`: Move ToolManager warnings to rcParam validator +* :ghpull:`19488`: Prefer ``tr1-tr2`` to ``tr1+tr2.inverted()``. +* :ghpull:`19485`: fix regression of axline behavior with non-linear scales +* :ghpull:`19314`: Fix over/under mathtext symbols +* :ghpull:`19468`: Include tex output in pdf LatexError. +* :ghpull:`19478`: Fix trivial typo in error message. +* :ghpull:`19449`: Switch array-like (M, N) to (M, N) array-like. +* :ghpull:`19459`: Merge v3.3.4 into master +* :ghpull:`18746`: Make figure parameter optional when constructing canvases. +* :ghpull:`19455`: Add note that pyplot cannot be used for 3D. +* :ghpull:`19457`: Use absolute link for discourse +* :ghpull:`19440`: Slightly reorganize api docs. +* :ghpull:`19344`: Improvements to Docs for new contributors +* :ghpull:`19435`: Replace gtk3 deprecated APIs that have simple replacements. +* :ghpull:`19452`: Fix the docstring of draw_markers to match the actual behavior. +* :ghpull:`19448`: Remove unnecessary facecolor cache in Patch3D. +* :ghpull:`19396`: CI: remove win prerelease azure + add py39 +* :ghpull:`19426`: Support empty stairs. +* :ghpull:`19399`: Fix empty Poly3DCollections +* :ghpull:`19416`: fixes TypeError constructor returned NULL in wayland session +* :ghpull:`19439`: Move cheatsheet focus to the cheatsheets away +* :ghpull:`19425`: Add units to bar_label padding documentation. +* :ghpull:`19422`: Style fixes to triintepolate docs. +* :ghpull:`19421`: Switch to documenting generic collections in lowercase. +* :ghpull:`19411`: DOC: fix incorrect parameter names +* :ghpull:`19387`: Fix CSS table header layout +* :ghpull:`18683`: Better document font. rcParams entries. +* :ghpull:`19418`: BF: DOCS: fix slash for windows in conf.py +* :ghpull:`18544`: REORG: JoinStyle and CapStyle classes +* :ghpull:`19415`: Make TaggedValue in basic_units a sequence +* :ghpull:`19412`: DOC: correct off by one indentation. +* :ghpull:`19407`: Improve doc of default labelpad. +* :ghpull:`19373`: test for align_ylabel bug with constrained_layout +* :ghpull:`19347`: os.environ-related cleanups. +* :ghpull:`19319`: DOC: make canonical version stable +* :ghpull:`19395`: wx: Use integers in more places +* :ghpull:`17850`: MNT: set the facecolor of nofill markers +* :ghpull:`19334`: Fix qt backend on mac big sur +* :ghpull:`19394`: Don't allow pyzmq 22.0.0 on AppVeyor. +* :ghpull:`19367`: Deprecate imread() reading from URLs +* :ghpull:`19341`: MarkerStyle is considered immutable +* :ghpull:`19337`: Move sphinx extension files into mpl-data. +* :ghpull:`19389`: Temporarily switch intersphinx to latest pytest. +* :ghpull:`19390`: Doc: Minor formatting +* :ghpull:`19383`: Always include sample_data in installs. +* :ghpull:`19378`: Modify indicate_inset default label value +* :ghpull:`19357`: Shorten/make more consistent the half-filled marker definitions. +* :ghpull:`18649`: Deprecate imread() reading from URLs +* :ghpull:`19370`: Force classic ("auto") date converter in classic style. +* :ghpull:`19364`: Fix trivial doc typos. +* :ghpull:`19359`: Replace use of pyplot with OO api in some examples +* :ghpull:`19342`: FIX: fix bbox_inches=tight and constrained layout bad interaction +* :ghpull:`19350`: Describe how to test regular installations of Matplotlib +* :ghpull:`19332`: Prefer concatenate to h/vstack in simple cases. +* :ghpull:`19340`: Remove the deprecated rcParams["datapath"]. +* :ghpull:`19326`: Whitespace in Choosing Colormaps tutorial plots +* :ghpull:`16417`: Deprecate rcParams["datapath"] in favor of mpl.get_data_path(). +* :ghpull:`19336`: Revert "Deprecate setting Line2D's pickradius via set_picker." +* :ghpull:`19153`: MNT: Remove deprecated axes kwargs collision detection (version 2) +* :ghpull:`19330`: Remove register storage class from Agg files. +* :ghpull:`19324`: Improve FT2Font docstrings. +* :ghpull:`19328`: Explain annotation behavior when used in conjunction with arrows +* :ghpull:`19329`: Fix building against system qhull +* :ghpull:`19331`: Skip an ImageMagick test if ffmpeg is unavailable. +* :ghpull:`19333`: Fix PGF with special character paths. +* :ghpull:`19322`: Improve docs of _path C-extension. +* :ghpull:`19317`: Pin to oldest supported PyQt on minver CI instance. +* :ghpull:`19315`: Update the markers part of matplotlib.pyplot.plot document (fix issue #19274) +* :ghpull:`18978`: API: Remove deprecated axes kwargs collision detection +* :ghpull:`19306`: Fix some packaging issues +* :ghpull:`19291`: Cleanup code for format processing +* :ghpull:`19316`: Simplify X11 checking for Qt. +* :ghpull:`19287`: Speedup LinearSegmentedColormap.from_list. +* :ghpull:`19293`: Fix some docstring interpolations +* :ghpull:`19313`: Add missing possible return value to docs of get_verticalalignment() +* :ghpull:`18916`: Add overset and underset support for mathtext +* :ghpull:`18126`: FIX: Allow deepcopy on norms and scales +* :ghpull:`19281`: Make all transforms copiable (and thus scales, too). +* :ghpull:`19294`: Deprecate project argument to Line3DCollection.draw. +* :ghpull:`19307`: DOC: remove stray assignment in "multiple legends" example +* :ghpull:`19303`: Extended the convolution filter for correct dilation +* :ghpull:`19261`: Add machinery for png-only, single-font mathtext tests. +* :ghpull:`16571`: Update Qhull to 2019.1 reentrant version +* :ghpull:`16720`: Download qhull at build-or-sdist time. +* :ghpull:`18653`: ENH: Add func norm +* :ghpull:`19272`: Strip irrelevant information from testing docs +* :ghpull:`19298`: Fix misplaced colon in bug report template. +* :ghpull:`19297`: Clarify return format of Line2D.get_data. +* :ghpull:`19277`: Warn on redundant definition of plot properties +* :ghpull:`19278`: Cleanup and document _plot_args() +* :ghpull:`19282`: Remove the unused TransformNode._gid. +* :ghpull:`19264`: Expand on slider_demo example +* :ghpull:`19244`: Move cbook._check_isinstance() to _api.check_isinstance() +* :ghpull:`19273`: Use proper pytest functionality for warnings and exceptions +* :ghpull:`19262`: more robust check for enter key in TextBox +* :ghpull:`19249`: Clarify Doc for Secondary axis, ad-hoc example +* :ghpull:`19248`: Make return value of _get_patch_verts always an array. +* :ghpull:`19247`: Fix markup for mplot3d example. +* :ghpull:`19216`: Ignore non-draw codes when calculating path extent +* :ghpull:`19215`: Collect information for setting up a development environment +* :ghpull:`19210`: Fix creation of AGG images bigger than 1024**3 pixels +* :ghpull:`18933`: Set clip path for PostScript texts. +* :ghpull:`19162`: Deprecate cbook.warn_deprecated and move internal calls to _api.warn_deprecated +* :ghpull:`16391`: Re-write sym-log-norm +* :ghpull:`19240`: FIX: process lists for inverse norms +* :ghpull:`18737`: Fix data cursor for images with additional transform +* :ghpull:`18642`: Propagate minpos from Collections to Axes.datalim +* :ghpull:`19242`: Update first occurrence of QT to show both 4 and 5 +* :ghpull:`19231`: Add reference section to all statistics examples +* :ghpull:`19217`: Request an autoscale at the end of ax.pie() +* :ghpull:`19176`: Deprecate additional positional args to plot_{surface,wireframe}. +* :ghpull:`19063`: Give plot_directive output a ``max-width: 100%`` +* :ghpull:`19187`: Support callable for formatting of Sankey labels +* :ghpull:`19220`: Remove one TOC level from the release guide +* :ghpull:`19212`: MNT: try to put more whitespace in welcome message +* :ghpull:`19155`: Consolidated the Install from Source docs +* :ghpull:`19208`: added version ask/hint to issue templates, grammar on pr bot +* :ghpull:`19185`: Document Triangulation.triangles +* :ghpull:`19181`: Remove unused imports +* :ghpull:`19207`: Fix Grouper example code +* :ghpull:`19204`: Clarify Date Format Example +* :ghpull:`19200`: Fix incorrect statement regarding test images cache size. +* :ghpull:`19198`: Fix link in contrbuting docs +* :ghpull:`19196`: Fix PR welcome action +* :ghpull:`19188`: Cleanup comparision between X11/CSS4 and xkcd colors +* :ghpull:`19194`: Fix trivial quiver doc typo. +* :ghpull:`19180`: Fix Artist.remove_callback() +* :ghpull:`19192`: Fixed part of Issue - #19100, changed documentation for axisartist +* :ghpull:`19179`: Check that no new figures are created in image comparison tests +* :ghpull:`19184`: Minor doc cleanup +* :ghpull:`19093`: DOCS: Specifying Colors tutorial format & arrange +* :ghpull:`17107`: Add Spines class as a container for all Axes spines +* :ghpull:`18829`: Create a RangeSlider widget +* :ghpull:`18873`: Getting Started GSoD +* :ghpull:`19175`: Fix axes direction for a floating axisartist +* :ghpull:`19130`: DOC: remove reference to 2.2.x branches from list of active branches +* :ghpull:`15212`: Dedupe window-title setting by moving it to FigureManagerBase. +* :ghpull:`19172`: Fix 3D surface example bug for non-square grid +* :ghpull:`19173`: Ensure backend tests are skipped if unavailable +* :ghpull:`19170`: Clarify meaning of facecolors for LineCollection +* :ghpull:`18310`: Add 3D stem plot +* :ghpull:`18127`: Implement lazy autoscaling in mplot3d. +* :ghpull:`16178`: Add multiple label support for Axes.plot() +* :ghpull:`19151`: Deprecate @cbook.deprecated and move internal calls to @_api.deprecated +* :ghpull:`19088`: Ignore CLOSEPOLY vertices when computing dataLim from patches +* :ghpull:`19166`: CI: add github action to post to first-time PRs openers +* :ghpull:`19124`: GOV/DOC: add section to docs on triaging and triage team +* :ghpull:`15602`: Add an auto-labeling helper function for bar charts +* :ghpull:`19164`: docs: fix simple typo, backslahes -> backslashes +* :ghpull:`19161`: Simplify test_backend_pdf::test_multipage_properfinalize. +* :ghpull:`19141`: FIX: suppress offset text in ConciseDateFormatter when largest scale is in years +* :ghpull:`19150`: Move from @cbook._classproperty to @_api.classproperty +* :ghpull:`19144`: Move from cbook._warn_external() to _api.warn_external() +* :ghpull:`19119`: Don't lose unit change handlers when pickling/unpickling. +* :ghpull:`19145`: Move from cbook._deprecate_*() to _api.deprecate_*() +* :ghpull:`19123`: Use Qt events to refresh pixel ratio. +* :ghpull:`19056`: Support raw/rgba frame format in FFMpegFileWriter +* :ghpull:`19140`: Fix the docstring of suptitle/subxlabel/supylabel. +* :ghpull:`19132`: Normalize docstring interpolation label for kwdoc() property lists +* :ghpull:`19134`: Switch internal API function calls from cbook to _api +* :ghpull:`19138`: Added non-code contributions to incubator docs +* :ghpull:`19125`: DOC: contributor incubator +* :ghpull:`18948`: DOC: Fix latexpdf build +* :ghpull:`18753`: Remove several more deprecations +* :ghpull:`19083`: Fix headless tests on Wayland. +* :ghpull:`19127`: Cleanups to webagg & friends. +* :ghpull:`19122`: FIX/DOC - make Text doscstring interp more easily searchable +* :ghpull:`19106`: Support setting rcParams["image.cmap"] to Colormap instances. +* :ghpull:`19085`: FIX: update a transfrom from transFigure to transSubfigure +* :ghpull:`19117`: Rename a confusing variable. +* :ghpull:`18647`: Axes.axline: implement support transform argument (for points but not slope) +* :ghpull:`16220`: Fix interaction with unpickled 3d plots. +* :ghpull:`19059`: Support blitting in webagg backend +* :ghpull:`19107`: Update pyplot.py +* :ghpull:`19044`: Cleanup Animation frame_formats. +* :ghpull:`19087`: FIX/TST: recursively remove ticks +* :ghpull:`19094`: Suppress -Wunused-function about _import_array when compiling tkagg.cpp. +* :ghpull:`19092`: Fix use transform mplot3d +* :ghpull:`19097`: DOC: add FuncScale to set_x/yscale +* :ghpull:`19089`: ENH: allow passing a scale instance to set_scale +* :ghpull:`19086`: FIX: add a default scale to Normalize +* :ghpull:`19073`: Mention in a few more places that artists default to not-pickable. +* :ghpull:`19079`: Remove incorrect statement about ``hist(..., log=True)``. +* :ghpull:`19076`: Small improvements to aitoff projection. +* :ghpull:`19071`: DOC: Add 'blackman' to list of imshow interpolations +* :ghpull:`17524`: ENH: add supxlabel and supylabel +* :ghpull:`18840`: Add tutorial about autoscaling +* :ghpull:`19042`: Simplify GridHelper invalidation. +* :ghpull:`19048`: Remove _draw_{ticks2,label2}; skip extents computation in _update_ticks. +* :ghpull:`18983`: Pass norm argument to spy +* :ghpull:`18802`: Add code of conduct +* :ghpull:`19060`: Fix broken link in Readme +* :ghpull:`18569`: More generic value snapping for Slider widgets +* :ghpull:`19055`: Fix kwargs handling in AnnotationBbox +* :ghpull:`19041`: Reword docs for exception_handler in CallbackRegistry. +* :ghpull:`19046`: Prepare inlining MovieWriter.cleanup() into MovieWriter.finish(). +* :ghpull:`19050`: Better validate tick direction. +* :ghpull:`19038`: Fix markup in interactive figures doc. +* :ghpull:`19035`: grid_helper_curvelinear cleanups. +* :ghpull:`19022`: Update event handling docs. +* :ghpull:`19025`: Remove individual doc entries for some methods Axes inherits from Artist +* :ghpull:`19018`: Inline and optimize ContourLabeler.get_label_coords. +* :ghpull:`19019`: Deprecate never used ``resize_callback`` param to FigureCanvasTk. +* :ghpull:`19023`: Cleanup comments/docs in backend_macosx, backend_pdf. +* :ghpull:`19020`: Replace mathtext assertions by unpacking. +* :ghpull:`19024`: Dedupe docs of GridSpec.subplots. +* :ghpull:`19013`: Improve docs of _get_packed_offsets, _get_aligned_offsets. +* :ghpull:`19009`: Compactify the implementation of ContourLabeler.add_label_near. +* :ghpull:`19008`: Deprecate event processing wrapper methods on FigureManagerBase. +* :ghpull:`19015`: Better document multilinebaseline (and other small TextArea fixes) +* :ghpull:`19012`: Common ``__init__`` for VPacker and HPacker. +* :ghpull:`19014`: Support normalize_kwargs(None) (== {}). +* :ghpull:`19010`: Inline _print_pdf_to_fh, _print_png_to_fh. +* :ghpull:`19003`: Remove reference to unicode-math in pgf preamble. +* :ghpull:`18847`: Cleanup interactive pan/zoom. +* :ghpull:`18868`: Expire _make_keyword_only deprecations from 3.2 +* :ghpull:`18903`: Move cbook._suppress_matplotlib_deprecation_warning() from cbook to _api +* :ghpull:`18997`: Micro-optimize check_isinstance. +* :ghpull:`18995`: Fix the doc of GraphicsContextBase.set_clip_rectangle. +* :ghpull:`18996`: Fix API change message from #18989 +* :ghpull:`18993`: Don't access private renderer attributes in tkagg blit. +* :ghpull:`18980`: DOC: fix typos +* :ghpull:`18989`: The Artist property rasterized cannot be None anymore +* :ghpull:`18987`: Fix punctuation in doc. +* :ghpull:`18894`: Use selectfont instead of findfont + scalefont + setfont in PostScript. +* :ghpull:`18990`: Minor cleanup of categorical example +* :ghpull:`18947`: Strictly increasing check with test coverage for streamplot grid +* :ghpull:`18981`: Cleanup Firefox SVG example. +* :ghpull:`18969`: Improve documentation on rasterization +* :ghpull:`18876`: Support fully-fractional HiDPI added in Qt 5.14. +* :ghpull:`18976`: Simplify contour_label_demo. +* :ghpull:`18975`: Fix typing error in pyplot's docs +* :ghpull:`18956`: Document rasterized parameter in pcolormesh() explicitly +* :ghpull:`18968`: Fix clabel() for backends without canvas.get_renderer() +* :ghpull:`18949`: Deprecate AxisArtist.ZORDER +* :ghpull:`18830`: Pgf plotting +* :ghpull:`18967`: Remove unnecessary calls to lower(). +* :ghpull:`18910`: Remove Artist.eventson and Container.eventson +* :ghpull:`18964`: Remove special-casing for PostScript dpi in pyplot.py. +* :ghpull:`18961`: Replace sphinx-gallery-specific references by standard :doc: refs. +* :ghpull:`18955`: added needs_ghostscript; skip test +* :ghpull:`18857`: Improve hat graph example +* :ghpull:`18943`: Small cleanup to StepPatch._update_path. +* :ghpull:`18937`: Cleanup stem docs and simplify implementation. +* :ghpull:`18895`: Introduce variable since which mpl version the minimal python version +* :ghpull:`18927`: Improve warning message for missing font family specified via alias. +* :ghpull:`18930`: Document limitations of Path.contains_point() and clarify its semantics +* :ghpull:`18892`: Fixes MIME type for svg frame_format in HTMLWriter. +* :ghpull:`18938`: Edit usetex docs. +* :ghpull:`18923`: Use lambdas to prevent gc'ing and deduplication of widget callbacks. +* :ghpull:`16171`: Contour fixes/improvements +* :ghpull:`18901`: Simplify repeat_delay and fix support for it when using iterable frames. +* :ghpull:`18911`: Added Aria-Labels to all inputs with tooltips for generated HTML animations: issue #17910 +* :ghpull:`18912`: Use CallbackRegistry for {Artist,Collection}.add_callback. +* :ghpull:`18919`: DOCS: fix contourf hatch demo legend +* :ghpull:`18905`: Make docs fail on Warning (and fix all existing warnings) +* :ghpull:`18763`: Single-line string notation for subplot_mosaic +* :ghpull:`18902`: Move ImageMagick version exclusion to _get_executable_info. +* :ghpull:`18915`: Remove hard-coded API removal version mapping. +* :ghpull:`18914`: Fix typo in error message: interable -> iterable. +* :ghpull:`15065`: step-between as drawstyle [Alternative approach to #15019] +* :ghpull:`18532`: Consistent behavior of draw_if_interactive across interactive backends. +* :ghpull:`18908`: Rework interactive backends tests. +* :ghpull:`18817`: MAINT: deprecate validCap, validJoin +* :ghpull:`18907`: Unmark wx-threading-test-failure as strict xfail. +* :ghpull:`18896`: Add note on keeping a reference to animation docstrings +* :ghpull:`18862`: Resolve mathtext.fontset at FontProperties creation time. +* :ghpull:`18877`: Remove fallback to nonexistent setDevicePixelRatioF. +* :ghpull:`18823`: Move from @cbook.deprecated to @_api.deprecated +* :ghpull:`18889`: Switch Tk to using PNG files for buttons +* :ghpull:`18888`: Update version of Matplotlib that needs Python 3.7 +* :ghpull:`18867`: Remove "Demo" from example titles (part 2) +* :ghpull:`18863`: Reword FontProperties docstring. +* :ghpull:`18866`: Fix RGBAxes docs markup. +* :ghpull:`18874`: Slightly compress down the pgf tests. +* :ghpull:`18565`: Make Tkagg blit thread safe +* :ghpull:`18858`: Remove "Demo" from example titles +* :ghpull:`15177`: Bind WX_CHAR_HOOK instead of WX_KEY_DOWN for wx key_press_event. +* :ghpull:`18821`: Simplification of animated histogram example +* :ghpull:`18844`: Fix sphinx formatting issues +* :ghpull:`18834`: Add cross-references to Artist tutorial +* :ghpull:`18827`: Update Qt version in event handling docs. +* :ghpull:`18825`: Warn in pgf backend when unknown font is requested. +* :ghpull:`18822`: Remove deprecate +* :ghpull:`18733`: Time series histogram plot example +* :ghpull:`18812`: Change LogFormatter coeff computation +* :ghpull:`18820`: Fix axes -> Axes changes in figure.py +* :ghpull:`18657`: Move cbook.deprecation to _api.deprecation +* :ghpull:`18818`: Clarify behavior of CallbackRegistry.disconnect with nonexistent cids. +* :ghpull:`18811`: DOC Use 'Axes' instead of 'axes' in figure.py +* :ghpull:`18814`: [Example] update Anscombe's Quartet +* :ghpull:`18806`: DOC Use 'Axes' in _axes.py docstrings +* :ghpull:`18799`: Remove unused wx private attribute. +* :ghpull:`18772`: BF: text not drawn shouldn't count for tightbbox +* :ghpull:`18793`: Consistently use axs to refer to a set of Axes (v2) +* :ghpull:`18792`: Cmap cleanup +* :ghpull:`18798`: Deprecate ps.useafm for mathtext +* :ghpull:`18302`: Remove 3D attributes from renderer +* :ghpull:`18795`: Make inset indicator more visible in the example +* :ghpull:`18781`: Update description of web application server example. +* :ghpull:`18791`: Fix documentation of edgecolors precedence for scatter() +* :ghpull:`14645`: Add a helper to copy a colormap and set its extreme colors. +* :ghpull:`17709`: Enh: SymNorm for normalizing symmetrical data around a center +* :ghpull:`18780`: CI: pydocstyle>=5.1.0, flake8-docstrings>=1.4.0 verified to work +* :ghpull:`18200`: Unpin pydocstyle +* :ghpull:`18767`: Turn "How to use Matplotlib in a web application server" into a sphinx-gallery example +* :ghpull:`18765`: Remove some unused tick private attributes. +* :ghpull:`18688`: Shorter property deprecation. +* :ghpull:`18748`: Allow dependabot to check GitHub actions daily +* :ghpull:`18529`: Synchronize view limits of shared axes after setting ticks +* :ghpull:`18575`: Colorbar grid position +* :ghpull:`18744`: DOCS: document log locator's ``numticks`` +* :ghpull:`18687`: Deprecate GraphicsContextPS. +* :ghpull:`18706`: Consistently use 3D, 2D, 1D for dimensionality +* :ghpull:`18702`: _make_norm_from_scale fixes. +* :ghpull:`18558`: Support usetex in date Formatters +* :ghpull:`18493`: MEP22 toolmanager set axes navigate_mode +* :ghpull:`18730`: TST: skip if known-bad version of imagemagick +* :ghpull:`18583`: Support binary comms in nbagg. +* :ghpull:`18728`: Disable mouseover info for NonUniformImage. +* :ghpull:`18710`: Deprecate cla() methods of Axis and Spines in favor of clear() +* :ghpull:`18719`: Added the trace plot of the end point +* :ghpull:`18729`: Use ax.add_image rather than ax.images.append in NonUniformImage example +* :ghpull:`18707`: Use "Return whether ..." docstring for functions returning bool +* :ghpull:`18724`: Remove extra newlines in contour(f) docs. +* :ghpull:`18696`: removed glossary +* :ghpull:`18721`: Remove the use_cmex font fallback mechanism. +* :ghpull:`18680`: wx backend API cleanups. +* :ghpull:`18709`: Use attributes Axes.x/yaxis instead of Axes.get_x/yaxis() +* :ghpull:`18712`: Shorten GraphicsContextWx.get_wxcolour. +* :ghpull:`18708`: Individualize contour and contourf docstrings +* :ghpull:`18663`: fix: keep baseline scale to baseline 0 even if set to None +* :ghpull:`18704`: Fix docstring of Axes.cla() +* :ghpull:`18675`: Merge ParasiteAxesAuxTransBase into ParasiteAxesBase. +* :ghpull:`18651`: Allow Type3 subsetting of otf fonts in pdf backend. +* :ghpull:`17396`: Improve headlessness detection for backend selection. +* :ghpull:`17737`: Deprecate BoxStyle._Base. +* :ghpull:`18655`: Sync SubplotDivider API with SubplotBase API changes. +* :ghpull:`18582`: Shorten mlab tests. +* :ghpull:`18599`: Simplify wx rubberband drawing. +* :ghpull:`18671`: DOC: fix autoscale docstring +* :ghpull:`18637`: BLD: sync build and run time numpy pinning +* :ghpull:`18693`: Also fix tk key mapping, following the same strategy as for gtk. +* :ghpull:`18691`: Cleanup sample_data. +* :ghpull:`18697`: Catch TypeError when validating rcParams types. +* :ghpull:`18537`: Create security policy +* :ghpull:`18356`: ENH: Subfigures +* :ghpull:`18694`: Document limitations on ``@deprecated`` with multiple-inheritance. +* :ghpull:`18669`: Rework checks for old macosx +* :ghpull:`17791`: More accurate handling of unicode/numpad input in gtk3 backends. +* :ghpull:`18679`: Further simplify pgf tmpdir cleanup. +* :ghpull:`18685`: Cleanup pgf examples +* :ghpull:`18682`: Small API cleanups to plot_directive. +* :ghpull:`18686`: Numpydocify setp. +* :ghpull:`18684`: Small simplification to triage_tests.py. +* :ghpull:`17832`: pdf: Support setting URLs on Text objects +* :ghpull:`18674`: Remove accidentally added swapfile. +* :ghpull:`18673`: Small cleanups to parasite axes. +* :ghpull:`18536`: axes3d panning +* :ghpull:`18667`: TST: Lock cache directory during cleanup. +* :ghpull:`18672`: Created Border for color examples +* :ghpull:`18661`: Define GridFinder.{,inv\_}transform_xy as normal methods. +* :ghpull:`18656`: Fix some missing references. +* :ghpull:`18659`: Small simplifications to BboxImage. +* :ghpull:`18511`: feat: StepPatch to take array as baseline +* :ghpull:`18646`: Support activating figures with plt.figure(figure_instance). +* :ghpull:`18370`: Move PostScript Type3 subsetting to pure python. +* :ghpull:`18645`: Simplify Colorbar.set_label, inline Colorbar._edges. +* :ghpull:`18633`: Support linestyle='none' in Patch +* :ghpull:`18527`: Fold ColorbarPatch into Colorbar, deprecate colorbar_factory. +* :ghpull:`17480`: Regenerate background when RectangleSelector active-flag is set back on. +* :ghpull:`18626`: Specify case when parameter is ignored. +* :ghpull:`18634`: Fix typo in warning message. +* :ghpull:`18603`: bugfix #18600 by using the MarkerStyle copy constructor +* :ghpull:`18628`: Remove outdate comment about canvases with no manager attribute. +* :ghpull:`18591`: Deprecate MathTextParser("bitmap") and associated APIs. +* :ghpull:`18617`: Remove special styling of sidebar heading +* :ghpull:`18616`: Improve instructions for building the docs +* :ghpull:`18623`: Provide a 'cursive' font present in Windows' default font set. +* :ghpull:`18579`: Fix stairs() tests +* :ghpull:`18618`: Correctly separate two fantasy font names. +* :ghpull:`18610`: DOCS: optional doc building dependencies +* :ghpull:`18601`: Simplify Rectangle and RegularPolygon. +* :ghpull:`18573`: add_subplot(..., axes_class=...) for more idiomatic mpl_toolkits usage. +* :ghpull:`18605`: Correctly sync state of wx toolbar buttons when triggered by keyboard. +* :ghpull:`18606`: Revert "FIX: pin pytest" +* :ghpull:`18587`: Fix docstring of zaxis_date. +* :ghpull:`18589`: Factor out pdf Type3 glyph drawing. +* :ghpull:`18586`: Text cleanups. +* :ghpull:`18594`: FIX: pin pytest +* :ghpull:`18577`: Random test cleanups +* :ghpull:`18578`: Merge all axisartist axis_direction demos together. +* :ghpull:`18588`: Use get_x/yaxis_transform more. +* :ghpull:`18585`: FIx precision in pie and donut example +* :ghpull:`18564`: Prepare for merging SubplotBase into AxesBase. +* :ghpull:`15127`: ENH/API: improvements to register_cmap +* :ghpull:`18576`: DOC: prefer colormap over color map +* :ghpull:`18340`: Colorbar grid postion +* :ghpull:`18568`: Added Reporting to code_of_conduct.md +* :ghpull:`18555`: Convert _math_style_dict into an Enum. +* :ghpull:`18567`: Replace subplot(ijk) calls by subplots(i, j) +* :ghpull:`18554`: Replace some usages of plt.subplot() by plt.subplots() in tests +* :ghpull:`18556`: Accept same types to errorevery as markevery +* :ghpull:`15932`: Use test cache for test result images too. +* :ghpull:`18557`: DOC: Add an option to disable Google Analytics. +* :ghpull:`18560`: Remove incorrect override of pcolor/contour in parasite axes. +* :ghpull:`18566`: Use fig, ax = plt.subplots() in tests (part 2) +* :ghpull:`18553`: Use fig, ax = plt.subplots() in tests +* :ghpull:`11748`: get_clip_path checks for nan +* :ghpull:`8987`: Tick formatter does not support grouping with locale +* :ghpull:`18552`: Change \*subplot(111, ...) to \*subplot(...) as 111 is the default. +* :ghpull:`18189`: FIX: Add get/set methods for 3D collections +* :ghpull:`18430`: FIX: do not reset ylabel ha when changing position +* :ghpull:`18515`: Remove deprecated backend code. +* :ghpull:`17935`: MNT: improve error messages on bad pdf metadata input +* :ghpull:`18525`: Add Text3D position getter/setter +* :ghpull:`18542`: CLEANUP: validate join/cap style centrally +* :ghpull:`18501`: TST: Add test for _repr_html_ +* :ghpull:`18528`: Deprecate TextArea minimumdescent. +* :ghpull:`18543`: Documentation improvements for stairs() +* :ghpull:`18531`: Unit handling improvements +* :ghpull:`18523`: Don't leak file paths into PostScript metadata +* :ghpull:`18526`: Templatize _image.resample to deduplicate it. +* :ghpull:`18522`: Remove mlab, toolkits, and misc deprecations +* :ghpull:`18516`: Remove deprecated font-related things. +* :ghpull:`18535`: Add a code of conduct link to github +* :ghpull:`17521`: Remove font warning when legend is added while using Tex +* :ghpull:`18517`: Include kerning when outputting pdf strings. +* :ghpull:`18521`: Inline some helpers in ColorbarBase. +* :ghpull:`18512`: Private api2 +* :ghpull:`18519`: Correctly position text with nonzero descent with afm fonts / ps output. +* :ghpull:`18513`: Remove Locator.autoscale. +* :ghpull:`18497`: Merge v3.3.x into master +* :ghpull:`18502`: Remove the deprecated matplotlib.cm.revcmap() +* :ghpull:`18506`: Inline ScalarFormatter._formatSciNotation. +* :ghpull:`18455`: Fix BoundingBox in EPS files. +* :ghpull:`18275`: feat: StepPatch +* :ghpull:`18507`: Fewer "soft" dependencies on LaTeX packages. +* :ghpull:`18378`: Deprecate public access to many mathtext internals. +* :ghpull:`18494`: Move cbook._check_in_list() to _api.check_in_list() +* :ghpull:`18423`: 2-D array RGB and RGBA values not understood in plt.plot() +* :ghpull:`18492`: Fix doc build failure due to #18440 +* :ghpull:`18435`: New environment terminal language +* :ghpull:`18456`: Reuse InsetLocator to make twinned axes follow their parents. +* :ghpull:`18440`: List existing rcParams in rcParams docstring. +* :ghpull:`18453`: FIX: allow manually placed axes in constrained_layout +* :ghpull:`18473`: Correct link to widgets examples +* :ghpull:`18466`: Remove unnecessary autoscale handling in hist(). +* :ghpull:`18465`: Don't modify bottom argument in place in stacked histograms. +* :ghpull:`18468`: Cleanup multiple_yaxis_with_spines example. +* :ghpull:`18463`: Improve formatting of defaults in docstrings. +* :ghpull:`6268`: ENH: support alpha arrays in collections +* :ghpull:`18449`: Remove the private Axes._set_position. +* :ghpull:`18460`: DOC: example gray level in 'Specifying Colors' tutorial +* :ghpull:`18426`: plot directive: caption-option +* :ghpull:`18444`: Support doubleclick in webagg/nbagg +* :ghpull:`12518`: Example showing scale-invariant angle arc +* :ghpull:`18446`: Normalize properties passed to ToolHandles. +* :ghpull:`18445`: Warn if an animation is gc'd before doing anything. +* :ghpull:`18452`: Move Axes ``__repr__`` from Subplot to AxesBase. +* :ghpull:`15374`: Replace _prod_vectorized by @-multiplication. +* :ghpull:`13643`: RecangleSelector constructor does not handle marker_props +* :ghpull:`18403`: DOC: Remove related topics entries from the sidebar +* :ghpull:`18421`: Move {get,set}_{x,y}label to _AxesBase. +* :ghpull:`18429`: DOC: fix date example +* :ghpull:`18353`: DOCS: describe shared axes behavior with units +* :ghpull:`18420`: Always strip out date in postscript's test_savefig_to_stringio. +* :ghpull:`18422`: Decrease output when running ``pytest -s``. +* :ghpull:`18418`: Cleanup menu example +* :ghpull:`18419`: Avoid demo'ing passing kwargs to gca(). +* :ghpull:`18372`: DOC: Fix various missing references and typos +* :ghpull:`18400`: Clarify argument name in constrained_layout error message +* :ghpull:`18384`: Clarification in ArtistAnimation docstring +* :ghpull:`17892`: Add earlier color validation +* :ghpull:`18367`: Support horizontalalignment in TextArea/AnchoredText. +* :ghpull:`18362`: DOC: Add some types to Returns entries. +* :ghpull:`18365`: move canvas focus after toomanager initialization +* :ghpull:`18360`: Add example for specifying figure size in different units +* :ghpull:`18341`: DOCS: add action items to PR template +* :ghpull:`18349`: Remove redundant angles in ellipse demo. +* :ghpull:`18145`: Created a parameter fontset that can be used in each Text element +* :ghpull:`18344`: More nouns/imperative forms in docs. +* :ghpull:`18308`: Synchronize units change in Axis.set_units for shared axis +* :ghpull:`17494`: Rewrite of constrained_layout.... +* :ghpull:`16646`: update colorbar.py make_axes_gridspec +* :ghpull:`18306`: Fix configure subplots +* :ghpull:`17509`: Fix ``swap_if_landscape`` call in backend_ps +* :ghpull:`18323`: Deleted "Our Favorite Recipes" section and moved the examples. +* :ghpull:`18128`: Change several deprecated symbols in _macosx.m +* :ghpull:`18251`: Merge v3.3.x into master +* :ghpull:`18329`: Change default keymap in toolmanager example. +* :ghpull:`18330`: Dedent rst list. +* :ghpull:`18286`: Fix imshow to work with subclasses of ndarray. +* :ghpull:`18320`: Make Colorbar outline into a Spine. +* :ghpull:`18316`: Safely import pyplot if a GUI framework is already running. +* :ghpull:`18321`: Capture output of CallbackRegistry exception test. +* :ghpull:`17900`: Add getters and _repr_html_ for over/under/bad values of Colormap objects. +* :ghpull:`17930`: Fix errorbar property cycling to match plot. +* :ghpull:`18290`: Remove unused import to fix flake8. +* :ghpull:`16818`: Dedupe implementations of configure_subplots(). +* :ghpull:`18284`: TkTimer interval=0 workaround +* :ghpull:`17901`: DOC: Autoreformating of backend/\*.py +* :ghpull:`17291`: Normalize gridspec ratios to lists in the setter. +* :ghpull:`18226`: Use CallbackRegistry in Widgets and some related cleanup +* :ghpull:`18203`: Force locator and formatter inheritence +* :ghpull:`18279`: boxplot: Add conf_intervals reference to notch docs. +* :ghpull:`18276`: Fix autoscaling to exclude inifinite data limits when possible. +* :ghpull:`18261`: Migrate tk backend tests into subprocesses +* :ghpull:`17961`: DOCS: Remove How-to: Contributing +* :ghpull:`18201`: Remove mpl.colors deprecations for 3.4 +* :ghpull:`18223`: Added example on how to make packed bubble charts +* :ghpull:`18264`: Fix broken links in doc build. +* :ghpull:`8031`: Add errorbars to mplot3d +* :ghpull:`18187`: Add option to create horizontally-oriented stem plots +* :ghpull:`18250`: correctly autolabel Documentation and Maintenance issues +* :ghpull:`18161`: Add more specific GitHub issue templates +* :ghpull:`18181`: Replace ttconv by plain python for pdf subsetting +* :ghpull:`17371`: add context manager functionality to ion and ioff +* :ghpull:`17789`: Tk backend improvements +* :ghpull:`15532`: Resolve 'text ignores rotational part of transformation' (#698) +* :ghpull:`17851`: Fix Axes3D.add_collection3d issues +* :ghpull:`18205`: Hat graph example +* :ghpull:`6168`: #5856: added option to create vertically-oriented stem plots +* :ghpull:`18202`: Remove mpl.testing deprecations for 3.4 +* :ghpull:`18081`: Support scale in ttf composite glyphs +* :ghpull:`18199`: Some cleanup on TickedStroke +* :ghpull:`18190`: Use ``super()`` more in backends +* :ghpull:`18193`: Allow savefig to save SVGs on FIPS enabled systems #18192 +* :ghpull:`17802`: fix FigureManagerTk close behavior if embedded in Tk App +* :ghpull:`15458`: TickedStroke, a stroke style with ticks useful for depicting constraints +* :ghpull:`18178`: DOC: clarify that display space coordinates are not stable +* :ghpull:`18172`: allow webAgg to report middle click events +* :ghpull:`17578`: Search for minus of any font size to get height of tex result +* :ghpull:`17546`: ``func`` argument in ``legend_elements`` with non-monotonically increasing functions +* :ghpull:`17684`: Deprecate passing bytes to FT2Font.set_text. +* :ghpull:`17500`: Tst improve memleak +* :ghpull:`17669`: Small changes to svg font embedding details +* :ghpull:`18095`: Error on unexpected kwargs in scale classes +* :ghpull:`18106`: Copy docstring description from Axes.legend() to Figure.legend() +* :ghpull:`18002`: Deprecate various vector-backend-specific mathtext helpers. +* :ghpull:`18006`: Fix ToolManager inconsistencies with regular toolbar +* :ghpull:`18004`: Typos and docs for mathtext fonts. +* :ghpull:`18133`: DOC: Update paths for moved API/what's new fragments +* :ghpull:`18122`: Document and test legend argument parsing +* :ghpull:`18124`: Fix FuncAnimation._draw_frame exception and testing +* :ghpull:`18125`: pdf: Convert operator list to an Enum. +* :ghpull:`18123`: Cleanup figure title example +* :ghpull:`18121`: Improve rasterization demo +* :ghpull:`18012`: Add explanatory text for rasterization demo +* :ghpull:`18103`: Support data reference for hexbin() parameter C +* :ghpull:`17826`: Add pause() and resume() methods to the base Animation class +* :ghpull:`18090`: Privatize cbook.format_approx. +* :ghpull:`18080`: Reduce numerical precision in Type 1 fonts +* :ghpull:`18044`: Super-ify parts of the code base, part 3 +* :ghpull:`18087`: Add a note on working around limit expansion of set_ticks() +* :ghpull:`18071`: Remove deprecated animation code +* :ghpull:`17822`: Check for float values for min/max values to ax{v,h}line +* :ghpull:`18069`: Remove support for multiple-color strings in to_rgba_array +* :ghpull:`18070`: Remove rcsetup deprecations +* :ghpull:`18073`: Remove disable_internet.py +* :ghpull:`18075`: typo in usetex.py example +* :ghpull:`18043`: Super-ify parts of the code base, part 2 +* :ghpull:`18062`: Bump matplotlib.patches coverage +* :ghpull:`17269`: Fix ConciseDateFormatter when plotting a range included in a second +* :ghpull:`18063`: Remove un-used trivial setters and getters +* :ghpull:`18025`: add figpager as a third party package +* :ghpull:`18046`: Discourage references in section headings. +* :ghpull:`18042`: scatter: Raise if unexpected type of ``s`` argument. +* :ghpull:`18028`: Super-ify parts of the code base, part 1 +* :ghpull:`18029`: Remove some unused imports. +* :ghpull:`18018`: Cache realpath resolution in font_manager. +* :ghpull:`18013`: Use argumentless ``super()`` more. +* :ghpull:`17988`: add test with -OO +* :ghpull:`17993`: Make inset_axes and secondary_axis picklable. +* :ghpull:`17992`: Shorten tight_bbox. +* :ghpull:`18003`: Deprecate the unneeded Fonts.destroy. +* :ghpull:`16457`: Build lognorm/symlognorm from corresponding scales. +* :ghpull:`17966`: Fix some words +* :ghpull:`17803`: Simplify projection-of-point-on-polyline in contour.py. +* :ghpull:`17699`: raise RuntimeError appropriately for animation update func +* :ghpull:`17954`: Remove another overspecified latex geometry. +* :ghpull:`17948`: Sync Cairo's usetex measurement with base class. +* :ghpull:`17788`: Tighten a bit the RendererAgg API. +* :ghpull:`12443`: Warn in colorbar() when mappable.axes != figure.gca(). +* :ghpull:`17926`: Deprecate hatch patterns with invalid values +* :ghpull:`17922`: Rewrite the barcode example +* :ghpull:`17890`: Properly use thin space after math text operator +* :ghpull:`16090`: Change pcolormesh snapping (fixes alpha colorbar/grid issues) [AGG] +* :ghpull:`17842`: Move "Request a new feature" from How-to to Contributing +* :ghpull:`17897`: Force origin='upper' in pyplot.specgram +* :ghpull:`17929`: Improve hatch demo +* :ghpull:`17927`: Remove unnecessary file save during test +* :ghpull:`14896`: Updated doc in images.py by adding direct link to 24-bit stink bug png +* :ghpull:`17909`: frame_format to support all listed by animation writers +* :ghpull:`13569`: Style cleanup to pyplot. +* :ghpull:`17924`: Remove the example "Easily creating subplots" +* :ghpull:`17869`: FIX: new date rcParams weren't being evaluated +* :ghpull:`17921`: Added density and combination hatching examples +* :ghpull:`17159`: Merge consecutive rasterizations +* :ghpull:`17895`: Use indexed color for PNG images in PDF files when possible +* :ghpull:`17894`: DOC: Numpydoc format. +* :ghpull:`17884`: Created Hatch marker styles Demo for Example Gallery +* :ghpull:`17347`: ENH: reuse oldgridspec is possible... +* :ghpull:`17915`: Document that set_ticks() increases view limits if necessary +* :ghpull:`17902`: Fix figure size in path effects guide +* :ghpull:`17899`: Add missing space in cairo error +* :ghpull:`17888`: Add _repr_png_ and _repr_html_ to Colormap objects. +* :ghpull:`17830`: Fix BoundaryNorm for multiple colors and one region +* :ghpull:`17883`: Remove Python 3.6 compatibility shims +* :ghpull:`17889`: Minor doc fixes +* :ghpull:`17879`: Link to style-file example page in style tutorial +* :ghpull:`17876`: Fix description of subplot2grid arguments +* :ghpull:`17856`: Clarify plotnonfinite parameter docs of scatter() +* :ghpull:`17843`: Add fullscreen toggle support to WxAgg backend +* :ghpull:`17022`: ENH: add rcParam for ConciseDate and interval_multiples +* :ghpull:`17799`: Deduplicate attribute docs of ContourSet and its derived classes +* :ghpull:`17847`: Remove overspecified latex geometry. +* :ghpull:`17662`: Mnt drop py36 +* :ghpull:`17845`: Fix size of donate button +* :ghpull:`17825`: Add quick-link buttons for contributing +* :ghpull:`17837`: Remove "Reporting a bug or submitting a patch" from How-to +* :ghpull:`17828`: API: treat xunits=None and yunits=None as "default" +* :ghpull:`17839`: Avoid need to lock in dvi generation, to avoid deadlocks. +* :ghpull:`17824`: Improve categorical converter error message +* :ghpull:`17834`: Keep using a single dividers LineCollection instance in colorbar. +* :ghpull:`17838`: Prefer colorbar(ScalarMappable(...)) to ColorbarBase in tutorial. +* :ghpull:`17836`: More precise axes section names in docs +* :ghpull:`17835`: Colorbar cleanups. +* :ghpull:`17727`: FIX: properly handle dates when intmult is true +* :ghpull:`15617`: Dev docs update +* :ghpull:`17819`: Fix typos in tight layout guide +* :ghpull:`17806`: Set colorbar label only in set_label. +* :ghpull:`17265`: Mnt rearrange next api again +* :ghpull:`17808`: Improve docstring of ColorbarBase.set_label() +* :ghpull:`17723`: Deprecate FigureCanvas.{get,set}_window_title. +* :ghpull:`17798`: Fix overindented bullet/enumerated lists. +* :ghpull:`17767`: Allow list of hatches to {bar, barh} +* :ghpull:`17749`: Deprecate ``FancyBboxPatch(..., boxstyle="custom", bbox_transmuter=...)`` +* :ghpull:`17783`: DOC: point to bbox static "constructor" functions in set_position +* :ghpull:`17782`: MNT: update mailmap +* :ghpull:`17776`: Changes in the image for test_load_from_url +* :ghpull:`17750`: Soft-deprecate mutation_aspect=None. +* :ghpull:`17780`: Reorganize colorbar docstrings. +* :ghpull:`17778`: Fix whatsnew confusing typo. +* :ghpull:`17748`: Don't use bezier helpers in axisartist. +* :ghpull:`17700`: Remove remnants of macosx old-style toolbar. +* :ghpull:`17753`: Support location="left"/"top" for gridspec-based colorbars. +* :ghpull:`17761`: Update hard-coded results in artist tutorial +* :ghpull:`17728`: Move Win32_{Get,Set}ForegroundWindow to c_internal_utils. +* :ghpull:`17754`: Small cleanups to contour() code. +* :ghpull:`17751`: Deprecate dpi_cor property of FancyArrowPatch. +* :ghpull:`15941`: FontManager fixes. +* :ghpull:`17661`: Issue #17659: set tick color and tick labelcolor independently from rcParams +* :ghpull:`17389`: Don't duplicate docstrings of pyplot-level cmap setters. +* :ghpull:`17555`: Set Win32 AppUserModelId to fix taskbar icons. +* :ghpull:`17726`: Clarify docs of box_aspect() +* :ghpull:`17704`: Remove "created-by-matplotlib" comment in svg output. +* :ghpull:`17697`: Add description examples/pyplots/pyplot simple.py +* :ghpull:`17694`: CI: Only skip devdocs deploy if PR is to this repo. +* :ghpull:`17691`: ci: Print out reasons for not deploying docs. +* :ghpull:`17099`: Make Spines accessable by the attributes. + +Issues (204): + +* :ghissue:`19701`: Notebook plotting regression in 3.4.0rc* +* :ghissue:`19754`: add space in python -mpip +* :ghissue:`18364`: ``Axes3d`` attaches itself to a figure, where as ``Axes`` does not +* :ghissue:`19700`: Setting pickradius regression in 3.4.0rc +* :ghissue:`19594`: code of conduct link 404s +* :ghissue:`19576`: duplicate pick events firing +* :ghissue:`19560`: segfault due to font objects when multi-threading +* :ghissue:`19598`: Axes order changed in 3.4.0rc1 +* :ghissue:`19631`: subplot mosaic 1 element list +* :ghissue:`19581`: Missing kerning for single-byte strings in PDF +* :ghissue:`17769`: interactive figure close with wxpython 4.1 causes freeze / crash (segfault?) +* :ghissue:`19427`: Fix mistake in documentation +* :ghissue:`19624`: Cannot add colorbar to figure after pickle +* :ghissue:`19544`: Regression in 3.4.0rc1 in creating ListedColormap from a set +* :ghissue:`5855`: plt.step(..., where="auto") +* :ghissue:`19474`: Memory leak with CallbackRegistry +* :ghissue:`19345`: legend is eating up huge amounts of memory +* :ghissue:`19066`: plt.scatter, error with NaN values and edge color +* :ghissue:`19432`: Unexpected change in behavior in plt.subplot +* :ghissue:`18020`: Scatter3D: facecolor or color to "none" leads to an error +* :ghissue:`18939`: Warn re: Axes3D constructor behavior change in mpl3.4 +* :ghissue:`19128`: webagg reports incorrect values for non-alphanumeric key events on non-qwerty keyboards +* :ghissue:`16558`: Request: for non-interactive backends make fig.canvas.draw() force the render +* :ghissue:`19234`: tick labels displaced vertically with text.usetex and xcolor +* :ghissue:`18407`: pgf backend no longer supports fig.draw +* :ghissue:`2298`: axes.xmargin/ymargin rcParam behaves differently than pyplot.margins() +* :ghissue:`19473`: Animations in Tkinter window advance non-uniformly +* :ghissue:`8688`: document moved examples +* :ghissue:`9553`: Display warning on out-of-date documentation websites +* :ghissue:`9556`: Examples page version is out of date +* :ghissue:`12374`: Examples in docs should be redirected to latest version number +* :ghissue:`19486`: Figure.tight_layout() raises MatplotlibDeprecationWarning +* :ghissue:`19445`: axline transform support broke axline in loglog scale +* :ghissue:`19178`: mathtext \lim is vertically misaligned +* :ghissue:`19446`: Better document and error handle third dimension in pyplot.text() positional argument +* :ghissue:`8790`: Inconsistent doc vs behavior for RendererXXX.draw_markers +* :ghissue:`18815`: Patch3D object does not return correct face color with get_facecolor +* :ghissue:`19152`: Automatically Aligned Labels outside Figure with Constrained Layout in Exported File +* :ghissue:`18934`: stairs() crashes with no values and one edge +* :ghissue:`11296`: Image in github repo does not match matplotlib.org (breaks image tutorial) +* :ghissue:`18699`: Issue with downloading stinkbug for "Image Tutorial" +* :ghissue:`19405`: TypeError constructor returned NULL in wayland session +* :ghissue:`18962`: Table CSS needs cleanup +* :ghissue:`19417`: CI failing on numpy... +* :ghissue:`17849`: Problems caused by changes to logic of scatter coloring in matplotlib 3.3.0.rc1 +* :ghissue:`18648`: Drop support for directly imread()ing urls. +* :ghissue:`19366`: Current CI doc builds fail +* :ghissue:`19372`: matplotlib.axes.Axes.indicate_inset default label value is incompatible with LaTeX +* :ghissue:`17100`: Is it a better solution to access one of the spines by class attribute? +* :ghissue:`17375`: Proposal: add_subfigs.... +* :ghissue:`19339`: constrained_layout + fixed-aspect axes + bbox_inches="tight" +* :ghissue:`19308`: Reduce whitespace in Choosing Colormaps tutorial plots +* :ghissue:`18832`: MNT: Remove AxesStack and deprecated behavior of reuse of existing axes with same arguments +* :ghissue:`19084`: Arrow coordinates slightly off when used with annotation text +* :ghissue:`17765`: PGF xelatex can't find fonts in special-character paths +* :ghissue:`19274`: Missing marker in documentation of plot +* :ghissue:`18241`: LaTeX overset: unknown symbol +* :ghissue:`19292`: Non interpolated placeholder value in docstring. +* :ghissue:`18119`: Can no longer deepcopy LogNorm objects on master +* :ghissue:`8665`: Noninteger Bases in mathtext sqrt +* :ghissue:`19243`: matplotlib doesn't build with qhull-2020.2 +* :ghissue:`19275`: Double specifications of plot attributes +* :ghissue:`15066`: Feature request: stem3 +* :ghissue:`19209`: Segfault when trying to create gigapixel image with agg backend +* :ghissue:`4321`: clabel ticks and axes limits with eps zoom output +* :ghissue:`16376`: ``SymLogNorm`` and ``SymLogScale`` give inconsistent results.... +* :ghissue:`19239`: _make_norm_from_scale needs to process values +* :ghissue:`16552`: Scatter autoscaling still has issues with log scaling and zero values +* :ghissue:`18417`: Documentation issue template should ask for matplotlib version +* :ghissue:`19206`: matplotlib.cbook.Grouper: Example raise exception: +* :ghissue:`19203`: Date Tick Labels example +* :ghissue:`18581`: Add a check in check_figures_equal that the test did not accidentally plot on non-fixture figures +* :ghissue:`18563`: Create a RangeSlider widget +* :ghissue:`19099`: axisartist axis_direction bug +* :ghissue:`19171`: 3D surface example bug for non-square grid +* :ghissue:`18112`: set_{x,y,z}bound 3d limits are not persistent upon interactive rotation +* :ghissue:`19078`: _update_patch_limits should not use CLOSEPOLY verticies for updating +* :ghissue:`16123`: test_dpi_ratio_change fails on Windows/Qt5Agg +* :ghissue:`15796`: [DOC] PDF build of matplotlib own documentation crashes with LaTeX error "too deeply nested" +* :ghissue:`19091`: 3D Axes don't work in SubFigures +* :ghissue:`7238`: better document how to configure artists for picking +* :ghissue:`11147`: FR: add a supxlabel and supylabel as the suptitle function which are already exist +* :ghissue:`17417`: tutorial on how autoscaling works +* :ghissue:`18917`: Spy displays nothing for full arrays +* :ghissue:`18562`: Allow slider valstep to be arraylike +* :ghissue:`18942`: AnnotationBbox errors with kwargs +* :ghissue:`11472`: Mention predefined keyboard shortcuts in the docs on event-handling +* :ghissue:`18898`: wrong bounds checking in streamplot start_points +* :ghissue:`18974`: Contour label demo would benefit from some more info and/or references. +* :ghissue:`17708`: Mention rasterized option in more methods +* :ghissue:`18826`: Pgf plots with pdflatex broken +* :ghissue:`18959`: Add sphinx-gallery cross ref instructions to documenting guide +* :ghissue:`18926`: Font not installed, unclear warning +* :ghissue:`18891`: SVG animation doesn't work in HTMLWriter due to wrong type +* :ghissue:`18222`: It is painful as a new user, to figure out what AxesSubplot is +* :ghissue:`16153`: gap size for contour labels is poorly estimated +* :ghissue:`17910`: Improve accessibility of form controls in HTML widgets +* :ghissue:`18273`: Surprising behavior of shared axes with categorical units +* :ghissue:`18731`: Compact string notation for subplot_mosaic +* :ghissue:`18221`: Add example of keys to explore 3D data +* :ghissue:`18882`: Incorrect version requirement message from setup.py +* :ghissue:`18491`: Mostly unused glossary still exists in our docs +* :ghissue:`18548`: add_subplot(..., axes_cls=...) +* :ghissue:`8249`: Bug in mpl_connect(): On Windows, with the wx backend, arrow keys are not reported +* :ghissue:`15609`: [SPRINT] Update Named Colors Example +* :ghissue:`18800`: Log-scale ticker fails at 1e-323 +* :ghissue:`18392`: ``scatter()``: ``edgecolor`` takes precedence over ``edgecolors`` +* :ghissue:`18301`: "How to use Matplotlib in a web application server" should be made an example +* :ghissue:`18386`: Path3DCollection.set_color(self, c) does not change the color of scatter points. +* :ghissue:`8946`: Axes with sharex can have divergent axes after setting tick markers +* :ghissue:`2294`: tex option not respected by date x-axis +* :ghissue:`4382`: use new binary comm in nbagg +* :ghissue:`17088`: ``projection`` kwarg could be better documented. +* :ghissue:`18717`: Tick formatting issues on horizontal histogram with datetime on 3.3.2 +* :ghissue:`12636`: Characters doesn't display correctly when figure saved as pdf with a custom font +* :ghissue:`18377`: Matplotlib picks a headless backend on Linux if Wayland is available but X11 isn't +* :ghissue:`13199`: Examples that use private APIs +* :ghissue:`18662`: Inconsistent setting of axis limits with autoscale=False +* :ghissue:`18690`: Class deprecation machinery and mixins +* :ghissue:`18510`: Build fails on OS X: wrong minimum version +* :ghissue:`18641`: Conversion cache cleaning is broken with xdist +* :ghissue:`15614`: named color examples need borders +* :ghissue:`5519`: The linestyle 'None', ' ' and '' not supported by PathPatch. +* :ghissue:`17487`: Polygon selector with useblit=True - polygon dissapears +* :ghissue:`17476`: RectangleSelector fails to clear itself after being toggled inactive and then back to active. +* :ghissue:`18600`: plt.errorbar raises error when given marker= +* :ghissue:`18355`: Optional components required to build docs aren't documented +* :ghissue:`18428`: small bug in the mtplotlib gallery +* :ghissue:`4438`: inconsistent behaviour of the errorevery option in pyplot.errorbar() to the markevery keyword +* :ghissue:`5823`: pleas dont include the Google Analytics tracking in the off-line doc +* :ghissue:`13035`: Path3DCollection from 3D scatter cannot set_color +* :ghissue:`9725`: scatter - set_facecolors is not working on Axes3D +* :ghissue:`3370`: Patch3DCollection doesn't update color after calling set_color +* :ghissue:`18427`: yaxis.set_label_position("right") resets "horizontalalignment" +* :ghissue:`3129`: super-ify the code base +* :ghissue:`17518`: Plotting legend throws error "font family ['serif'] not found. Falling back to DejaVu Sans" +* :ghissue:`18282`: Bad interaction between kerning and non-latin1 characters in pdf output +* :ghissue:`6669`: [Feature request] Functions for "manually" plotting histograms +* :ghissue:`18411`: 2-D array RGB and RGBA values not understood in plt.plot() +* :ghissue:`18404`: Double-click events are not recognised in Jupyter notebook +* :ghissue:`12027`: marker_props is never used in the constructor of RectangleSelector +* :ghissue:`18438`: Warn when a non-started animation is gc'ed. +* :ghissue:`11259`: Symbols appear as streaks with usetex=True, times font and PDF backend +* :ghissue:`18345`: Specify what sharex and sharey do... +* :ghissue:`18082`: Feature Request: Non overlapping Bubble Plots +* :ghissue:`568`: Support error bars on 3D plots +* :ghissue:`17865`: Earlier validation of color inputs +* :ghissue:`18363`: ha="right" breaks AnchoredText placement. +* :ghissue:`11050`: keyboard shortcuts don't get registered using the experimental toolmanager with qt +* :ghissue:`17906`: Set mathtext.fontset per element +* :ghissue:`18311`: Subplot scatter plot with categorical data on y-axis with 'sharey=True' option overwrites the y-axis labels +* :ghissue:`10304`: No link to shared axes for Axis.set_units +* :ghissue:`17712`: constrained_layout fails on suptitle+colorbars+some figure sizes +* :ghissue:`14638`: colorbar.make_axes doesn't anchor in constrained_layout +* :ghissue:`18299`: New configure_subplots behaves badly on TkAgg backend +* :ghissue:`18300`: Remove the examples category "Our Favorite Recipies" +* :ghissue:`18077`: Imshow breaks if given a unyt_array input +* :ghissue:`7074`: Using a linestyle cycler with plt.errorbar results in strange plots +* :ghissue:`18236`: FuncAnimation fails to display with interval 0 on Tkagg backend +* :ghissue:`8107`: invalid command name "..._on_timer" in FuncAnimation for (too) small interval +* :ghissue:`18272`: Add CI Intervall to boxplot notch documentation +* :ghissue:`18137`: axhspan() in empty plots changes the xlimits of plots sharing the X axis +* :ghissue:`18246`: test_never_update is flaky +* :ghissue:`5856`: Horizontal stem plot +* :ghissue:`18160`: Add feature request template +* :ghissue:`17197`: Missing character upon savefig() with Free Serif font +* :ghissue:`17013`: Request: provide a contextmanager for ioff or allow plt.figure(draw_on_create=False) +* :ghissue:`17537`: hat graphs need an example... +* :ghissue:`17755`: mplot3d: add_collection3d issues +* :ghissue:`18192`: Cannot save SVG file with FIPS compliant Python +* :ghissue:`17574`: Vertical alignment of tick labels containing minus in font size other than 10 with usetex=True +* :ghissue:`18097`: Feature Request: Allow hexbin to use a string for parameter C to refer to column in data (DataFrame) +* :ghissue:`17689`: Add pause/resume methods to Animation baseclass +* :ghissue:`16087`: Error with greek letters in pdf export when using usetex=True and mathptmx +* :ghissue:`17136`: set_ticks() changes view limits of the axis +* :ghissue:`12198`: axvline incorrectly tries to handle unitized ymin, ymax +* :ghissue:`9139`: Python3 matplotlib 2.0.2 with Times New Roman misses unicode minus sign in pdf +* :ghissue:`5970`: pyplot.scatter raises obscure error when mistakenly passed a third string param +* :ghissue:`17936`: documenattion and behavior do not match for suppressing (PDF) metadata +* :ghissue:`17932`: latex textrm does not work in Cairo backend +* :ghissue:`17714`: Universal fullscreen command +* :ghissue:`4584`: ColorbarBase draws edges in slightly wrong positions. +* :ghissue:`17878`: flipping of imshow in specgram +* :ghissue:`6118`: consider using qtpy for qt abstraction layer +* :ghissue:`17908`: rcParams restrictions on frame_formats are out of sync with supported values (HTMLWriter) +* :ghissue:`17867`: datetime plotting broken on master +* :ghissue:`16810`: Docs do not build in parallel +* :ghissue:`17918`: Extend hatch reference +* :ghissue:`17149`: Rasterization creates multiple bitmap elements and large file sizes +* :ghissue:`17855`: Add Hatch Example to gallery +* :ghissue:`15821`: Should constrained_layout work as plt.figure() argument? +* :ghissue:`15616`: Colormaps should have a ``_repr_html_`` that is an image of the colormap +* :ghissue:`17579`: ``BoundaryNorm`` yield a ``ZeroDivisionError: division by zero`` +* :ghissue:`17652`: NEP 29 : Stop support fro Python 3.6 soon ? +* :ghissue:`11095`: Repeated plot calls with xunits=None throws exception +* :ghissue:`17733`: Rename "array" (and perhaps "fields") section of Axes API +* :ghissue:`15610`: Link to most recent DevDocs when installing from Master Source +* :ghissue:`17817`: (documentation, possible first-timer bug) Typo and grammar on Legends and Annotations for tight layout guide page +* :ghissue:`17804`: Setting the norm on imshow object removes colorbar ylabel +* :ghissue:`17758`: bar, barh should take a list of hatches like it does of colors +* :ghissue:`17746`: Antialiasing with colorbars? +* :ghissue:`17659`: Enhancement: Set tick and ticklabel colors separately from matplotlib style file +* :ghissue:`17144`: Wrong icon on windows task bar for figure windows +* :ghissue:`2870`: Wrong symbols from a TrueType font diff --git a/doc/users/prev_whats_new/github_stats_3.4.1.rst b/doc/users/prev_whats_new/github_stats_3.4.1.rst new file mode 100644 index 000000000000..0819a6850a3e --- /dev/null +++ b/doc/users/prev_whats_new/github_stats_3.4.1.rst @@ -0,0 +1,55 @@ +.. _github-stats-3-4-1: + +GitHub statistics for 3.4.1 (Mar 31, 2021) +========================================== + +GitHub statistics for 2021/03/26 (tag: v3.4.0) - 2021/03/31 + +These lists are automatically generated, and may be incomplete or contain duplicates. + +We closed 7 issues and merged 20 pull requests. +The full list can be seen `on GitHub `__ + +The following 6 authors contributed 43 commits. + +* Antony Lee +* Elliott Sales de Andrade +* Jody Klymak +* Thomas A Caswell +* Tim Hoffmann +* Xianxiang Li + +GitHub issues and pull requests: + +Pull Requests (20): + +* :ghpull:`19834`: Backport PR #19812: FIX: size and color rendering for Path3DCollection +* :ghpull:`19833`: Backport PR #19811 on branch v3.4.x (Fix Inkscape cleanup at exit on Windows.) +* :ghpull:`19812`: FIX: size and color rendering for Path3DCollection +* :ghpull:`19811`: Fix Inkscape cleanup at exit on Windows. +* :ghpull:`19816`: Fix legend of colour-mapped scatter plots. +* :ghpull:`19830`: Backport PR #19824 on branch v3.4.x (Access pdf annotations while inside pikepdf.Pdf context manager.) +* :ghpull:`19829`: Backport PR #19822 on branch v3.4.x (Clarify default backend selection doc.) +* :ghpull:`19827`: Backport PR #19805 on branch v3.4.x (Fix suptitle out of layout) +* :ghpull:`19824`: Access pdf annotations while inside pikepdf.Pdf context manager. +* :ghpull:`19805`: Fix suptitle out of layout +* :ghpull:`19823`: Backport PR #19814 on branch v3.4.x (Fix positioning of annotation arrow.) +* :ghpull:`19820`: Backport PR #19817 on branch v3.4.x (Fix antialiasing with old pycairo/cairocffi.) +* :ghpull:`19814`: Fix positioning of annotation arrow. +* :ghpull:`19817`: Fix antialiasing with old pycairo/cairocffi. +* :ghpull:`19818`: Backport PR #19784 on branch v3.4.x (FIX errorbar problem with fillstyle) +* :ghpull:`19784`: FIX errorbar problem with fillstyle +* :ghpull:`19815`: Backport PR #19793 on branch v3.4.x (Fix non existent URIs) +* :ghpull:`19793`: Fix non existent URIs +* :ghpull:`19783`: Backport PR #19719 on branch v3.4.x (Respect antialiasing settings in cairo backends as well.) +* :ghpull:`19719`: Respect antialiasing settings in cairo backends as well. + +Issues (7): + +* :ghissue:`19779`: BUG: matplotlib 3.4.0 -- Scatter with colormap and legend gives TypeError: object of type 'NoneType' has no len() +* :ghissue:`19787`: Marker sizes in Axes3D scatter plot are changing all the time +* :ghissue:`19809`: Tests that use "image_comparison" fail to cleanup on Windows +* :ghissue:`19803`: Suptitle positioning messed up in 3.4.0 +* :ghissue:`19785`: Starting point of annotation arrows has changed in 3.4.0 +* :ghissue:`19776`: Errorbars with yerr fail when fillstyle is specified +* :ghissue:`19780`: redirect_from extension breaks latex build diff --git a/doc/users/prev_whats_new/github_stats_3.4.2.rst b/doc/users/prev_whats_new/github_stats_3.4.2.rst new file mode 100644 index 000000000000..22b4797c2fc2 --- /dev/null +++ b/doc/users/prev_whats_new/github_stats_3.4.2.rst @@ -0,0 +1,153 @@ +.. _github-stats-3-4-2: + +GitHub statistics for 3.4.2 (May 08, 2021) +========================================== + +GitHub statistics for 2021/03/31 (tag: v3.4.1) - 2021/05/08 + +These lists are automatically generated, and may be incomplete or contain duplicates. + +We closed 21 issues and merged 97 pull requests. +The full list can be seen `on GitHub `__ + +The following 13 authors contributed 138 commits. + +* AkM-2018 +* Antony Lee +* David Stansby +* Elliott Sales de Andrade +* hannah +* Ian Thomas +* Jann Paul Mattern +* Jody Klymak +* pwohlhart +* richardsheridan +* Thomas A Caswell +* Tim Hoffmann +* Xianxiang Li + +GitHub issues and pull requests: + +Pull Requests (97): + +* :ghpull:`20184`: Backport PR #20147 on branch v3.4.x (DOC: add example of labelling axes) +* :ghpull:`20181`: Backport PR #20171 on branch v3.4.x (Remove unsupported arguments from tricontourf documentation) +* :ghpull:`20180`: Backport PR #19876 on branch v3.4.x (FIX: re-order unit conversion and mask array coercion) +* :ghpull:`20171`: Remove unsupported arguments from tricontourf documentation +* :ghpull:`19876`: FIX: re-order unit conversion and mask array coercion +* :ghpull:`20178`: Backport PR #20150 on branch v3.4.x +* :ghpull:`20172`: Backport PR #20161 on branch v3.4.x (Fix resetting grid visibility) +* :ghpull:`20161`: Fix resetting grid visibility +* :ghpull:`20167`: Backport PR #20146 on branch v3.4.x (Don't clip clip paths to Figure bbox.) +* :ghpull:`20166`: Backport PR #19978 on branch v3.4.x (fixed bug in CenteredNorm, issue #19972) +* :ghpull:`20146`: Don't clip clip paths to Figure bbox. +* :ghpull:`19978`: fixed bug in CenteredNorm, issue #19972 +* :ghpull:`20160`: Backport PR #20148 on branch v3.4.x (FIX: MouseButton representation in boilerplate generated signatures) +* :ghpull:`20148`: FIX: MouseButton representation in boilerplate generated signatures +* :ghpull:`20152`: Backport PR #20145 on branch v3.4.x (Fix broken link to ggplot in docs) +* :ghpull:`20139`: Backport PR #20135 on branch v3.4.x (Add tricontour/tricontourf arguments(corner_mask, vmin vmax, antialiased, nchunk, hatches) documentation) +* :ghpull:`20135`: Add tricontour/tricontourf arguments(corner_mask, vmin vmax, antialiased, nchunk, hatches) documentation +* :ghpull:`20136`: Backport PR #19959 on branch v3.4.x (Bugfix Tk start_event_loop) +* :ghpull:`19959`: Bugfix Tk start_event_loop +* :ghpull:`20128`: Backport PR #20123 on branch v3.4.x (Ensure that Matplotlib is importable even if there's no HOME.) +* :ghpull:`20123`: Ensure that Matplotlib is importable even if there's no HOME. +* :ghpull:`20009`: Fix removal of shared polar axes. +* :ghpull:`20104`: Backport PR #19686 on branch v3.4.x (Declare sphinxext.redirect_from parallel_read_safe) +* :ghpull:`19686`: Declare sphinxext.redirect_from parallel_read_safe +* :ghpull:`20098`: Backport PR #20096 on branch v3.4.x (Ignore errors for sip with no setapi.) +* :ghpull:`20096`: Ignore errors for sip with no setapi. +* :ghpull:`20087`: Backport PR #20083 on branch v3.4.x (Revert "Temporarily switch intersphinx to latest pytest.") +* :ghpull:`20085`: Backport PR #20082 on branch v3.4.x (Fix bar_label for bars with nan values) +* :ghpull:`20082`: Fix bar_label for bars with nan values +* :ghpull:`20076`: Backport PR #20062 on branch v3.4.x ([DOC] Add top-level .. module:: definition for matplotlib) +* :ghpull:`20043`: Backport PR #20041 on branch v3.4.x (Clarify docs for stackplot.) +* :ghpull:`20041`: Clarify docs for stackplot. +* :ghpull:`20039`: Backport PR #20037 on branch v3.4.x (Don't generate wheels unusable on PyPy7.3.{0,1}.) +* :ghpull:`20037`: Don't generate wheels unusable on PyPy7.3.{0,1}. +* :ghpull:`20033`: Backport PR #20031 on branch v3.4.x (Cleanup widget examples) +* :ghpull:`20031`: Cleanup widget examples +* :ghpull:`20022`: Backport PR #19949 on branch v3.4.x (FIX: subfigure indexing error) +* :ghpull:`19949`: FIX: subfigure indexing error +* :ghpull:`20018`: Backport PR #20017 on branch v3.4.x (FIX typos in imshow_extent.py) +* :ghpull:`20017`: FIX typos in imshow_extent.py +* :ghpull:`20015`: Backport PR #19962 on branch v3.4.x (Dev install troubleshooting) +* :ghpull:`19962`: Dev install troubleshooting +* :ghpull:`20002`: Backport PR #19995 on branch v3.4.x (Fix valinit argument to RangeSlider) +* :ghpull:`20004`: Backport PR #19999 on branch v3.4.x (DOC: add note about axes order to docstring) +* :ghpull:`19998`: Backport PR #19964 on branch v3.4.x (FIX: add subplot_mosaic axes in the order the user gave them to us) +* :ghpull:`19999`: DOC: add note about axes order to docstring +* :ghpull:`19997`: Backport PR #19992 on branch v3.4.x (Minor fixes to polar locator docstrings.) +* :ghpull:`19995`: Fix valinit argument to RangeSlider +* :ghpull:`19964`: FIX: add subplot_mosaic axes in the order the user gave them to us +* :ghpull:`19993`: Backport PR #19983 on branch v3.4.x (Fix handling of "d" glyph in backend_ps.) +* :ghpull:`19992`: Minor fixes to polar locator docstrings. +* :ghpull:`19991`: Backport PR #19987 on branch v3.4.x (Fix set_thetalim((min, max)).) +* :ghpull:`19976`: Backport PR #19970 on branch v3.4.x (Initialize members of PathClipper and check for m_has_init) +* :ghpull:`19983`: Fix handling of "d" glyph in backend_ps. +* :ghpull:`19987`: Fix set_thetalim((min, max)). +* :ghpull:`19970`: Initialize members of PathClipper and check for m_has_init +* :ghpull:`19973`: Backport PR #19971 on branch v3.4.x (Fix missing closing bracket in docs) +* :ghpull:`19971`: Fix missing closing bracket in docs +* :ghpull:`19966`: Backport PR #19963 on branch v3.4.x (test_StrCategoryLocator using parameterized plotter) +* :ghpull:`19965`: Backport PR #19961 on branch v3.4.x (FIX: subfigure tightbbox) +* :ghpull:`19963`: test_StrCategoryLocator using parameterized plotter +* :ghpull:`19961`: FIX: subfigure tightbbox +* :ghpull:`19953`: Backport PR #19919 on branch v3.4.x (Copy errorbar style normalization to 3D) +* :ghpull:`19919`: Copy errorbar style normalization to 3D +* :ghpull:`19950`: Backport PR #19948 on branch v3.4.x (Allow numpy arrays to be used as elinewidth) +* :ghpull:`19948`: Allow numpy arrays to be used as elinewidth +* :ghpull:`19944`: Backport PR #19939 on branch v3.4.x (add highlight-text to the third party packages list) +* :ghpull:`19921`: Backport PR #19913 on branch v3.4.x (Minor docstring improvement for set_aspect()) +* :ghpull:`19920`: Backport PR #19903 on branch v3.4.x (Fix textbox cursor color, set its linewidth.) +* :ghpull:`19913`: Minor docstring improvement for set_aspect() +* :ghpull:`19903`: Fix textbox cursor color, set its linewidth. +* :ghpull:`19917`: Backport PR #19911 on branch v3.4.x (Shorten "how-to draw order") +* :ghpull:`19916`: Backport PR #19888 on branch v3.4.x (Fix errorbar drawstyle) +* :ghpull:`19911`: Shorten "how-to draw order" +* :ghpull:`19888`: Fix errorbar drawstyle +* :ghpull:`19910`: Backport PR #19895 on branch v3.4.x (Added PyPI info to third party page) +* :ghpull:`19895`: Added PyPI info to third party page +* :ghpull:`19896`: Backport PR #19893 on branch v3.4.x (Remove Howto: Plot numpy.datetime64 values) +* :ghpull:`19893`: Remove Howto: Plot numpy.datetime64 values +* :ghpull:`19886`: Backport PR #19881 on branch v3.4.x (Remove two sections from Plotting FAQ) +* :ghpull:`19877`: Backport PR #19863 on branch v3.4.x (Cleanup docstrings related to interactive mode) +* :ghpull:`19881`: Remove two sections from Plotting FAQ +* :ghpull:`19885`: Backport PR #19883 on branch v3.4.x (Small cleanups to FAQ.) +* :ghpull:`19883`: Small cleanups to FAQ. +* :ghpull:`19878`: Backport PR #19867 on branch v3.4.x (Remove "Use show()" from how-to ) +* :ghpull:`19875`: Backport PR #19868 on branch v3.4.x (Remove "Install from source" from Installing FAQ) +* :ghpull:`19867`: Remove "Use show()" from how-to +* :ghpull:`19863`: Cleanup docstrings related to interactive mode +* :ghpull:`19868`: Remove "Install from source" from Installing FAQ +* :ghpull:`19874`: Backport PR #19847 on branch v3.4.x (Reformat references (part 2)) +* :ghpull:`19847`: Reformat references (part 2) +* :ghpull:`19865`: Backport PR #19860 on branch v3.4.x (Move "howto interpreting box plots" to boxplot docstring) +* :ghpull:`19860`: Move "howto interpreting box plots" to boxplot docstring +* :ghpull:`19862`: Backport PR #19861 on branch v3.4.x (Remove FAQ Installing - Linux notes) +* :ghpull:`19861`: Remove FAQ Installing - Linux notes +* :ghpull:`18060`: Correctly handle 'none' facecolors in do_3d_projection +* :ghpull:`19846`: Backport PR #19788 on branch v3.4.x (Reformat references) + +Issues (21): + +* :ghissue:`19871`: Matplotlib >= v3.3.3 breaks with pandas.plotting.register_matplotlib_converters(), ax.pcolormesh(), and datetime objects +* :ghissue:`20149`: KeyError: 'gridOn' in axis.py when axis.tick_params() is used with reset = True +* :ghissue:`20127`: Zooming on a contour plot with clipping results in bad clipping +* :ghissue:`19972`: CenteredNorm with halfrange raises exception when passed to imshow +* :ghissue:`19940`: Tkagg event loop throws error on window close +* :ghissue:`20122`: Run in a system service / without configuration +* :ghissue:`19989`: Removal of y-shared polar axes causes crash at draw time +* :ghissue:`19988`: Removal of x-shared polar axes causes crash +* :ghissue:`20040`: AttributeError: module 'sip' has no attribute 'setapi' +* :ghissue:`20058`: bar_label fails with nan data values +* :ghissue:`20036`: Minor changes about stackplot documentation +* :ghissue:`20014`: undefined symbol: PyPyUnicode_ReadChar +* :ghissue:`19947`: Figure.subfigures dont show/update correctly +* :ghissue:`19960`: Failed to init RangeSlider with valinit attribute +* :ghissue:`19736`: subplot_mosaic axes are not added in consistent order +* :ghissue:`19979`: Blank EPS figures if plot contains 'd' +* :ghissue:`19938`: unuseful deprecation warning figbox +* :ghissue:`19958`: subfigures missing bbox_inches attribute in inline backend +* :ghissue:`19936`: Errorbars elinewidth raise error when numpy array +* :ghissue:`19879`: Using "drawstyle" raises AttributeError in errorbar, when yerr is specified. +* :ghissue:`19454`: I cannot import matplotlib.pyplot as plt diff --git a/doc/users/prev_whats_new/github_stats_3.4.3.rst b/doc/users/prev_whats_new/github_stats_3.4.3.rst new file mode 100644 index 000000000000..b248bf69b6ef --- /dev/null +++ b/doc/users/prev_whats_new/github_stats_3.4.3.rst @@ -0,0 +1,133 @@ +.. _github-stats-3-4-3: + +GitHub statistics for 3.4.3 (August 21, 2021) +============================================= + +GitHub statistics for 2021/05/08 - 2021/08/12 (tag: v3.4.2) + +These lists are automatically generated, and may be incomplete or contain duplicates. + +We closed 22 issues and merged 69 pull requests. +The full list can be seen `on GitHub `__ + +The following 20 authors contributed 95 commits. + +* Antony Lee +* David Stansby +* Diego +* Diego Leal Petrola +* Diego Petrola +* Elliott Sales de Andrade +* Eric Firing +* Frank Sauerburger +* Greg Lucas +* Ian Hunt-Isaak +* Jash Shah +* Jody Klymak +* Jouni K. Seppänen +* MichaÅ‚ Górny +* sandipanpanda +* Slava Ostroukh +* Thomas A Caswell +* Tim Hoffmann +* Viacheslav Ostroukh +* Xianxiang Li + +GitHub issues and pull requests: + +Pull Requests (69): + +* :ghpull:`20830`: Backport PR #20826 on branch v3.4.x (Fix clear of Axes that are shared.) +* :ghpull:`20826`: Fix clear of Axes that are shared. +* :ghpull:`20823`: Backport PR #20817 on branch v3.4.x (Make test_change_epoch more robust.) +* :ghpull:`20817`: Make test_change_epoch more robust. +* :ghpull:`20820`: Backport PR #20771 on branch v3.4.x (FIX: tickspacing for subfigures) +* :ghpull:`20771`: FIX: tickspacing for subfigures +* :ghpull:`20777`: FIX: dpi and scatter for subfigures now correct +* :ghpull:`20787`: Backport PR #20786 on branch v3.4.x (Fixed typo in _constrained_layout.py (#20782)) +* :ghpull:`20786`: Fixed typo in _constrained_layout.py (#20782) +* :ghpull:`20763`: Backport PR #20761 on branch v3.4.x (Fix suplabel autopos) +* :ghpull:`20761`: Fix suplabel autopos +* :ghpull:`20751`: Backport PR #20748 on branch v3.4.x (Ensure _static directory exists before copying CSS.) +* :ghpull:`20748`: Ensure _static directory exists before copying CSS. +* :ghpull:`20713`: Backport PR #20710 on branch v3.4.x (Fix tests with Inkscape 1.1.) +* :ghpull:`20687`: Enable PyPy wheels for v3.4.x +* :ghpull:`20710`: Fix tests with Inkscape 1.1. +* :ghpull:`20696`: Backport PR #20662 on branch v3.4.x (Don't forget to disable autoscaling after interactive zoom.) +* :ghpull:`20662`: Don't forget to disable autoscaling after interactive zoom. +* :ghpull:`20683`: Backport PR #20645 on branch v3.4.x (Fix leak if affine_transform is passed invalid vertices.) +* :ghpull:`20645`: Fix leak if affine_transform is passed invalid vertices. +* :ghpull:`20642`: Backport PR #20629 on branch v3.4.x (Add protection against out-of-bounds read in ttconv) +* :ghpull:`20643`: Backport PR #20597 on branch v3.4.x +* :ghpull:`20629`: Add protection against out-of-bounds read in ttconv +* :ghpull:`20597`: Fix TTF headers for type 42 stix font +* :ghpull:`20624`: Backport PR #20609 on branch v3.4.x (FIX: fix figbox deprecation) +* :ghpull:`20609`: FIX: fix figbox deprecation +* :ghpull:`20594`: Backport PR #20590 on branch v3.4.x (Fix class docstrings for Norms created from Scales.) +* :ghpull:`20590`: Fix class docstrings for Norms created from Scales. +* :ghpull:`20587`: Backport PR #20584: FIX: do not simplify path in LineCollection.get_s… +* :ghpull:`20584`: FIX: do not simplify path in LineCollection.get_segments +* :ghpull:`20578`: Backport PR #20511 on branch v3.4.x (Fix calls to np.ma.masked_where) +* :ghpull:`20511`: Fix calls to np.ma.masked_where +* :ghpull:`20568`: Backport PR #20565 on branch v3.4.x (FIX: PILLOW asarray bug) +* :ghpull:`20566`: Backout pillow=8.3.0 due to a crash +* :ghpull:`20565`: FIX: PILLOW asarray bug +* :ghpull:`20503`: Backport PR #20488 on branch v3.4.x (FIX: Include 0 when checking lognorm vmin) +* :ghpull:`20488`: FIX: Include 0 when checking lognorm vmin +* :ghpull:`20483`: Backport PR #20480 on branch v3.4.x (Fix str of empty polygon.) +* :ghpull:`20480`: Fix str of empty polygon. +* :ghpull:`20478`: Backport PR #20473 on branch v3.4.x (_GSConverter: handle stray 'GS' in output gracefully) +* :ghpull:`20473`: _GSConverter: handle stray 'GS' in output gracefully +* :ghpull:`20456`: Backport PR #20453 on branch v3.4.x (Remove ``Tick.apply_tickdir`` from 3.4 deprecations.) +* :ghpull:`20441`: Backport PR #20416 on branch v3.4.x (Fix missing Patch3DCollection._z_markers_idx) +* :ghpull:`20416`: Fix missing Patch3DCollection._z_markers_idx +* :ghpull:`20417`: Backport PR #20395 on branch v3.4.x (Pathing issue) +* :ghpull:`20395`: Pathing issue +* :ghpull:`20404`: Backport PR #20403: FIX: if we have already subclassed mixin class ju… +* :ghpull:`20403`: FIX: if we have already subclassed mixin class just return +* :ghpull:`20383`: Backport PR #20381 on branch v3.4.x (Prevent corrections and completions in search field) +* :ghpull:`20307`: Backport PR #20154 on branch v3.4.x (ci: Bump Ubuntu to 18.04 LTS.) +* :ghpull:`20285`: Backport PR #20275 on branch v3.4.x (Fix some examples that are skipped in docs build) +* :ghpull:`20275`: Fix some examples that are skipped in docs build +* :ghpull:`20267`: Backport PR #20265 on branch v3.4.x (Legend edgecolor face) +* :ghpull:`20265`: Legend edgecolor face +* :ghpull:`20260`: Fix legend edgecolor face +* :ghpull:`20259`: Backport PR #20248 on branch v3.4.x (Replace pgf image-streaming warning by error.) +* :ghpull:`20248`: Replace pgf image-streaming warning by error. +* :ghpull:`20241`: Backport PR #20212 on branch v3.4.x (Update span_selector.py) +* :ghpull:`20212`: Update span_selector.py +* :ghpull:`19980`: Tidy up deprecation messages in ``_subplots.py`` +* :ghpull:`20234`: Backport PR #20225 on branch v3.4.x (FIX: correctly handle ax.legend(..., legendcolor='none')) +* :ghpull:`20225`: FIX: correctly handle ax.legend(..., legendcolor='none') +* :ghpull:`20232`: Backport PR #19636 on branch v3.4.x (Correctly check inaxes for multicursor) +* :ghpull:`20228`: Backport PR #19849 on branch v3.4.x (FIX DateFormatter for month names when usetex=True) +* :ghpull:`19849`: FIX DateFormatter for month names when usetex=True +* :ghpull:`20154`: ci: Bump Ubuntu to 18.04 LTS. +* :ghpull:`20186`: Backport PR #19975 on branch v3.4.x (CI: remove workflow to push commits to macpython/matplotlib-wheels) +* :ghpull:`19975`: CI: remove workflow to push commits to macpython/matplotlib-wheels +* :ghpull:`19636`: Correctly check inaxes for multicursor + +Issues (22): + +* :ghissue:`20219`: Regression: undocumented change of behaviour in mpl 3.4.2 with axis ticks direction +* :ghissue:`20721`: ax.clear() adds extra ticks, un-hides shared-axis tick labels +* :ghissue:`20765`: savefig re-scales xticks and labels of some (but not all) subplots +* :ghissue:`20782`: [Bug]: _supylabel get_in_layout() typo? +* :ghissue:`20747`: [Bug]: _copy_css_file assumes that the _static directory already exists +* :ghissue:`20617`: tests fail with new inkscape +* :ghissue:`20519`: Toolbar zoom doesn't change autoscale status for versions 3.2.0 and above +* :ghissue:`20628`: Out-of-bounds read leads to crash or broken TrueType fonts +* :ghissue:`20612`: Broken EPS for Type 42 STIX +* :ghissue:`19982`: regression for 3.4.x - ax.figbox replacement incompatible to all version including 3.3.4 +* :ghissue:`19938`: unuseful deprecation warning figbox +* :ghissue:`16400`: Inconsistent behavior between Normalizers when input is Dataframe +* :ghissue:`20583`: Lost class descriptions since 3.4 docs +* :ghissue:`20551`: set_segments(get_segments()) makes lines coarse +* :ghissue:`20560`: test_png is failing +* :ghissue:`20487`: test_huge_range_log is failing... +* :ghissue:`20472`: test_backend_pgf.py::test_xelatex[pdf] - ValueError: invalid literal for int() with base 10: b'ate missing from Resources. [...] +* :ghissue:`20328`: Path.intersects_path sometimes returns incorrect values +* :ghissue:`20258`: Using edgecolors='face' with stackplot causes value error when using plt.legend() +* :ghissue:`20200`: examples/widgets/span_selector.py is brittle +* :ghissue:`20231`: MultiCursor bug +* :ghissue:`19836`: Month names not set as text when using usetex diff --git a/doc/users/prev_whats_new/github_stats_3.5.0.rst b/doc/users/prev_whats_new/github_stats_3.5.0.rst new file mode 100644 index 000000000000..70d599f10c5d --- /dev/null +++ b/doc/users/prev_whats_new/github_stats_3.5.0.rst @@ -0,0 +1,1292 @@ +.. _github-stats-3-5-0: + +GitHub statistics for 3.5.0 (Nov 15, 2021) +========================================== + +GitHub statistics for 2021/03/26 (tag: v3.4.0) - 2021/11/15 + +These lists are automatically generated, and may be incomplete or contain duplicates. + +We closed 187 issues and merged 939 pull requests. +The full list can be seen `on GitHub `__ + +The following 144 authors contributed 3406 commits. + +* Aaron Rogers +* Abhinav Sagar +* Adrian Price-Whelan +* Adrien F. Vincent +* ain-soph +* Aitik Gupta +* Akiomi Kamakura +* AkM-2018 +* Andrea PIERRÉ +* andthum +* Antony Lee +* Antti Soininen +* apodemus +* astromancer +* Bruno Beltran +* Carlos Cerqueira +* Casper da Costa-Luis +* ceelo777 +* Christian Baumann +* dan +* Dan Zimmerman +* David Matos +* David Poznik +* David Stansby +* dependabot[bot] +* Diego Leal Petrola +* Dmitriy Fishman +* Ellert van der Velden +* Elliott Sales de Andrade +* Engjell Avdiu +* Eric Firing +* Eric Larson +* Eric Prestat +* Ewan Sutherland +* Felix Nößler +* Fernando +* fourpoints +* Frank Sauerburger +* Gleb Fedorov +* Greg Lucas +* hannah +* Hannes Breytenbach +* Hans Meine +* Harshal Prakash Patankar +* harupy +* Harutaka Kawamura +* Hildo Guillardi Júnior +* Holtz Yan +* Hood +* Ian Hunt-Isaak +* Ian Thomas +* ianhi +* Illviljan +* ImportanceOfBeingErnest +* Isha Mehta +* iury simoes-sousa +* Jake Bowhay +* Jakub Klus +* Jan-Hendrik Müller +* Janakarajan Natarajan +* Jann Paul Mattern +* Jash Shah +* Jay Joshi +* jayjoshi112711 +* jeffreypaul15 +* Jerome F. Villegas +* Jerome Villegas +* Jesus Briales +* Jody Klymak +* Jonathan Yong +* Joschua Conrad +* Joschua-Conrad +* Jouni K. Seppänen +* K-Monty +* katrielester +* kdpenner +* Kent +* Kent Gauen +* kentcr +* kir0ul +* kislovskiy +* KIU Shueng Chuan +* KM Goh +* Konstantin Popov +* kyrogon +* Leeh Peter +* Leo Singer +* lgfunderburk +* Liam Toney +* luz paz +* luzpaz +* Madhav Humagain +* MalikIdreesHasa +* Marat Kopytjuk +* Marco Rigobello +* Marco Salathe +* Markus Wesslén +* martinRenou +* Matthias Bussonnier +* MeeseeksMachine +* MichaÅ‚ Górny +* Mihai Anton +* Navid C. Constantinou +* Nico Schlömer +* Phil Nagel +* Philip Schiff +* Philipp Nagel +* pwohlhart +* Péter Leéh +* Quentin Peter +* Ren Pang +* rgbmrc +* Richard Barnes +* richardsheridan +* Rike-Benjamin Schuppner +* Roberto Toro +* Ruth Comer +* ryahern +* Ryan May +* Sam Van Kooten +* sandipanpanda +* Simon Hoxbro +* Slava Ostroukh +* Stefan Appelhoff +* Stefanie Molin +* takimata +* tdpetrou +* theOehrly +* Thomas A Caswell +* Tim Hoffmann +* tohc1 +* Tom Charrett +* Tom Neep +* Tomas Hrnciar +* Tortar +* Tranquilled +* Vagrant Cascadian +* Viacheslav Ostroukh +* Vishnu V K +* Xianxiang Li +* Yannic Schroeder +* Yo Yehudi +* Zexi +* znstrider + +GitHub issues and pull requests: + +Pull Requests (939): + +* :ghpull:`21645`: Backport PR #21628 on branch v3.5.x (Fix METH_VARARGS method signatures ) +* :ghpull:`21644`: Backport PR #21640 on branch v3.5.x (DOC: remove sample_plots from tutorials) +* :ghpull:`21628`: Fix METH_VARARGS method signatures +* :ghpull:`21640`: DOC: remove sample_plots from tutorials +* :ghpull:`21636`: Backport PR #21604 on branch v3.5.x (Fix centre square rectangle selector part 1) +* :ghpull:`21604`: Fix centre square rectangle selector part 1 +* :ghpull:`21633`: Backport PR #21501 on branch v3.5.x (Refix for pyparsing compat.) +* :ghpull:`21606`: BLD: limit support of pyparsing to <3 +* :ghpull:`21501`: Refix for pyparsing compat. +* :ghpull:`21624`: Backport PR #21621 on branch v3.5.x (Fix GhostScript error handling types) +* :ghpull:`21625`: Backport PR #21568 on branch v3.5.x (Enhancing support for tex and datetimes) +* :ghpull:`21568`: Enhancing support for tex and datetimes +* :ghpull:`21621`: Fix GhostScript error handling types +* :ghpull:`21623`: Backport PR #21619 on branch v3.5.x (Revert "Pin sphinx to fix sphinx-gallery") +* :ghpull:`21619`: Revert "Pin sphinx to fix sphinx-gallery" +* :ghpull:`21618`: Backport PR #21617 on branch v3.5.x (FIX: Make sure we do not over-write eps short cuts) +* :ghpull:`21622`: Backport PR #21350 on branch v3.5.x (Remove plot_gallery setting from conf.py) +* :ghpull:`21617`: FIX: Make sure we do not over-write eps short cuts +* :ghpull:`21616`: Backport PR #21613 on branch v3.5.x (SEC/DOC update supported versions) +* :ghpull:`21615`: Backport PR #21607 on branch v3.5.x (DOC: link to cheatsheets site, not github repo) +* :ghpull:`21614`: Backport PR #21609 on branch v3.5.x (Fix documentation link with renaming ``voxels`` to ``voxelarray``) +* :ghpull:`21613`: SEC/DOC update supported versions +* :ghpull:`21607`: DOC: link to cheatsheets site, not github repo +* :ghpull:`21609`: Fix documentation link with renaming ``voxels`` to ``voxelarray`` +* :ghpull:`21605`: Backport PR #21317 on branch v3.5.x (Move label hiding rectilinear-only check into _label_outer_{x,y}axis.) +* :ghpull:`21317`: Move label hiding rectilinear-only check into _label_outer_{x,y}axis. +* :ghpull:`21602`: Backport PR #21586 on branch v3.5.x (Defer enforcement of hatch validation) +* :ghpull:`21601`: Backport PR #21530 on branch v3.5.x (Fix interrupting GTK on plain Python) +* :ghpull:`21603`: Backport PR #21596 on branch v3.5.x (Pin sphinx to fix sphinx-gallery) +* :ghpull:`21586`: Defer enforcement of hatch validation +* :ghpull:`21530`: Fix interrupting GTK on plain Python +* :ghpull:`21397`: Support for pre 2.7.1 freetype savannah versions +* :ghpull:`21599`: Backport PR #21592 on branch v3.5.x ([BUG in 3.5.0rc1] - Anatomy of a Figure has the legend in the wrong spot) +* :ghpull:`21587`: Backport PR #21581 on branch v3.5.x (Fix RangeSlider.reset) +* :ghpull:`21592`: [BUG in 3.5.0rc1] - Anatomy of a Figure has the legend in the wrong spot +* :ghpull:`21596`: Pin sphinx to fix sphinx-gallery +* :ghpull:`21577`: Backport PR #21527 on branch v3.5.x (Add more 3.5 release notes) +* :ghpull:`21527`: Add more 3.5 release notes +* :ghpull:`21573`: Backport PR #21570 on branch v3.5.x (Raise correct exception out of Spines.__getattr__) +* :ghpull:`21563`: Backport PR #21559 on branch v3.5.x (Fix eventplot units) +* :ghpull:`21560`: Backport PR #21553 on branch v3.5.x (Fix check for manager presence in blocking_input.) +* :ghpull:`21561`: Backport PR #21555 on branch v3.5.x (MNT: reject more possibly unsafe strings in validate_cycler) +* :ghpull:`21555`: MNT: reject more possibly unsafe strings in validate_cycler +* :ghpull:`21553`: Fix check for manager presence in blocking_input. +* :ghpull:`21559`: Fix eventplot units +* :ghpull:`21543`: Backport PR #21443 on branch v3.5.x (FIX: re-instate ability to have position in axes) +* :ghpull:`21550`: Ignore transOffset if no offsets passed to Collection +* :ghpull:`21443`: FIX: re-instate ability to have position in axes +* :ghpull:`21531`: Backport PR #21491 on branch v3.5.x (Relocate inheritance diagram to the top of the document) +* :ghpull:`21491`: Relocate inheritance diagram to the top of the document +* :ghpull:`21504`: Backport PR #21481 on branch v3.5.x (FIX: spanning subfigures) +* :ghpull:`21481`: FIX: spanning subfigures +* :ghpull:`21483`: Backport PR #21387 on branch v3.5.x (Fix path simplification of closed loops) +* :ghpull:`21486`: Backport PR #21478 on branch v3.5.x (Fix GTK4 embedding example) +* :ghpull:`21497`: Backport PR #21484 on branch v3.5.x (Replacement for imread should return an array) +* :ghpull:`21484`: Replacement for imread should return an array +* :ghpull:`21495`: Backport PR #21492 on branch v3.5.x (added parameter documentation for MultiCursor) +* :ghpull:`21493`: Backport PR #21488 on branch v3.5.x (Added to contour docs) +* :ghpull:`21492`: added parameter documentation for MultiCursor +* :ghpull:`21488`: Added to contour docs +* :ghpull:`21478`: Fix GTK4 embedding example +* :ghpull:`21387`: Fix path simplification of closed loops +* :ghpull:`21479`: Backport PR #21472 on branch v3.5.x (Clarify set_parse_math documentation.) +* :ghpull:`21472`: Clarify set_parse_math documentation. +* :ghpull:`21471`: Backport PR #21470 on branch v3.5.x (Hide fully transparent latex text in PS output) +* :ghpull:`21470`: Hide fully transparent latex text in PS output +* :ghpull:`21469`: Backport PR #21468 on branch v3.5.x (Fix some typos in examples) +* :ghpull:`21468`: Fix some typos in examples +* :ghpull:`21461`: Backport #21429 from jklymak/doc-use-mpl-sphinx +* :ghpull:`21464`: Backport PR #21460 on branch v3.5.x (Clip slider init marker to slider track.) +* :ghpull:`21460`: Clip slider init marker to slider track. +* :ghpull:`21458`: Backport: #21429 from jklymak/doc-use-mpl-sphinx +* :ghpull:`21454`: Fix error with pyparsing 3 for 3.5.x +* :ghpull:`21459`: Backport PR #21423 on branch v3.5.x (Change CircleCI job title to "Rendered docs") +* :ghpull:`21423`: Change CircleCI job title to "Rendered docs" +* :ghpull:`21457`: Backport PR #21455 on branch v3.5.x (Hide note linking to the download section at the bottom of galleries) +* :ghpull:`21456`: Backport PR #21453 on branch v3.5.x (Cleanup index.rst sectioning) +* :ghpull:`21455`: Hide note linking to the download section at the bottom of galleries +* :ghpull:`21453`: Cleanup index.rst sectioning +* :ghpull:`21224`: DOC: Nav-bar: Add icon linking to contents +* :ghpull:`21451`: Backport PR #21445 on branch v3.5.x (Mnt pin pyparsing) +* :ghpull:`21429`: DOC: use mpl-sphinx-theme for navbar, social, logo +* :ghpull:`21450`: Backport PR #21449 on branch v3.5.x (Less verbose install info on index page) +* :ghpull:`21449`: Less verbose install info on index page +* :ghpull:`21446`: Also exclude pyparsing 3.0.0 in setup.py. +* :ghpull:`21445`: Mnt pin pyparsing +* :ghpull:`21439`: Backport PR #21420 on branch v3.5.x (Enable Python 3.10 wheel building on all systems) +* :ghpull:`21438`: Backport PR #21427 on branch v3.5.x (Update docstrings of get_{view,data}_interval.) +* :ghpull:`21437`: Backport PR #21435 on branch v3.5.x (DOC: Fix selection of parameter names in HTML theme) +* :ghpull:`21420`: Enable Python 3.10 wheel building on all systems +* :ghpull:`21427`: Update docstrings of get_{view,data}_interval. +* :ghpull:`21435`: DOC: Fix selection of parameter names in HTML theme +* :ghpull:`21428`: Backport PR #21422 on branch v3.5.x (More doc reorganization) +* :ghpull:`21422`: More doc reorganization +* :ghpull:`21421`: Backport PR #21411 on branch v3.5.x (Document webagg in docs.) +* :ghpull:`21419`: Backport PR #21251 on branch v3.5.x (DOC: more site re-org) +* :ghpull:`21411`: Document webagg in docs. +* :ghpull:`21251`: DOC: more site re-org +* :ghpull:`21416`: Backport PR #21326 on branch v3.5.x (Add ability to scale BBox with just x or y values) +* :ghpull:`21418`: Backport PR #21414 on branch v3.5.x (Support pathological tmpdirs in TexManager.) +* :ghpull:`21410`: Backport PR #20591 on branch v3.5.x (Webagg backend: get rid of tornado) +* :ghpull:`21414`: Support pathological tmpdirs in TexManager. +* :ghpull:`21326`: Add ability to scale BBox with just x or y values +* :ghpull:`20591`: Webagg backend: get rid of tornado +* :ghpull:`21406`: Backport PR #21212 on branch v3.5.x (Fix set_size_inches on HiDPI and also GTK4) +* :ghpull:`21405`: Backport PR #21365 on branch v3.5.x (Convert macosx backend to use device_pixel_ratio) +* :ghpull:`18274`: Improve initial macosx device scale +* :ghpull:`21212`: Fix set_size_inches on HiDPI and also GTK4 +* :ghpull:`21365`: Convert macosx backend to use device_pixel_ratio +* :ghpull:`21372`: Backport PR #20708 on branch v3.5.x (Describe possible need for loading the 'lmodern' package when using PGF files) +* :ghpull:`20708`: Describe possible need for loading the 'lmodern' package when using PGF files +* :ghpull:`21359`: Add GHA testing whether files were added and deleted in the same PR. +* :ghpull:`21360`: Backport PR #21335 on branch v3.5.x (DOC: move usage tutorial info to Users guide rst) +* :ghpull:`21363`: Backport PR #21287 on branch v3.5.x (Inherit more docstrings.) +* :ghpull:`21361`: Fix flake8 from #21335 +* :ghpull:`21287`: Inherit more docstrings. +* :ghpull:`21335`: DOC: move usage tutorial info to Users guide rst +* :ghpull:`21358`: Backport PR #21357 on branch v3.5.x (DOC: remove test from README.rst) +* :ghpull:`21357`: DOC: remove test from README.rst +* :ghpull:`21350`: Remove plot_gallery setting from conf.py +* :ghpull:`21340`: Backport PR #21332 on branch v3.5.x (Fix default value for ``shading`` in``pyplot.pcolormesh`` docstring) +* :ghpull:`21332`: Fix default value for ``shading`` in``pyplot.pcolormesh`` docstring +* :ghpull:`21334`: Backport PR #21330 on branch v3.5.x (Fix medical image caption in tutorial) +* :ghpull:`21329`: Backport PR #21321 on branch v3.5.x (DOC Update description of ax.contour method, resolves #21310) +* :ghpull:`21330`: Fix medical image caption in tutorial +* :ghpull:`21321`: DOC Update description of ax.contour method, resolves #21310 +* :ghpull:`21327`: Backport PR #21313 on branch v3.5.x (DOC: Minimal getting started page) +* :ghpull:`21313`: DOC: Minimal getting started page +* :ghpull:`21316`: Backport PR #21312 on branch v3.5.x (Update link to Agg website) +* :ghpull:`21312`: Update link to Agg website +* :ghpull:`21308`: Backport PR #21307 on branch v3.5.x (Use in-tree builds for PyPy wheels) +* :ghpull:`21307`: Use in-tree builds for PyPy wheels +* :ghpull:`21306`: Backport PR #21303 on branch v3.5.x (Pin macOS to 10.15 for wheels) +* :ghpull:`21305`: Backport PR #21286 on branch v3.5.x (Clarify FigureBase.tight_bbox as different from all other artists.) +* :ghpull:`21286`: Clarify FigureBase.tight_bbox as different from all other artists. +* :ghpull:`21302`: Backport PR #21291 on branch v3.5.x (DOC: Bump to the sphinx-gallery release) +* :ghpull:`21304`: Backport PR #21294 on branch v3.5.x (Disable blitting on GTK4 backends) +* :ghpull:`21294`: Disable blitting on GTK4 backends +* :ghpull:`21277`: Backport PR #21263 on branch v3.5.x (Ensure internal FreeType matches Python compile) +* :ghpull:`21291`: DOC: Bump to the sphinx-gallery release +* :ghpull:`21296`: Backport PR #21288 on branch v3.5.x (Allow macosx thread safety test on macOS11) +* :ghpull:`21297`: Backport PR #21293 on branch v3.5.x (Fix snap argument to pcolormesh) +* :ghpull:`21293`: Fix snap argument to pcolormesh +* :ghpull:`21288`: Allow macosx thread safety test on macOS11 +* :ghpull:`21279`: Fix freetype wheel building +* :ghpull:`21292`: Backport PR #21290 on branch v3.5.x (DOC: Fix some lists in animation examples) +* :ghpull:`21290`: DOC: Fix some lists in animation examples +* :ghpull:`21284`: Backport PR #21282 on branch v3.5.x (Fix incorrect markup in example.) +* :ghpull:`21282`: Fix incorrect markup in example. +* :ghpull:`21281`: Backport PR #21275 on branch v3.5.x (Fix format_cursor_data for values close to float resolution.) +* :ghpull:`21275`: Fix format_cursor_data for values close to float resolution. +* :ghpull:`21263`: Ensure internal FreeType matches Python compile +* :ghpull:`21273`: Backport PR #21269 on branch v3.5.x (Don't use pixelDelta() on X11.) +* :ghpull:`21269`: Don't use pixelDelta() on X11. +* :ghpull:`21268`: Backport PR #21236: DOC: Update interactive colormap example +* :ghpull:`21265`: Backport PR #21264 on branch v3.5.x (DOC: Fix footnote that breaks PDF builds) +* :ghpull:`21264`: DOC: Fix footnote that breaks PDF builds +* :ghpull:`21236`: DOC: Update interactive colormap example +* :ghpull:`21262`: Backport PR #21250 on branch v3.5.x (DOC: Remove examples/README) +* :ghpull:`21260`: DOC: Fix source links to prereleases +* :ghpull:`21261`: Backport PR #21240: DOC: Fix source links and flake8 cleanup +* :ghpull:`21248`: Backport PR #21247 on branch v3.5.x (Fix release notes typos.) +* :ghpull:`21254`: Backport PR #21249 on branch v3.5.x (Fix some syntax highlights in coding and contributing guide.) +* :ghpull:`21250`: DOC: Remove examples/README +* :ghpull:`21249`: Fix some syntax highlights in coding and contributing guide. +* :ghpull:`20652`: Fixed Comments and Clarification +* :ghpull:`21240`: DOC: Fix source links and flake8 cleanup +* :ghpull:`21247`: Fix release notes typos. +* :ghpull:`21244`: Backport PR #20907 on branch v3.5.x (Move sigint tests into subprocesses) +* :ghpull:`21245`: Backport PR #21226 on branch v3.5.x (DOC: Adapt some colors in examples) +* :ghpull:`21226`: DOC: Adapt some colors in examples +* :ghpull:`20907`: Move sigint tests into subprocesses +* :ghpull:`21241`: Backport PR #21237 on branch v3.5.x (DOC: Add fill_between to plot_types) +* :ghpull:`21237`: DOC: Add fill_between to plot_types +* :ghpull:`21235`: Backport PR #20852 on branch v3.5.x (Prepare docs for 3.5) +* :ghpull:`20852`: Prepare docs for 3.5 +* :ghpull:`21234`: Backport PR #21221 on branch v3.5.x (Updates to plot types) +* :ghpull:`21232`: Backport PR #21228 on branch v3.5.x (Small doc nits.) +* :ghpull:`21233`: Backport PR #21229 on branch v3.5.x (Shorten PdfPages FAQ entry.) +* :ghpull:`21221`: Updates to plot types +* :ghpull:`21229`: Shorten PdfPages FAQ entry. +* :ghpull:`21228`: Small doc nits. +* :ghpull:`21227`: Backport PR #20730 on branch v3.5.x (DOC: Add a release mode tag) +* :ghpull:`20730`: DOC: Add a release mode tag +* :ghpull:`21225`: Backport PR #21223 on branch v3.5.x (Fix nav link for "Usage guide" and remove release/date info from that page) +* :ghpull:`21223`: Fix nav link for "Usage guide" and remove release/date info from that page +* :ghpull:`21222`: Backport PR #21211 on branch v3.5.x (updated resources) +* :ghpull:`21211`: updated resources +* :ghpull:`21219`: Backport PR #21216 on branch v3.5.x (Use correct confidence interval) +* :ghpull:`21216`: Use correct confidence interval +* :ghpull:`21217`: Backport PR #21215 on branch v3.5.x (Fix more edge cases in psd, csd.) +* :ghpull:`21215`: Fix more edge cases in psd, csd. +* :ghpull:`21210`: Backport PR #21191 on branch v3.5.x (Fix very-edge case in csd(), plus small additional cleanups.) +* :ghpull:`21209`: Backport PR #21188 on branch v3.5.x (Rework headers for individual backend docs.) +* :ghpull:`21191`: Fix very-edge case in csd(), plus small additional cleanups. +* :ghpull:`21188`: Rework headers for individual backend docs. +* :ghpull:`21208`: Backport PR #21203 on branch v3.5.x (Rework plot types quiver) +* :ghpull:`21203`: Rework plot types quiver +* :ghpull:`21207`: Backport PR #21198 on branch v3.5.x (Update coding_guide.rst) +* :ghpull:`21206`: Backport PR #21201 on branch v3.5.x (Fix signature of barh() in plot types) +* :ghpull:`21204`: Backport PR #21193 on branch v3.5.x (Update contributing guide.) +* :ghpull:`21198`: Update coding_guide.rst +* :ghpull:`21201`: Fix signature of barh() in plot types +* :ghpull:`21200`: Backport PR #21196 on branch v3.5.x (Update fonts.rst) +* :ghpull:`21199`: Backport PR #21026 on branch v3.5.x (Place 3D contourf patches between levels) +* :ghpull:`21197`: Backport PR #21186 on branch v3.5.x (Fixed typos using codespell. (previous pull request was told not to change the agg files) ) +* :ghpull:`21196`: Update fonts.rst +* :ghpull:`21026`: Place 3D contourf patches between levels +* :ghpull:`21186`: Fixed typos using codespell. (previous pull request was told not to change the agg files) +* :ghpull:`21195`: Backport PR #21189 on branch v3.5.x (Small doc fixes.) +* :ghpull:`21194`: Backport PR #21192 on branch v3.5.x (Discourage making style changes to extern/.) +* :ghpull:`21189`: Small doc fixes. +* :ghpull:`21192`: Discourage making style changes to extern/. +* :ghpull:`21193`: Update contributing guide. +* :ghpull:`21184`: Backport PR #21172 on branch v3.5.x (skip QImage leak workaround for PySide2 >= 5.12) +* :ghpull:`21183`: Backport PR #21081 on branch v3.5.x (Improve docs for to_jshtml()) +* :ghpull:`21172`: skip QImage leak workaround for PySide2 >= 5.12 +* :ghpull:`21181`: Backport PR #21166 on branch v3.5.x (Cleanup contour(f)3d examples.) +* :ghpull:`21182`: Backport PR #21180 on branch v3.5.x (Remove uninformative ``.. figure::`` titles in docs.) +* :ghpull:`21081`: Improve docs for to_jshtml() +* :ghpull:`21180`: Remove uninformative ``.. figure::`` titles in docs. +* :ghpull:`21166`: Cleanup contour(f)3d examples. +* :ghpull:`21174`: Backport PR #19343 on branch v3.5.x (Enh improve agg chunks error) +* :ghpull:`19343`: Enh improve agg chunks error +* :ghpull:`21171`: Backport PR #20951 on branch v3.5.x ([ENH]: data kwarg support for mplot3d #20912) +* :ghpull:`21169`: Backport PR #21126 on branch v3.5.x (Deprecate passing formatting parameters positionally to stem()) +* :ghpull:`21126`: Deprecate passing formatting parameters positionally to stem() +* :ghpull:`21164`: Backport PR #21039 on branch v3.5.x (Fix ``hexbin`` marginals and log scaling) +* :ghpull:`21039`: Fix ``hexbin`` marginals and log scaling +* :ghpull:`21160`: Backport PR #21136 on branch v3.5.x (More (minor) plot types gallery fixes.) +* :ghpull:`21136`: More (minor) plot types gallery fixes. +* :ghpull:`21158`: Backport PR #21140 on branch v3.5.x (Docstring cleanups around DATA_PARAMETER_PLACEHOLDER.) +* :ghpull:`21159`: Backport PR #21127 on branch v3.5.x (Simplify argument parsing in stem().) +* :ghpull:`21157`: Backport PR #21153 on branch v3.5.x (Improve curve_error_band example.) +* :ghpull:`21156`: Backport PR #21154 on branch v3.5.x (Increase marker size in double_pendulum example.) +* :ghpull:`21127`: Simplify argument parsing in stem(). +* :ghpull:`21140`: Docstring cleanups around DATA_PARAMETER_PLACEHOLDER. +* :ghpull:`21153`: Improve curve_error_band example. +* :ghpull:`21154`: Increase marker size in double_pendulum example. +* :ghpull:`21149`: Backport PR #21146 on branch v3.5.x (Fix clim handling for pcolor{,mesh}.) +* :ghpull:`21151`: Backport PR #21141 on branch v3.5.x (Fix DATA_PARAMETER_PLACEHOLDER interpolation for quiver&contour{,f}.) +* :ghpull:`21150`: Backport PR #21145 on branch v3.5.x (Fix format_cursor_data with nans.) +* :ghpull:`21141`: Fix DATA_PARAMETER_PLACEHOLDER interpolation for quiver&contour{,f}. +* :ghpull:`21145`: Fix format_cursor_data with nans. +* :ghpull:`21146`: Fix clim handling for pcolor{,mesh}. +* :ghpull:`21148`: Backport PR #21142 on branch v3.5.x (Mac qt ctrl) +* :ghpull:`21142`: Mac qt ctrl +* :ghpull:`21144`: Backport PR #21122 on branch v3.5.x (CTRL does not fix aspect in zoom-to-rect mode.) +* :ghpull:`21143`: Backport PR #19515 on branch v3.5.x (Colorbar axis zoom and pan) +* :ghpull:`21122`: CTRL does not fix aspect in zoom-to-rect mode. +* :ghpull:`19515`: Colorbar axis zoom and pan +* :ghpull:`21138`: Backport PR #21131 on branch v3.5.x (Fix polar() regression on second call failure) +* :ghpull:`21134`: Backport PR #21124 on branch v3.5.x (Tweak streamplot plot_types example.) +* :ghpull:`21133`: Backport PR #21114 on branch v3.5.x (Add contour and tricontour plots to plot types) +* :ghpull:`21132`: Backport PR #21093 on branch v3.5.x (DOC: clarify what we mean by object oriented in pyplot api) +* :ghpull:`21124`: Tweak streamplot plot_types example. +* :ghpull:`21114`: Add contour and tricontour plots to plot types +* :ghpull:`21130`: Backport PR #21129 on branch v3.5.x (Fix decenter of image in gallery thumbnails) +* :ghpull:`21093`: DOC: clarify what we mean by object oriented in pyplot api +* :ghpull:`21129`: Fix decenter of image in gallery thumbnails +* :ghpull:`21125`: Backport PR #21086 on branch v3.5.x (Capitalization fixes in example section titles.) +* :ghpull:`21128`: Backport PR #21123 on branch v3.5.x (Simplify/uniformize sample data setup in plot_types examples.) +* :ghpull:`21123`: Simplify/uniformize sample data setup in plot_types examples. +* :ghpull:`21121`: Backport PR #21111 on branch v3.5.x (Rename section title Gallery -> Examples) +* :ghpull:`21086`: Capitalization fixes in example section titles. +* :ghpull:`21120`: Backport PR #21115 on branch v3.5.x (Improve errorbar plot types example) +* :ghpull:`21119`: Backport PR #21116 on branch v3.5.x (Adapt css so that galleries have four columns) +* :ghpull:`21116`: Adapt css so that galleries have four columns +* :ghpull:`21118`: Backport PR #21112 on branch v3.5.x (Fix make_norm_from_scale ``__name__`` when used inline.) +* :ghpull:`21111`: Rename section title Gallery -> Examples +* :ghpull:`21112`: Fix make_norm_from_scale ``__name__`` when used inline. +* :ghpull:`20951`: [ENH]: data kwarg support for mplot3d #20912 +* :ghpull:`21115`: Improve errorbar plot types example +* :ghpull:`21109`: Backport PR #21104 on branch v3.5.x (Remove the index and module index pages) +* :ghpull:`21104`: Remove the index and module index pages +* :ghpull:`21102`: Backport PR #21100 on branch v3.5.x (Cleanup demo_tight_layout.) +* :ghpull:`21106`: Backport PR #21034 on branch v3.5.x (Make rcParams["backend"] backend fallback check rcParams identity first.) +* :ghpull:`21105`: Backport PR #21083 on branch v3.5.x (Fix capitalizations) +* :ghpull:`21103`: Backport PR #21089 on branch v3.5.x (Update sticky_edges docstring to new behavior.) +* :ghpull:`21034`: Make rcParams["backend"] backend fallback check rcParams identity first. +* :ghpull:`21083`: Fix capitalizations +* :ghpull:`21099`: Backport PR #20935 on branch v3.5.x (Add ColormapsRegistry as experimental and add it to pyplot) +* :ghpull:`21100`: Cleanup demo_tight_layout. +* :ghpull:`21098`: Backport PR #20903 on branch v3.5.x (Use release-branch version scheme ) +* :ghpull:`20935`: Add ColormapsRegistry as experimental and add it to pyplot +* :ghpull:`20903`: Use release-branch version scheme +* :ghpull:`21089`: Update sticky_edges docstring to new behavior. +* :ghpull:`21084`: Backport PR #20988 on branch v3.5.x (Add HiDPI support in GTK.) +* :ghpull:`21085`: Backport PR #21082 on branch v3.5.x (Fix layout of sidebar entries) +* :ghpull:`20345`: ENH: call update_ticks before we return them to the user +* :ghpull:`21082`: Fix layout of sidebar entries +* :ghpull:`20988`: Add HiDPI support in GTK. +* :ghpull:`21080`: Backport PR #19619 on branch v3.5.x (Fix bug in shape assignment) +* :ghpull:`19619`: Fix bug in shape assignment +* :ghpull:`21079`: Backport PR #21078 on branch v3.5.x (Cache build dependencies on Circle) +* :ghpull:`21078`: Cache build dependencies on Circle +* :ghpull:`21077`: Backport PR #21076 on branch v3.5.x (Break links between twinned axes when removing) +* :ghpull:`21076`: Break links between twinned axes when removing +* :ghpull:`21073`: Backport PR #21072 on branch v3.5.x (Use sysconfig directly instead of through distutils) +* :ghpull:`21072`: Use sysconfig directly instead of through distutils +* :ghpull:`21071`: Backport PR #21061 on branch v3.5.x (Remove most visible dependencies on distutils.) +* :ghpull:`21061`: Remove most visible dependencies on distutils. +* :ghpull:`21070`: Backport PR #21025 on branch v3.5.x (Fix Cairo backends on HiDPI screens) +* :ghpull:`21065`: Backport PR #20819 on branch v3.5.x (Add CPython 3.10 wheels) +* :ghpull:`21069`: Backport PR #21051 on branch v3.5.x (set_dashes does not support offset=None anymore.) +* :ghpull:`21068`: Backport PR #21067 on branch v3.5.x (Remove generated file accidentally added in #20867) +* :ghpull:`21025`: Fix Cairo backends on HiDPI screens +* :ghpull:`21051`: set_dashes does not support offset=None anymore. +* :ghpull:`21067`: Remove generated file accidentally added in #20867 +* :ghpull:`21066`: Backport PR #21060 on branch v3.5.x (Correct the default for fillstyle parameter in MarkerStyle()) +* :ghpull:`20819`: Add CPython 3.10 wheels +* :ghpull:`21064`: Backport PR #20913 on branch v3.5.x ([Doc] colors.to_hex input & output) +* :ghpull:`20913`: [Doc] colors.to_hex input & output +* :ghpull:`21063`: Backport PR #21062 on branch v3.5.x (Fix typo in template of current dev-docs) +* :ghpull:`21062`: Fix typo in template of current dev-docs +* :ghpull:`21060`: Correct the default for fillstyle parameter in MarkerStyle() +* :ghpull:`21058`: Backport PR #21053 on branch v3.5.x (Fix validate_markevery docstring markup.) +* :ghpull:`21053`: Fix validate_markevery docstring markup. +* :ghpull:`21052`: Backport PR #20867 on branch v3.5.x ("inner" index reorganization) +* :ghpull:`21047`: Backport PR #21040 on branch v3.5.x (Document ``handleheight`` parameter of ``Legend`` constructor) +* :ghpull:`21048`: Backport PR #21044 on branch v3.5.x (Support for forward/back mousebuttons on WX backend) +* :ghpull:`20867`: "inner" index reorganization +* :ghpull:`21044`: Support for forward/back mousebuttons on WX backend +* :ghpull:`21040`: Document ``handleheight`` parameter of ``Legend`` constructor +* :ghpull:`21045`: Backport PR #21041 on branch v3.5.x (Prefer "none" to "None" in docs, examples and comments.) +* :ghpull:`21041`: Prefer "none" to "None" in docs, examples and comments. +* :ghpull:`21037`: Backport PR #20949 on branch v3.5.x (Improve formatting of imshow() cursor data independently of colorbar.) +* :ghpull:`21035`: Backport PR #21031 on branch v3.5.x (Make date.{converter,interval_multiples} rcvalidators side-effect free.) +* :ghpull:`20949`: Improve formatting of imshow() cursor data independently of colorbar. +* :ghpull:`21031`: Make date.{converter,interval_multiples} rcvalidators side-effect free. +* :ghpull:`21032`: Backport PR #21017 on branch v3.5.x (FIX: Don't subslice lines if non-standard transform) +* :ghpull:`21030`: Backport PR #20980 on branch v3.5.x (FIX: remove colorbar from list of colorbars on axes) +* :ghpull:`21029`: Backport PR #21028 on branch v3.5.x (Minor homogeneization of markup for MEP titles.) +* :ghpull:`21028`: Minor homogeneization of markup for MEP titles. +* :ghpull:`21022`: Backport PR #20518 on branch v3.5.x ( Support sketch_params in pgf backend) +* :ghpull:`20518`: Support sketch_params in pgf backend +* :ghpull:`21018`: Backport PR #20976 on branch v3.5.x (Separate tick and spine examples) +* :ghpull:`20976`: Separate tick and spine examples +* :ghpull:`21014`: Backport PR #20994 on branch v3.5.x (Remove unused icon_filename, window_icon globals.) +* :ghpull:`21013`: Backport PR #21012 on branch v3.5.x (Use numpydoc for GridSpecFromSubplotSpec.__init__) +* :ghpull:`20994`: Remove unused icon_filename, window_icon globals. +* :ghpull:`21012`: Use numpydoc for GridSpecFromSubplotSpec.__init__ +* :ghpull:`21011`: Backport PR #21003 on branch v3.5.x (Deemphasize mpl_toolkits in API docs.) +* :ghpull:`21003`: Deemphasize mpl_toolkits in API docs. +* :ghpull:`21002`: Backport PR #20987 on branch v3.5.x (FIX: colorbar with boundary norm, proportional, extend) +* :ghpull:`20987`: FIX: colorbar with boundary norm, proportional, extend +* :ghpull:`21000`: Backport PR #20997 on branch v3.5.x (Fix ToolManager + TextBox support.) +* :ghpull:`20997`: Fix ToolManager + TextBox support. +* :ghpull:`20985`: Backport PR #20942 on branch v3.5.x (DOC Use 'Axes' instead of 'axes' in axes._base.py) +* :ghpull:`20983`: Backport PR #20973 on branch v3.5.x (Docstring cleanups.) +* :ghpull:`20982`: Backport PR #20972 on branch v3.5.x (Cleanup some dviread docstrings.) +* :ghpull:`20942`: DOC Use 'Axes' instead of 'axes' in axes._base.py +* :ghpull:`20981`: Backport PR #20975 on branch v3.5.x (Clarify support for 2D coordinate inputs to streamplot.) +* :ghpull:`20972`: Cleanup some dviread docstrings. +* :ghpull:`20975`: Clarify support for 2D coordinate inputs to streamplot. +* :ghpull:`20973`: Docstring cleanups. +* :ghpull:`20971`: Backport PR #20970 on branch v3.5.x (Build wheels for Apple Silicon.) +* :ghpull:`20970`: Build wheels for Apple Silicon. +* :ghpull:`20969`: Backport PR #20321 on branch v3.5.x (Add a GTK4 backend.) +* :ghpull:`20321`: Add a GTK4 backend. +* :ghpull:`20966`: Backport PR #19553 on branch v3.5.x (ENH: Adding callbacks to Norms for update signals) +* :ghpull:`20967`: Backport PR #20965 on branch v3.5.x (BUG: Fix f_back is None handling) +* :ghpull:`20965`: BUG: Fix f_back is None handling +* :ghpull:`19553`: ENH: Adding callbacks to Norms for update signals +* :ghpull:`20960`: Backport PR #20745 on branch v3.5.x (Clean up some Event class docs.) +* :ghpull:`20745`: Clean up some Event class docs. +* :ghpull:`20959`: Backport PR #20952 on branch v3.5.x (Redirect to new 3rd party packages page) +* :ghpull:`20952`: Redirect to new 3rd party packages page +* :ghpull:`20958`: Backport PR #20956 on branch v3.5.x (Make warning for no-handles legend more explicit.) +* :ghpull:`20956`: Make warning for no-handles legend more explicit. +* :ghpull:`20954`: Backport PR #20931 on branch v3.5.x (API: rename draw_no_output to draw_without_rendering) +* :ghpull:`20931`: API: rename draw_no_output to draw_without_rendering +* :ghpull:`20934`: Backport PR #20919 on branch v3.5.x (Improve various release notes)" +* :ghpull:`20948`: Backport PR #20944 on branch v3.5.x (Switch documented deprecations in mathtext by ``__getattr__`` deprecations) +* :ghpull:`20944`: Switch documented deprecations in mathtext by ``__getattr__`` deprecations +* :ghpull:`20947`: Backport PR #20941 on branch v3.5.x (Fix variable capitalization in plot types headings) +* :ghpull:`20941`: Fix variable capitalization in plot types headings +* :ghpull:`20939`: Backport PR #20937 on branch v3.5.x (Fix documented allowed values for Patch.set_edgecolor.) +* :ghpull:`20940`: Backport PR #20938 on branch v3.5.x (Fix missorted changelog entry.) +* :ghpull:`20938`: Fix missorted changelog entry. +* :ghpull:`20937`: Fix documented allowed values for Patch.set_edgecolor. +* :ghpull:`20933`: Backport PR #20916 on branch v3.5.x (Improve deleted Animation warning) +* :ghpull:`20916`: Improve deleted Animation warning +* :ghpull:`20919`: Improve various release notes +* :ghpull:`20928`: Backport PR #20889 on branch v3.5.x (Fix clearing selector) +* :ghpull:`20927`: Backport PR #20924 on branch v3.5.x (Improve ``path.py`` docstrings a bit) +* :ghpull:`20889`: Fix clearing selector +* :ghpull:`20922`: Backport PR #20920 on branch v3.5.x (Fix cubic curve code in ``Path.__doc__``) +* :ghpull:`20925`: Backport PR #20917 on branch v3.5.x (Move installing FAQ to installing page.) +* :ghpull:`20924`: Improve ``path.py`` docstrings a bit +* :ghpull:`20917`: Move installing FAQ to installing page. +* :ghpull:`20920`: Fix cubic curve code in ``Path.__doc__`` +* :ghpull:`20918`: Backport PR #20915 on branch v3.5.x ([Doc] boxplot typo) +* :ghpull:`20915`: [Doc] boxplot typo +* :ghpull:`20908`: [Doc] FigureCanvasBase draw +* :ghpull:`20899`: Backport PR #20885 on branch v3.5.x (Fix broken QApplication init in a test.) +* :ghpull:`20885`: Fix broken QApplication init in a test. +* :ghpull:`20894`: Backport PR #20891 on branch v3.5.x (Add dependency link for 3.5) +* :ghpull:`20893`: Backport PR #20892 on branch v3.5.x (Label pylab as "discouraged" instead of "disapproved") +* :ghpull:`20891`: Add dependency link for 3.5 +* :ghpull:`20888`: Backport PR #20864 on branch v3.5.x (Add Python 3.10 testing.) +* :ghpull:`20890`: Backport PR #20693 on branch v3.5.x (Fix setting artists properties of selectors) +* :ghpull:`20892`: Label pylab as "discouraged" instead of "disapproved" +* :ghpull:`20693`: Fix setting artists properties of selectors +* :ghpull:`20864`: Add Python 3.10 testing. +* :ghpull:`20886`: Backport PR #20884 on branch v3.5.x (Ensure full environment is passed to headless test.) +* :ghpull:`20884`: Ensure full environment is passed to headless test. +* :ghpull:`20883`: Make pywin32 optional in Ctrl+C Qt test. +* :ghpull:`20874`: Add additional external resource. +* :ghpull:`20875`: Use mpl.colormaps in examples +* :ghpull:`20586`: Deprecate matplotlib.test() +* :ghpull:`19892`: Add Figure parameter layout and discourage tight_layout / constrained_layout +* :ghpull:`20882`: Don't add QtNetwork to the API exported by qt_compat. +* :ghpull:`20881`: Deprecate some old globals in qt_compat. +* :ghpull:`13306`: Qt5: SIGINT kills just the mpl window and not the process itself +* :ghpull:`20876`: DOC: Fix dependency link. +* :ghpull:`20878`: Use tables for Locator and Formatter docs +* :ghpull:`20873`: Remove mplutils.cpp; shorten mplutils.h. +* :ghpull:`20872`: Remove some boilerplate from C extension inits. +* :ghpull:`20871`: Move setup.cfg to mplsetup.cfg. +* :ghpull:`20869`: Ignore errors trying to delete make_release_tree. +* :ghpull:`20868`: Fix qt key mods +* :ghpull:`20856`: TST: Add unit test to catch recurrences of #20822, #20855 +* :ghpull:`20857`: Propose a less error-prone helper for module-level getattrs. +* :ghpull:`20840`: Speed up Tkagg blit with Tk_PhotoPutBlock +* :ghpull:`20805`: Ensure all params are restored after ``reset_ticks``. +* :ghpull:`20863`: new github citation format +* :ghpull:`20859`: Allow SubFigure legends +* :ghpull:`20848`: Fix PyPy wheels and tests +* :ghpull:`20862`: Fix minor typo in setupext.py +* :ghpull:`20814`: FIX: Avoid copying source script when ``plot_html_show_source_link`` is False in plot directive +* :ghpull:`20855`: BUG: __getattr__ must raise AttributeError if name not found (again) +* :ghpull:`20079`: Prepare axes_divider for simpler(?) indexing-based API. +* :ghpull:`20444`: Delete _Bracket and update the _Curve to be able to ']->' and '<-[' +* :ghpull:`20812`: Clarify tutorial "Customizing Matplotlib with style sheets and rcParams" +* :ghpull:`20806`: Deprecate matplotlib.cm.LUTSIZE +* :ghpull:`20818`: Swap Cap/Cup glyphs when using STIX font. +* :ghpull:`20849`: Add external resources to devdoc landing page +* :ghpull:`20846`: Re-re-remove deprecated Qt globals. +* :ghpull:`18503`: Add a dedicated ColormapRegistry class +* :ghpull:`20603`: Deprecate unused LassoSelector event handlers. +* :ghpull:`20679`: Fix selector onselect call when the selector is removed by an "empty" click and add ``ignore_event_outside`` argument +* :ghpull:`11358`: FIX/ENH: Introduce a monolithic legend handler for Line2D +* :ghpull:`20699`: FIX/ENH: Introduce a monolithic legend handler for Line2D +* :ghpull:`20837`: Merge branch v3.4.x +* :ghpull:`18782`: ENH: allow image to interpolate post RGBA +* :ghpull:`20829`: TST: neither warned and pytest upstream deprecated this usage +* :ghpull:`20828`: Increase test timeouts to 60 s to aid slower architectures +* :ghpull:`20816`: ENH: Add the ability to block callback signals +* :ghpull:`20646`: Handle NaN values in ``plot_surface`` zsort +* :ghpull:`20725`: ``Axes3D.plot_surface``: Allow masked arrays and ``NaN`` values +* :ghpull:`20825`: Fix image triage tool with Qt6 +* :ghpull:`20229`: ENH: Only do constrained layout at draw... +* :ghpull:`20822`: BUG: __getattr__ must raise AttributeError if name not found +* :ghpull:`20815`: circle: Switch to next-gen image. +* :ghpull:`20813`: add doc-link to dufte +* :ghpull:`20799`: MNT: Rename callbacksSM to callbacks +* :ghpull:`20803`: Re-remove deprecated Qt globals. +* :ghpull:`17810`: FIX: don't fail on first show if animation already exhausted +* :ghpull:`20733`: Deprecate globals using module-level ``__getattr__``. +* :ghpull:`20788`: FIX: Check for colorbar creation with multi-dimensional alpha +* :ghpull:`20115`: ENH: pass extra kwargs in FigureBase, SubFigure, Figure to set +* :ghpull:`20795`: TST/MNT: deprecate unused fixture +* :ghpull:`20792`: Change legend guide to object oriented approach +* :ghpull:`20717`: Fix collection offsets +* :ghpull:`20673`: Point [SOURCE] documents to github +* :ghpull:`19255`: Support for PyQt6/PySide6. +* :ghpull:`20772`: Implement remove_rubberband rather than release_zoom. +* :ghpull:`20783`: Document how to check for the existence of current figure/axes. +* :ghpull:`20778`: Dedupe handling of mouse buttons in macos backend. +* :ghpull:`20749`: Cleanup font subsetting code +* :ghpull:`20775`: Remove some remnants of qt4 support. +* :ghpull:`20659`: Add HiDPI-related config for mathmpl +* :ghpull:`20767`: Factor out latex ifpackageloaded pattern. +* :ghpull:`20769`: Simplify backend_ps._nums_to_str. +* :ghpull:`20768`: Avoid using gca() in examples. +* :ghpull:`20766`: Fix line dash offset format in PS output +* :ghpull:`20706`: Include ``underscore.sty`` +* :ghpull:`20729`: Support vmin/vmax with bins='log' in hexbin +* :ghpull:`20753`: Deprecate support for case-insensitive scales. +* :ghpull:`20602`: Merge EllipseSelector example together with RectangleSelector. +* :ghpull:`20744`: Add an example showing alternate mouse cursors. +* :ghpull:`20758`: FIX: pass colorbar.set_ticklabels down to long_axis +* :ghpull:`20759`: Modernize mathtext examples +* :ghpull:`20739`: Small simplifications to streamplot. +* :ghpull:`20756`: Add new external resource: Python Graph Gallery +* :ghpull:`20330`: Fix cla colorbar +* :ghpull:`20688`: issue form files +* :ghpull:`20743`: Set the canvas cursor when using a SpanSelector +* :ghpull:`20391`: Type42 subsetting in PS/PDF +* :ghpull:`20737`: DOC: new index page +* :ghpull:`20686`: Fix interaction between make_keyword_only and pyplot generation. +* :ghpull:`20731`: Improved implementation of Path.copy and deepcopy +* :ghpull:`20732`: Fix style in ``assert(x)``. +* :ghpull:`20620`: Move set_cursor from the toolbar to FigureCanvas. +* :ghpull:`20728`: Fix broken link in 'Contributing' docs +* :ghpull:`20727`: DOC/TST make circle faster +* :ghpull:`20726`: DOC: Provide alternative to cbar.patch +* :ghpull:`20719`: Fix color normalization in plot types scatter +* :ghpull:`20634`: Implement Type-1 decryption +* :ghpull:`20633`: Emit non BMP chars as XObjects in PDF +* :ghpull:`20709`: Fix Circle merge on master branch. +* :ghpull:`20701`: Small cleanup to GTK backend +* :ghpull:`20670`: Support markevery on figure-level lines. +* :ghpull:`20707`: Rename a confusingly named variable in backend_pdf. +* :ghpull:`20680`: CI: Build merged version on CircleCI +* :ghpull:`20471`: add interactive colorbar example to gallery +* :ghpull:`20692`: Small cleanups to hatch.py. +* :ghpull:`20702`: DOC: add note about contouring algorithm +* :ghpull:`18869`: Add __version_info__ as a tuple-based version identifier +* :ghpull:`20689`: Fix some very unlikely leaks in extensions. +* :ghpull:`20254`: Define FloatingAxes boundary patch in data coordinates. +* :ghpull:`20682`: Bump codecov/codecov-action from 1 to 2 +* :ghpull:`20544`: Support of different locations for the text fixing cursor of TextBox +* :ghpull:`20648`: Simplify barchart_demo +* :ghpull:`20606`: Dynamically generate CbarAxes. +* :ghpull:`20405`: ENH: expose make_norm_from_scale +* :ghpull:`20555`: Fix the way to get xs length in set_3d_properties() +* :ghpull:`20546`: Improve tutorial figures in the new theme +* :ghpull:`20676`: Fix bounds when initialising ``SpanSelector`` +* :ghpull:`20678`: Clarify comment about backend_pgf.writeln. +* :ghpull:`20675`: Shorten the ``@deprecated`` docs. +* :ghpull:`20585`: Rename parameter selectors +* :ghpull:`20672`: Remove outdated parts of MatplotlibDeprecationWarning docs. +* :ghpull:`20671`: Standardize description of kwargs in legend_handler. +* :ghpull:`20669`: Cleanup related to usage of axs +* :ghpull:`20664`: Reword docs about fallbacks on headless linux. +* :ghpull:`20663`: Document $MPLSETUPCFG. +* :ghpull:`20638`: Small simplifications to FixedAxisArtistHelper. +* :ghpull:`20626`: Simplify curvilinear grid examples. +* :ghpull:`20088`: fix some http: -> https: URLs +* :ghpull:`20654`: Remove some usages of plt.setp() +* :ghpull:`20615`: Font 42 kerning +* :ghpull:`20636`: Use set_xticks(ticks, labels) instead of a separate set_xticklabels() +* :ghpull:`20450`: [Doc] Font Types and Font Subsetting +* :ghpull:`20582`: Fix twoslopenorm colorbar +* :ghpull:`20632`: Use ticklabels([]) instead of ticklabels('') +* :ghpull:`20608`: doc/conf.py: if set, use SOURCE_DATE_EPOCH to set copyright year. +* :ghpull:`20605`: Add \dddot and \ddddot as accents in mathtext +* :ghpull:`20621`: TST/DOC: just run circle once... +* :ghpull:`20498`: Adapt the release guide to the new release notes structure +* :ghpull:`20601`: Hide some ``_SelectorWidget`` state internals. +* :ghpull:`20600`: Inline _print_svg into its only call site (print_svg). +* :ghpull:`20589`: Add directional sizing cursors +* :ghpull:`20481`: Deprecate Colorbar.patch. +* :ghpull:`20598`: Don't forget to propagate kwargs from print_svgz to print_svg. +* :ghpull:`19495`: Move svg basename detection down to RendererSVG. +* :ghpull:`20501`: Colorbar redo again! +* :ghpull:`20407`: Turn shared_axes, stale_viewlims into {axis_name: value} dicts. +* :ghpull:`18966`: PR: Remove modality of figure options +* :ghpull:`19265`: Change styling of slider widgets +* :ghpull:`20593`: DOC: fix various typos +* :ghpull:`20374`: Check modification times of included RST files +* :ghpull:`20569`: Better signature and docstring for Artist.set +* :ghpull:`20574`: Add tricontourf hatching example +* :ghpull:`18666`: Remove unused/deprecated ``AVConv`` classes +* :ghpull:`20514`: Fix example for rcParams['autolimit_mode'] +* :ghpull:`20571`: Switch default ArrowStyle angle values from None to zero. +* :ghpull:`20510`: Consistent capitalization of section headers +* :ghpull:`20573`: Move the marker path example into the marker reference +* :ghpull:`20572`: Clarify allowable backend switches in matplotlib.use(). +* :ghpull:`20538`: Show box/arrowstyle parameters in reference examples. +* :ghpull:`20515`: Shorten the implementation of bxp(). +* :ghpull:`20562`: More concise how to for subplot adjustment +* :ghpull:`20570`: Reduce vertical margins in property tables +* :ghpull:`20563`: Expire deprecation of passing nbins to MaxNLocator in two ways +* :ghpull:`20561`: Fix limits in plot types example hist(x) +* :ghpull:`20559`: Fix deprecation of encoding in plot_directive. +* :ghpull:`20547`: Raise if passed invalid kwargs to set_constrained_layout_pads. +* :ghpull:`20527`: Factor out DEBUG_TRUETYPE checks in ttconv, & removals of unused defs. +* :ghpull:`20465`: Remove remaining 3.3 deprecations +* :ghpull:`20558`: Rename recently introduced parameters in SpanSelector +* :ghpull:`20535`: Improve the documentation guide +* :ghpull:`20113`: Interactive span selector improvement +* :ghpull:`20524`: Dedupe some box anchoring code between legend.py and offsetbox.py. +* :ghpull:`20451`: Add initial TextBox widget testing +* :ghpull:`20543`: Deprecate ``@pytest.mark.style(...)``. +* :ghpull:`20530`: Plot nothing for incompatible 0 shape in x,y data +* :ghpull:`20367`: Add parse_math in Text and default it False for TextBox +* :ghpull:`20509`: Cleanup plot types +* :ghpull:`20537`: Don't sort boxstyles/arrowstyles/etc. alphabetically. +* :ghpull:`20542`: Fix ScalarFormatter.format_ticks for non-ordered tick locations. +* :ghpull:`20533`: Rename (N, M) -> (M, N) array-like +* :ghpull:`20540`: Deprecate :encoding: option to .. plot::, which has no effect since 2011 +* :ghpull:`20541`: Minor fix +* :ghpull:`20539`: Document defaults in plot_directive. +* :ghpull:`20536`: Make most of annotation tutorial a comment, and remove figure titles. +* :ghpull:`20439`: Remove dead code from LGTM alerts. +* :ghpull:`20528`: Merge subplot_demo into subplot example. +* :ghpull:`20493`: Cleanup AnchoredOffsetbox-related demos. +* :ghpull:`20513`: Shorten the bxp docstring. +* :ghpull:`20507`: Merge subplot_toolbar example into subplots_adjust. +* :ghpull:`20505`: Add rc_context to customizing tutorial +* :ghpull:`20449`: Suppress repeated logwarns in postscript output. +* :ghpull:`20500`: DOC: Add twitter icon and fix logo link +* :ghpull:`20499`: Simplify plot types pie() +* :ghpull:`20495`: Fix shape of Z in contour docs +* :ghpull:`20497`: Remove obsolete footnote on pyside +* :ghpull:`20485`: DOC: hexbin 'extent' must be 4-tuple of float, not float +* :ghpull:`20466`: Various cleanups to pgf backend. +* :ghpull:`20474`: Make lack of support more explicit for non-postscript fonts + usetex. +* :ghpull:`20476`: give Font a root widget +* :ghpull:`20477`: remove _master attribute from FigureCanvasTk +* :ghpull:`19731`: DOC: first pass at switching to pydata theme +* :ghpull:`20475`: Less pyplot, more OO in docs. +* :ghpull:`20467`: Small cleanups to sphinxext.plot_directive. +* :ghpull:`20437`: Use packaging to do version comparisons. +* :ghpull:`20354`: Merge Colorbar and ColorbarBase. +* :ghpull:`20464`: tinypages/conf.py doesn't need to manipulate sys.path. +* :ghpull:`20420`: Add a select_overload helper for signature-overloaded functions. +* :ghpull:`20460`: Shorten the AnchoredOffsetbox docstring. +* :ghpull:`20458`: Set the axes of legend text +* :ghpull:`20438`: Fix deprecation of ``Tick.apply_tickdir``. +* :ghpull:`20457`: Rename data variables in histogram example. +* :ghpull:`20442`: Fix dvi baseline detector when ``\usepackage{chemformula}`` is used. +* :ghpull:`20454`: Tell LGTM to use Python 3 explicitly. +* :ghpull:`20446`: Make used tex packages consistent between ps and other backends. +* :ghpull:`20447`: Remove Figure/Axes/Axis deprecations from 3.3 +* :ghpull:`20414`: ENH: add colorbar info to gridspec cbar +* :ghpull:`20436`: Add missing super __init__ in subclasses +* :ghpull:`20284`: Use a GtkApplication in GTK backend. +* :ghpull:`20400`: Make pdftex.map parsing stricter +* :ghpull:`20292`: Cleanup plot types docs +* :ghpull:`20445`: Small cleanups to backend_ps. +* :ghpull:`20399`: Improve example for 3D polygons +* :ghpull:`20432`: Small doc cleanups. +* :ghpull:`20398`: Document Axes.get_aspect() +* :ghpull:`20428`: Deprecate public use of get_path_in_displaycoord. +* :ghpull:`20397`: Improve hexbin() documentation +* :ghpull:`20430`: Improve fancyarrow_demo. +* :ghpull:`20431`: Fix indentation of Arrow/Box/Connection styles tables. +* :ghpull:`20427`: Fix references in ArrowStyle docstring. +* :ghpull:`20346`: Clarify/Improve docs on family-names vs generic-families +* :ghpull:`20410`: PGF: Clip lines/markers to maximum LaTeX dimensions. +* :ghpull:`20363`: Don't disable path clipping on paths with codes. +* :ghpull:`20244`: Inline and simplify SubplotToolQt. +* :ghpull:`20165`: Slightly improve output of dvi debug utilities, and tiny cleanups. +* :ghpull:`20390`: Cleanup arrow_demo. +* :ghpull:`20408`: Remove mention of now-removed Encoding class. +* :ghpull:`20327`: FIX: fix colorbars with no scales +* :ghpull:`20215`: Quadmesh.set_array validates dimensions +* :ghpull:`20293`: Simplify font setting in usetex mode +* :ghpull:`20386`: Merge arrow_simple_demo into arrow_guide. +* :ghpull:`20348`: codecs.getwriter has simpler lifetime semantics than TextIOWrapper. +* :ghpull:`20132`: Create release notes page +* :ghpull:`20331`: Remove Axis, Tick, and Axes deprecations from 3.3 +* :ghpull:`20373`: Handle direction="column" in axes_grid.Grid +* :ghpull:`20394`: Remove separate section for support of 3d subplots. +* :ghpull:`20393`: Remove non-informative figure captions. +* :ghpull:`17453`: Displaying colorbars with specified boundaries correctly +* :ghpull:`20369`: Switch version scheme to release-branch-semver. +* :ghpull:`20377`: Cleanup some examples titles & texts. +* :ghpull:`20378`: Redirect agg_buffer{,_to_array} examples to canvasagg. +* :ghpull:`20376`: Small improvements to canvasagg example. +* :ghpull:`20365`: Reorganize a bit text-related rcs in matplotlibrc. +* :ghpull:`20362`: Add research notice +* :ghpull:`20353`: Remove incorrect statement about data-kwarg interface. +* :ghpull:`20343`: Fix exception handling when constructing C-level PathGenerator. +* :ghpull:`20349`: Fix missing write in TTStreamWriter::printf. +* :ghpull:`20347`: Fix possible refleak in PathGenerator. +* :ghpull:`20339`: Cleanup autoscale-related docstrings. +* :ghpull:`20338`: Fix some indent-related style lints. +* :ghpull:`20337`: Small unit-related cleanups. +* :ghpull:`20168`: FIX: clean up re-limiting hysteresis +* :ghpull:`20336`: Deduplicate color format specification +* :ghpull:`20334`: Remove need for ConversionInterface to support unitless values. +* :ghpull:`20020`: For polar plots, report cursor position with correct precision. +* :ghpull:`20319`: DOC: Tweaks to module API pages +* :ghpull:`20332`: Quadmesh's default value of shading is now set to 'flat' instead of False +* :ghpull:`20333`: Better align param comments in ``Legend.__init__`` signature. +* :ghpull:`20323`: Adding cla and remove to ColorbarAxes +* :ghpull:`20320`: Fix remaining E265 exceptions. +* :ghpull:`20318`: DOC: Fix missing refs in what's new pages +* :ghpull:`20315`: Fix spelling. +* :ghpull:`20291`: Write data parameter docs as regular parameter not as note (v2) +* :ghpull:`19908`: Implement get_cursor_data for QuadMesh. +* :ghpull:`20314`: MAINT: Removing deprecated colorbar functions. +* :ghpull:`20310`: Add test for font selection by texmanager. +* :ghpull:`19348`: Make YearLocator a subclass of RRuleLocator +* :ghpull:`20208`: Rewrite blocking_input to something much simpler. +* :ghpull:`19033`: Templatize class factories. +* :ghpull:`20309`: DOC: Spell out args/kwargs in examples/tutorials +* :ghpull:`20305`: Merge two axisartist examples and point to standard methods. +* :ghpull:`20306`: Document legend(handles=handles) signature +* :ghpull:`20311`: Warn if a non-str is passed to an rcParam requiring a str. +* :ghpull:`18472`: Adding a get_coordinates() method to Quadmesh collections +* :ghpull:`20032`: axvline()/axvspan() should not update r limits in polar plots. +* :ghpull:`20304`: Don't mention dviread in the PsfontsMap "missing entry" error message. +* :ghpull:`20308`: Remove outdated comment re: pgf/windows. +* :ghpull:`20302`: Further remove use of meshWidth, meshHeight in QuadMesh. +* :ghpull:`20101`: Fix ``Text`` class bug when ``font`` argument is provided without ``math_fontfamily`` +* :ghpull:`15436`: Allow imshow from float16 data +* :ghpull:`20299`: Simplify tfm parsing. +* :ghpull:`20290`: Support imshow(). +* :ghpull:`20303`: Remove tilde in code links where not necessary +* :ghpull:`19873`: Allow changing the vertical axis in 3d plots +* :ghpull:`19558`: Use luatex in --luaonly mode to query kpsewhich. +* :ghpull:`20301`: Clarify the effect of PolygonCollection properties on Quiver +* :ghpull:`20235`: Warn user when mathtext font is used for ticks +* :ghpull:`20237`: Make QuadMesh arguments with defaults keyword_only +* :ghpull:`20054`: Enh better colorbar axes +* :ghpull:`20164`: Auto-generate required kwdoc entries into docstring.interpd. +* :ghpull:`19677`: Convert axis limit units in Qt plot options widget +* :ghpull:`14913`: Reimplement NonUniformImage, PcolorImage in Python, not C. +* :ghpull:`20295`: Replace text._wrap_text by _cm_set(). +* :ghpull:`19859`: Write data parameter docs as regular parameter not as note +* :ghpull:`20273`: Fix cursor with toolmanager on GTK3. +* :ghpull:`20288`: Small markup fixes in api docs. +* :ghpull:`20276`: Tiny fixes to mathtext/usetex tutorials. +* :ghpull:`20084`: Add legend.labelcolor in rcParams +* :ghpull:`19253`: Improve font spec for SVG font referencing. +* :ghpull:`20278`: Deprecate public access to certain texmanager attributes. +* :ghpull:`19375`: Don't composite path-clipped image; forward suppressComposite as needed. +* :ghpull:`20190`: Simplify handling of uncomparable formats in tests. +* :ghpull:`20277`: Fix ordering of tex font usepackages. +* :ghpull:`20279`: Slightly reword intros of mpl_toolkits API docs. +* :ghpull:`20272`: De-duplicate fonts in LaTeX preamble. +* :ghpull:`15604`: Deprecate auto-removal of grid by pcolor/pcolormesh. +* :ghpull:`20193`: Simplify HostAxes.draw and its interaction with ParasiteAxes. +* :ghpull:`19441`: Make backend_gtk3foo importable on headless environments. +* :ghpull:`20126`: Simplify font_manager font enumeration logic. +* :ghpull:`19869`: Factor out x/y lo/hi handling in errorbar. +* :ghpull:`20173`: Rename (with deprecation) first parameter of grid() from b to visible. +* :ghpull:`19499`: Fully fold overset/underset into _genset. +* :ghpull:`20268`: Api pcolorargs deprecation +* :ghpull:`20264`: Fix blitting selector +* :ghpull:`20081`: Limit documenting special members to __call__ +* :ghpull:`20245`: MAINT: Removing deprecated ``offset_position`` from Collection +* :ghpull:`20218`: Update Axes showcase in "Embedding in Tk" example +* :ghpull:`20019`: Example: Cursor widget with text +* :ghpull:`20242`: Add comments and format Axis._get_coord_info +* :ghpull:`20207`: Move axisartist towards using standard Transforms. +* :ghpull:`20247`: Explicitly reject black autoformatting. +* :ghpull:`20217`: ci: Export sphinx-gallery run results to CircleCI. +* :ghpull:`20238`: Clarify docstring of ScalarMappable.set/get_array() +* :ghpull:`20239`: Style tables in style guide +* :ghpull:`19894`: Remove deprecated Qt4 backends +* :ghpull:`19937`: Add washing machine to Axes3D +* :ghpull:`20233`: Add a Ubuntu 20.04 / Python 3.9 CI run +* :ghpull:`20227`: Adding an equals method to colormaps +* :ghpull:`20216`: Documentation Style Guide for contributors +* :ghpull:`20222`: Fix C coverage +* :ghpull:`20221`: DOC: clarify that savefig(..., transparent=False) has no effect +* :ghpull:`20047`: Add labels parameter to set_ticks() +* :ghpull:`20118`: Convert FontEntry to a data class +* :ghpull:`19167`: Add support for HiDPI in TkAgg on Windows +* :ghpull:`18397`: fix cmr10 negative sign in cmsy10 (RuntimeWarning: Glyph 8722 missing) +* :ghpull:`20170`: SubplotParams.validate-associated fixes. +* :ghpull:`19467`: Shorten the implementation of violin(). +* :ghpull:`12226`: FIX: pcolor/pcolormesh honour edgecolors kwarg when facecolors is set 'none' +* :ghpull:`18870`: Expand ScalarMappable.set_array to accept array-like inputs +* :ghpull:`20073`: Support SubFigures in AxesDivider. +* :ghpull:`20209`: Deprecate cbook.report_memory. +* :ghpull:`20211`: Use check_getitem in legend location resolution. +* :ghpull:`20206`: Cleanup axisartist in preparation for future changes. +* :ghpull:`20191`: Small simplifications to FloatingAxesBase. +* :ghpull:`20189`: Add tests for ginput and waitforbuttonpress. +* :ghpull:`20199`: Make set_marker{edge,face}color(None) more consistent. +* :ghpull:`16943`: Changing get_cmap to return copies of the registered colormaps. +* :ghpull:`19483`: MNT: deprecate epoch2num/num2epoch +* :ghpull:`20201`: Simplify _process_plot_var_args.set_prop_cycle. +* :ghpull:`20197`: Speedup Line2D marker color setting. +* :ghpull:`20194`: Fix markup on MEP22. +* :ghpull:`20198`: Fix validation of Line2D color. +* :ghpull:`20046`: Deprecation warning +* :ghpull:`20144`: More tight_layout cleanups +* :ghpull:`20105`: Shorten Curve arrowstyle implementations. +* :ghpull:`19401`: Simplify axisartist line clipping. +* :ghpull:`19260`: Update round fix +* :ghpull:`20196`: Replaced links to colormap packages with link to third-party packages list in MPL docs +* :ghpull:`18819`: Usage guide edit +* :ghpull:`18346`: Soft-deprecate Axes.plot_date() +* :ghpull:`20187`: Merge v3.4.x up into master +* :ghpull:`15333`: Enh: DivergingNorm Fair +* :ghpull:`20188`: Remove 3.3 deprecations in cbook. +* :ghpull:`20177`: Fix broken test re: polar tick visibility. +* :ghpull:`20026`: DOC: move third-party packages to new page +* :ghpull:`19994`: Don't hide shared "x/y"ticklabels for grids of non-rectilinear axes. +* :ghpull:`20150`: Rename mosaic layout +* :ghpull:`19369`: Add Artist._cm_set for temporarily setting an Artist property. +* :ghpull:`15889`: Add svg logo icon +* :ghpull:`20140`: DOC: make 2x versions of all gallery figures +* :ghpull:`20155`: Fix wheel builds on CI +* :ghpull:`19951`: Convert Qhull wrapper to C++ and array_view +* :ghpull:`19918`: Cleanup some consistency in contour extensions +* :ghpull:`20153`: Fix wheel builds on CI +* :ghpull:`19363`: Create box3d example +* :ghpull:`20129`: Cleanup some "variable assigned but not used" lints. +* :ghpull:`20107`: Support full-sharex/y in subplot_mosaic. +* :ghpull:`20094`: Switch _auto_adjust_subplotpars to take rowspan/colspan as input. +* :ghpull:`16368`: Improve warning for unsupported scripts. +* :ghpull:`19660`: Allow PolygonSelector points to be removed +* :ghpull:`16291`: Split Norm and LinearNorm up +* :ghpull:`20119`: Cleanup flake8 exceptions for examples +* :ghpull:`20109`: Fix trailing text in doctest-syntax plot_directive. +* :ghpull:`19538`: Speedup pdftex.map parsing. +* :ghpull:`20003`: Bump minimum NumPy to 1.17 +* :ghpull:`20074`: Copy-edit axes_grid tutorial. +* :ghpull:`20124`: Remove workaround unneeded on Py3.7+, which we require now. +* :ghpull:`20120`: Cleanup subsetting tool. +* :ghpull:`20108`: Skip back-and-forth between pixels and points in contour code. +* :ghpull:`20106`: Shorten bracket arrowstyle docs. +* :ghpull:`20090`: Cleanup anchored_artists, inset_locator docstrings. +* :ghpull:`20097`: Use nullcontext more as do-nothing context manager. +* :ghpull:`20095`: Remove 3.3 ticker deprecations +* :ghpull:`20064`: Expire deprecation of AxesDivider defaulting to zero pads. +* :ghpull:`20091`: Cleanup tight_layout. +* :ghpull:`20069`: Don't make VBoxDivider inherit from HBoxDivider. +* :ghpull:`20078`: Remove some usages of OrderedDict +* :ghpull:`20077`: Expire Artist.set() property reordering +* :ghpull:`20070`: Harmonize descriptions of the 'anchor' parameter. +* :ghpull:`20011`: Move development dependencies to dependencies page +* :ghpull:`20072`: Improve labeling in simple_axes_divider1 example. +* :ghpull:`20063`: Deprecate some untested, never used axes_grid1 methods. +* :ghpull:`20065`: Deprecate AxesDivider.append_axes(..., add_to_figure=True). +* :ghpull:`20066`: Cleanup axes_divider docstrings, and detail calculations. +* :ghpull:`20059`: Include left and right titles for labeling axes in qt axes selector. +* :ghpull:`20052`: Remove axes_grid/axisartist APIs deprecated in Matplotlib 3.3. +* :ghpull:`18807`: make FancyArrow animatable +* :ghpull:`15281`: Don't use ImageGrid in demo_text_rotation_mode. +* :ghpull:`20051`: Remove offsetbox APIs deprecated in Matplotlib 3.3. +* :ghpull:`14854`: Improved dev installation documentation +* :ghpull:`18900`: Enh better colorbar axes +* :ghpull:`20042`: DOC: fix typos +* :ghpull:`13860`: Deprecate {Locator,Formatter}.set_{{view,data}_interval,bounds}. +* :ghpull:`20028`: Shorten the repr of scaling transforms. +* :ghpull:`20027`: Fix axvspan for drawing slices on polar plots. +* :ghpull:`20024`: Small fixes to latex-related docs. +* :ghpull:`20023`: Simplify _redo_transform_rel_fig. +* :ghpull:`20012`: Fix default theta tick locations for non-full-circle polar plots. +* :ghpull:`20021`: DOC: fix typos +* :ghpull:`20013`: Move restriction of polar theta scales to ThetaAxis._set_scale. +* :ghpull:`20010`: DOC: fix heading level for plot_types/stats +* :ghpull:`20000`: Remove ax fixture from category tests. +* :ghpull:`20007`: Correct minor typos in legend.py and autoscale.py +* :ghpull:`20005`: DOC: Fix numpydoc syntax, and parameters names. +* :ghpull:`19996`: Small simplification to RadialLocator. +* :ghpull:`19968`: ENH: draw no output +* :ghpull:`19657`: Allow Selectors to be dragged from anywhere within their patch +* :ghpull:`19304`: Add legend title font properties +* :ghpull:`19977`: Fix doc build +* :ghpull:`19974`: CI: update the ssh key used to push the devdocs +* :ghpull:`9888`: Add an Annulus patch class +* :ghpull:`13680`: Update seaborn style +* :ghpull:`19967`: ENH: add user-facing no-output draw +* :ghpull:`19765`: ENH: use canvas renderer in draw +* :ghpull:`19525`: Don't create page transparency group in pdf output (for pdftex compat). +* :ghpull:`19952`: avoid implicit np.array -> float conversion +* :ghpull:`19931`: Remove now unused patches to ttconv. +* :ghpull:`19934`: Deprecate drawtype to RectangleSelector +* :ghpull:`19941`: Simplify 3D random walk example +* :ghpull:`19926`: Move custom scales/custom projections docs to module docstrings. +* :ghpull:`19898`: Remove 3.3 backend deprecations +* :ghpull:`19901`: Remove 3.3 rcParam deprecations +* :ghpull:`19900`: Remove 3.3 text deprecations +* :ghpull:`19922`: Remove 3.3 deprecated modules +* :ghpull:`19925`: Include projections.geo in api docs. +* :ghpull:`19924`: Discourage use of imread & improve its docs. +* :ghpull:`19866`: Switch to asciiart for boxplot illustration. +* :ghpull:`19912`: Add symlog to figureoptions scalings +* :ghpull:`19564`: Micro-optimize type1font loading +* :ghpull:`19623`: FIX: Contour lines rendered incorrectly when closed loops +* :ghpull:`19902`: Implement ``ArtistList.__[r]add__``. +* :ghpull:`19904`: Don't set zoom/pan cursor for non-navigatable axes. +* :ghpull:`19909`: Use unicode when interactively displaying 3d azim/elev. +* :ghpull:`19905`: pyplot: do not apply kwargs twice in to x/yticklabels +* :ghpull:`19126`: Move pixel ratio handling into FigureCanvasBase +* :ghpull:`19897`: DOC/MNT fix make clean for plot_types +* :ghpull:`19858`: Move Line2D units handling to Axes & deprecate "units finalize" signal. +* :ghpull:`19889`: Include length in ArtistList repr. +* :ghpull:`19887`: Fix E265 in test files. +* :ghpull:`19882`: Use ax.set() for a more compact notation of styling in plot types docs +* :ghpull:`17231`: Fix errobar order +* :ghpull:`19703`: DOC: new plot gallery +* :ghpull:`19825`: Factor out machinery for running subprocess tk tests. +* :ghpull:`19872`: Fix unit handling in errorbar for astropy. +* :ghpull:`19526`: Apply unit conversion early in errorbar(). +* :ghpull:`19855`: Correct handle default backend. +* :ghpull:`18216`: Combine Axes.{lines,images,collections,patches,text,tables} into single list +* :ghpull:`19853`: Consistent corner variables names in widgets.py +* :ghpull:`19575`: Deprecate Text.get_prop_tup. +* :ghpull:`19810`: Remove JPEG-specific parameters and rcParams. +* :ghpull:`19666`: Change dictionary to list of tuples to permit duplicate keys +* :ghpull:`19400`: Fix tk event coordinates in the presence of scrollbars. +* :ghpull:`19603`: Remove matplotlibrc.template. +* :ghpull:`19835`: Merge v3.4.x into master +* :ghpull:`19821`: Hide stderr output from subprocess call in test suite. +* :ghpull:`19819`: Correct small typos in _axes.py and legend.py +* :ghpull:`19795`: Remove usetex-related APIs deprecated in Matplotlib 3.3. +* :ghpull:`19789`: Fix zorder handling for OffsetBoxes and subclasses. +* :ghpull:`19796`: Expire ````keymap.all_axes````-related deprecations. +* :ghpull:`19806`: Remove outdated api changes notes. +* :ghpull:`19801`: Expire deprecation of mathtext.fallback_to_cm. +* :ghpull:`12744`: Explicit plotorder +* :ghpull:`19681`: Merge branch 'v3.4.x' into master +* :ghpull:`18971`: Switch to setuptools_scm. +* :ghpull:`19727`: DOC: simplify API index +* :ghpull:`19760`: Speed up _delete_parameter. +* :ghpull:`19756`: Minor cleanup of documentation guide +* :ghpull:`19752`: Cleanup backend_tools docstrings, and minor refactorings. +* :ghpull:`19552`: Remove scalarmappable private update attributes +* :ghpull:`19728`: Factor out clip-path attr handling in backend_svg. +* :ghpull:`19540`: Share subplots() label visibility handling with label_outer(). +* :ghpull:`19753`: Cleanup string formatting in backend_pgf. +* :ghpull:`19750`: Simplify maxdict implementation. +* :ghpull:`19749`: Remove unused _find_dedent_regex & _dedent_regex. +* :ghpull:`19751`: Update some matplotlib.lines docstrings. +* :ghpull:`13072`: ENH: add figure.legend; outside kwarg for better layout outside subplots +* :ghpull:`19740`: Minor backend docstring fixes. +* :ghpull:`19734`: Remove unused _fonts attribute in RendererSVG. +* :ghpull:`19733`: Reword AutoDateFormatter docs. +* :ghpull:`19718`: Small style fixes to matplotlibrc.template. +* :ghpull:`19679`: Add inheritance diagram to patches docs +* :ghpull:`19717`: Don't sort lexicographially entries in SVG output. +* :ghpull:`19716`: Fix colon placement in issue template. +* :ghpull:`19704`: Cleanup license page in docs +* :ghpull:`19487`: Deprecate unused \*args to print_. +* :ghpull:`19654`: Dedupe various method implementations using functools.partialmethod. +* :ghpull:`19655`: Deprecate Tick.apply_tickdir. +* :ghpull:`19653`: deprecate_privatize_attribute also works for privatizing methods. +* :ghpull:`19646`: Add angle setter/getter to Rectangle +* :ghpull:`19659`: Improve docs for rgba conversion +* :ghpull:`19641`: Fix Bbox.frozen() not copying minposx/minposy +* :ghpull:`19626`: Clean up E265 in examples. +* :ghpull:`19622`: Prefer Axes.remove() over Figure.delaxes() in docs. +* :ghpull:`19621`: Dedupe docstrings of Figure.{get_axes,axes}. +* :ghpull:`19600`: DOC: better intro for dates.py +* :ghpull:`19606`: Remove versionadded notes; correct doc link +* :ghpull:`19620`: Remove suggestion to remove rk4/rk45 integrators from streamplot. +* :ghpull:`19586`: DOC: more improve date example +* :ghpull:`19566`: add docstring to ax.quiver +* :ghpull:`19601`: Handle None entries in sys.modules. +* :ghpull:`19517`: Deprecate toplevel is_url, URL_REGEX helpers. +* :ghpull:`19570`: Dedupe part of error message in check_in_list. +* :ghpull:`14508`: Add force_zorder parameter +* :ghpull:`19585`: Deprecate trivial helpers in style.core. +* :ghpull:`19534`: BUG: fill_between with interpolate=True and NaN. +* :ghpull:`18887`: FIX: Generalize Colorbar Scale Handling +* :ghpull:`16788`: Adding png image return for inline backend figures with _repr_html_ + +Issues (187): + +* :ghissue:`21518`: [Bug]: Datetime axis with usetex is unclear +* :ghissue:`21509`: [Bug]: Text sometimes is missing when figure saved to EPS +* :ghissue:`21569`: [Bug]: AttributeError: 'NoneType' object has no attribute 'dpi' after drawing and removing contours inside artist +* :ghissue:`21612`: [Bug]: Security.md out of date +* :ghissue:`21608`: [Doc]: ``ax.voxels`` links to wrong method. +* :ghissue:`21528`: [Doc]: Outdated QT_API docs +* :ghissue:`21517`: [Bug]: this example shows ok on matplotlib-3.4.3, but not in matplotlib-3.5.0 master of october 30th +* :ghissue:`21548`: [Bug]: blocking_input +* :ghissue:`21552`: [Bug]: eventplot cannot handle multiple datetime-based series +* :ghissue:`21441`: [Bug]: axes(position = [...]) behavior +* :ghissue:`10346`: Passing clim as keyword argument to pcolormesh does not change limits. +* :ghissue:`21480`: [Bug]: Subfigure breaks for some ``Gridspec`` slices when using ``constrained_layout`` +* :ghissue:`20989`: [Bug]: regression with setting ticklabels for colorbars in matplotlib 3.5.0b1 +* :ghissue:`21474`: [Doc]: Suggestion to use PIL.image.open is not a 1:1 replacement for imread +* :ghissue:`19634`: Multicursor docstring missing a Parameters Section +* :ghissue:`20847`: [Bug]: Contourf not filling contours. +* :ghissue:`21300`: [Bug]: zooming in on contour plot gives false extra contour lines +* :ghissue:`21466`: [Bug]: EPS export shows hidden tick labels when using tex for text rendering +* :ghissue:`21463`: [Bug]: Plotting lables with Greek latters in math mode produces Parsing error when plt.show() runs +* :ghissue:`20534`: Document formatting for for sections +* :ghissue:`21246`: [Doc]: Install info takes up too much room on new front page +* :ghissue:`21432`: [Doc]: Double clicking parameter name also highlights next item of text +* :ghissue:`21310`: [Bug]: contour on 3d plot fails if x and y are 1d and different lengths +* :ghissue:`18213`: Figure out why test_interactive_backend fails on Travis macOS +* :ghissue:`21090`: [MNT]: Should set_size_inches be updated to use device_pixel_ratio? +* :ghissue:`13948`: Allow colorbar.ax.set_ylim to set the colorbar limits? +* :ghissue:`21314`: Inconsistensy in ``pyplot.pcolormesh`` docstring regarding default value for ``shading`` +* :ghissue:`21320`: [Doc]: Incorrect image caption in imshow() example +* :ghissue:`21311`: [Doc]: dead link for agg +* :ghissue:`20929`: [Bug]: PyPy Win64 wheels use incorrect version +* :ghissue:`21202`: [Bug]: python3.7/site-packages/matplotlib/ft2font.so: Undefined symbol "FT_Done_Glyph" +* :ghissue:`20932`: Qt Ctrl-C broken on windows +* :ghissue:`21230`: [Doc]: [source] links is devdocs are broken +* :ghissue:`20906`: 3.5.0b1: ax.contour generates different artists +* :ghissue:`21161`: [Doc]: In new docs, "Usage guide" entry in the top menu does not link to the "Usage guide" +* :ghissue:`21016`: [Bug] Error: 'PathCollection' object has no attribute 'do_3d_projection' when doing contourf in 3d with extend = 'both' +* :ghissue:`21135`: [Doc]: Data parameter description is not always replaced +* :ghissue:`4132`: Support clim kwarg in pcolor-type plots +* :ghissue:`21110`: Qt swapping ctrl and cmd on OSX +* :ghissue:`20912`: [ENH]: data kwarg support for mplot3d +* :ghissue:`15005`: Cleanup API for setting ticks +* :ghissue:`21095`: [ENH]: A data-type check is missed in cm.ScalarMappable.set_array() +* :ghissue:`7711`: Colorbar: changing the norm does not update the Formatter +* :ghissue:`18925`: Removing axes created by twiny() leads to an error +* :ghissue:`21057`: [Bug]: distutils deprecation +* :ghissue:`21024`: [ENH]: Cairo backends do not fully support HiDPI +* :ghissue:`20811`: Python 3.10 manylinux wheels +* :ghissue:`11509`: On making the rc-validators function know the rcParam affected instance +* :ghissue:`20516`: Sketch params ignored when using PGF backend +* :ghissue:`20963`: [Bug]: broken 'proportional' colorbar when using contourf+cmap+norm+extend +* :ghissue:`13974`: [DOC] Undocumented behavior in streamplot +* :ghissue:`16251`: API changes are too hard to find in the rendered docs +* :ghissue:`20770`: [Doc]: How to replicate behaviour of ``plt.gca(projection=...)``? +* :ghissue:`17052`: Colorbar update error with clim change in multi_image.py example +* :ghissue:`4387`: make ``Normalize`` objects notifiy scalar-mappables on changes +* :ghissue:`20001`: rename fig.draw_no_output +* :ghissue:`20936`: [Bug]: edgecolor 'auto' doesn't work properly +* :ghissue:`20909`: [Bug]: Animation error message +* :ghissue:`6864`: Add release dates to what's new page +* :ghissue:`20905`: [Bug]: error plotting z-axis array with np.nan -- does not plot with cmap option (surface plot) +* :ghissue:`20618`: BUG: Lost functionality of interactive selector update +* :ghissue:`20791`: [Bug]: spines and ticklabels +* :ghissue:`20723`: Adding a legend to a ``SubFigure`` doesn't work +* :ghissue:`20637`: PyPy wheels are pinned to v3.3, so pypy-based wheels for latest versions are not available +* :ghissue:`19160`: pypy failures +* :ghissue:`20385`: Add ']->' , '<-[' arrowstyles +* :ghissue:`19016`: Move away from set_ticklabels() +* :ghissue:`20800`: [Bug]: Setting backend in custom style sheet raises UserWarning +* :ghissue:`20809`: [Bug]: \Cap and \Cup in mathtext are inconsistent +* :ghissue:`20762`: [Doc]: Add external resources to devdoc landing page +* :ghissue:`18490`: Add a method to access the list of registered colormaps +* :ghissue:`20666`: Interactive SpanSelector no longer notifies when the selector is removed by an "empty" click +* :ghissue:`20552`: Expose legend's line: ``legline._legmarker`` as public +* :ghissue:`18391`: Bug? Legend Picking Not Working on Marker +* :ghissue:`11357`: Unable to retrieve marker from legend handle +* :ghissue:`2035`: legend marker update bug +* :ghissue:`19748`: Incorrect & inconsistent coloring in .imshow() with LogNorm +* :ghissue:`18735`: imshow padding around NaN values +* :ghissue:`7928`: [Bug] backend_bases.key_press_handler sneakily uses digit keys +* :ghissue:`20802`: Add ability to disable callbacks temporarily +* :ghissue:`16470`: Inconsistent Corner Masking w/ plot_surface +* :ghissue:`12395`: Rendering issue occurs when plotting 3D surfaces at a discontinuity +* :ghissue:`8222`: matplotlib 3D surface - gaps / holes in surface +* :ghissue:`4941`: Axes3d plot_surface not supporting masked arrays? +* :ghissue:`487`: Plotting masked arrays with plot_surface() +* :ghissue:`20794`: [Doc]: "Bachelor's degrees by gender" example is more or less dufte +* :ghissue:`20557`: Have ``[Source]`` in api docs link to github +* :ghissue:`20754`: [Doc]: legend guide should be OO +* :ghissue:`17770`: animation.save and fig.savefig interfere with each other and raise StopIteration +* :ghissue:`20785`: [Bug]: Colorbar creation from pcolormesh with cell specific alpha values +* :ghissue:`19843`: collection with alpha + colorer +* :ghissue:`20698`: collections.Collections offset improvements +* :ghissue:`17774`: Cannot make Latex plots when Pandas dataframe has underscore in variable name +* :ghissue:`19884`: Better document Axes.set() +* :ghissue:`20760`: [Bug]: subfigure position shifts on y-axis when x kwarg added to supxlabel +* :ghissue:`20296`: colorbar set_ticklabels - text properties not working +* :ghissue:`18191`: PostScript Type42 embedding is broken in various ways +* :ghissue:`11303`: Using fonttype 42 will make the produced PDF size considerably larger when the image has Chinese characters +* :ghissue:`20735`: The top level of the docs needs modification +* :ghissue:`20684`: make_keyword_only doesn't work for pyplot-wrapped methods +* :ghissue:`20635`: DOC: Document patch deprecation +* :ghissue:`17473`: Issue with appearance of RectangleSelector +* :ghissue:`20616`: Type 42 chars beyond BMP not displayed in PDF +* :ghissue:`20658`: MAINT: CircleCI build merged PRs +* :ghissue:`18312`: Add easily comparable version info to toplevel +* :ghissue:`20665`: interactive SpanSelector incorrectly forces axes limits to include 0 +* :ghissue:`20614`: Missing kerning in PDFs with Type 42 font +* :ghissue:`20640`: Column direction breaks label mode L for AxesGrid. +* :ghissue:`20581`: Change in custom norm colour map display +* :ghissue:`20595`: Triple and quadruple dot Mathtext accents don't stack or align. +* :ghissue:`19755`: Avoid showing a black background before the plot is ready with Qt5agg backend +* :ghissue:`10235`: Why not get the same clear image on a high-resolution screen? +* :ghissue:`20479`: ColorbarAxes is an imperfect proxy for the Axes passed to Colorbar +* :ghissue:`18965`: Figure options with qt backend breaks +* :ghissue:`19256`: New Styling for Sliders +* :ghissue:`14148`: zorder ignored in mplot3d +* :ghissue:`20523`: plot_directive is confused by include directives, part 2 (context option) +* :ghissue:`17860`: Plot directive may be confused by ``..include::`` +* :ghissue:`19431`: Tricontour documentation and examples should be updated in line with contour +* :ghissue:`20508`: rcParams['axes.autolimit_mode'] = 'round_numbers' is broken +* :ghissue:`20289`: Simplify font setting in usetex mode +* :ghissue:`20370`: Test Coverage for TextBox +* :ghissue:`20522`: Improve 'Writing ReST Pages' section on docs +* :ghissue:`19259`: Set legend title font properties +* :ghissue:`20049`: add legend.labelcolor "argument" to mplstyle stylesheet +* :ghissue:`20452`: Wrong/not useful error message when plotting incompatible x and y +* :ghissue:`20266`: "$$" can not be displayed by ax.text() +* :ghissue:`20517`: Wrong shape of Z in documentation of contour +* :ghissue:`19423`: Switch to pydata-sphinx-theme +* :ghissue:`20435`: Legend Text's ``axes`` attribute is ``None`` +* :ghissue:`20379`: Change name of variables in histogram example +* :ghissue:`20440`: Wrong text vertical position with LaTeX enabled +* :ghissue:`10042`: Inconsistent use of graphicx and color packages in LaTeX preambles +* :ghissue:`4482`: PGF Backend: "Dimension too large" error while processing log-scale plot +* :ghissue:`20324`: New colorbar doesn't handle norms without a scale properly... +* :ghissue:`17508`: Quadmesh.set_array should validate dimensions +* :ghissue:`20372`: Incorrect axes positioning in axes_grid.Grid with direction='column' +* :ghissue:`19419`: Dev version hard to check +* :ghissue:`17310`: Matplotlib git master version fails to pass serveral pytest's tests. +* :ghissue:`7742`: plot_date() after axhline() doesn't rescale axes +* :ghissue:`20322`: QuadMesh default for shading inadvertently changed. +* :ghissue:`9653`: SVG savefig + LaTeX extremely slow on macOS +* :ghissue:`20099`: ``fontset`` from ``mathtext`` throwing error after setting Text ``font=`` +* :ghissue:`18399`: How to get Quadmesh coordinates +* :ghissue:`15432`: Add support in matplotlib.pyplot.imshow for float16 +* :ghissue:`20298`: plt.quiver linestyle option doesn't work?..... +* :ghissue:`19075`: Qt backend's Figure options to support axis units +* :ghissue:`15039`: NonUniformImage wrong image when using large values for axis +* :ghissue:`18499`: Saving as a pdf ignores ``set_clip_path`` when there is more than one of them. +* :ghissue:`15600`: Grid disappear after pcolormesh apply +* :ghissue:`20080`: API docs currently include entries for class ``__dict__``, ``__module__``, ``__weakref__`` +* :ghissue:`20159`: Zoom in NavigationToolbar2Tk stops working after updating the canvas figure. +* :ghissue:`17007`: Computer Modern Glyph Error +* :ghissue:`19494`: Update azure ubuntu images to 18.04, or update texlive in CI +* :ghissue:`18841`: ScalarMappable should copy its input and allow non-arrays +* :ghissue:`20121`: Adding cmocean and CMasher to the colormaps tutorial +* :ghissue:`18154`: Deprecate plot_date() +* :ghissue:`7413`: Autoscaling has fundamental problems +* :ghissue:`19627`: Replace use of Python/C API with numpy::array_view in _tri.cpp and qhull_wrap.c +* :ghissue:`19111`: plot_directive errantly tries to run code +* :ghissue:`11007`: BUG: Plot directive fails if its content ends with a normal text line (sphinxext) +* :ghissue:`19929`: Selecting axes when customizing gives +* :ghissue:`19578`: bisect very hard with rcParam changes +* :ghissue:`19506`: Allow saving PDF files without a page group +* :ghissue:`19906`: symlog is not in scale setting +* :ghissue:`19568`: Contour lines are rendered incorrectly when closed loops +* :ghissue:`19890`: Should ArtistList implement ``__add__``? +* :ghissue:`14405`: ENH: Add HiDPI physical to logical pixel ratio property +* :ghissue:`17139`: errorbar doesn't follow plot order +* :ghissue:`18277`: Create new sphinx gallery page for "Chart Types" +* :ghissue:`15446`: the python script in Catalina dock icon display wrong +* :ghissue:`19848`: ValueError: Key backend: '' is not a valid value for backend +* :ghissue:`1622`: zorder is not respected by all parts of ``errorbar`` +* :ghissue:`17247`: Move towards making Axes.lines, Axes.patches, ... read-only views of a single child list. +* :ghissue:`19842`: UserWarning: "Trying to register the cmap '...' which already exists" is not very helpful. +* :ghissue:`7962`: pip interprets Matplotlib dev version as stable +* :ghissue:`19607`: Curves with same label not appearing in Figure options (only the last one) +* :ghissue:`17584`: NavigationToolbar2Tk behave unexpected when using it in with Tkinter Canvas +* :ghissue:`19838`: Unexpected behaviour of imshow default interpolation +* :ghissue:`7650`: anchored_artists don't support zorder argument +* :ghissue:`19687`: License doc cleanup +* :ghissue:`19635`: Multicursor updates to events for any axis +* :ghissue:`17967`: Document how to use mathtext to obtain unicode minus instead of dashes for negative numbers +* :ghissue:`8519`: Closed figures linger in memory +* :ghissue:`14175`: RFC: Allow users to force zorder in 3D plots +* :ghissue:`19464`: Quiver docs don't have a return section +* :ghissue:`18986`: fill_between issue with interpolation & NaN diff --git a/doc/users/prev_whats_new/github_stats_3.5.1.rst b/doc/users/prev_whats_new/github_stats_3.5.1.rst new file mode 100644 index 000000000000..7eb37b769d6c --- /dev/null +++ b/doc/users/prev_whats_new/github_stats_3.5.1.rst @@ -0,0 +1,151 @@ +.. _github-stats-3-5-1: + +GitHub statistics for 3.5.1 (Dec 11, 2021) +========================================== + +GitHub statistics for 2021/11/16 (tag: v3.5.0) - 2021/12/11 + +These lists are automatically generated, and may be incomplete or contain duplicates. + +We closed 29 issues and merged 84 pull requests. +The full list can be seen `on GitHub `__ + +The following 17 authors contributed 123 commits. + +* Antony Lee +* Constantine Evans +* David Stansby +* Elliott Sales de Andrade +* franzhaas +* Greg Lucas +* Hansin Ahuja +* Hood Chatham +* Jake Lishman +* Jody Klymak +* Matthias Bussonnier +* Ryan May +* Steffen Rehberg +* Sven Eschlbeck +* sveneschlbeck +* Thomas A Caswell +* Tim Hoffmann + +GitHub issues and pull requests: + +Pull Requests (84): + +* :ghpull:`21926`: Backport PR #21913 on branch v3.5.x (Make colorbar boundaries work again) +* :ghpull:`21924`: Backport PR #21861 on branch v3.5.x (DOC: Small formatting improvement to set_markevery) +* :ghpull:`21913`: Make colorbar boundaries work again +* :ghpull:`21922`: Backport PR #21753 on branch v3.5.x (DOC: update anatomy of figure) +* :ghpull:`21861`: DOC: Small formatting improvement to set_markevery +* :ghpull:`21919`: Fix use_data_coordinates docstring +* :ghpull:`21912`: Backport PR #21900 on branch v3.5.x (Include test notebooks in test package) +* :ghpull:`21900`: Include test notebooks in test package +* :ghpull:`21908`: Backport PR #21834 on branch v3.5.x (MAINT Fix signature qhull version function ) +* :ghpull:`21907`: Backport PR #21905 on branch v3.5.x (Fix image testing decorator in pytest importlib mode) +* :ghpull:`21906`: Backport PR #21773 on branch v3.5.x (FIX: Reset label of axis to center) +* :ghpull:`21834`: MAINT Fix signature qhull version function +* :ghpull:`21905`: Fix image testing decorator in pytest importlib mode +* :ghpull:`21773`: FIX: Reset label of axis to center +* :ghpull:`21902`: Backport PR #21884 on branch v3.5.x (FIX: be more careful about coercing unit-full containers to ndarray) +* :ghpull:`21884`: FIX: be more careful about coercing unit-full containers to ndarray +* :ghpull:`21899`: Backport PR #21859 on branch v3.5.x (Fix streamline plotting from upper edges of grid) +* :ghpull:`21859`: Fix streamline plotting from upper edges of grid +* :ghpull:`21896`: Backport PR #21890 on branch v3.5.x (Drop retina images when building PDF docs) +* :ghpull:`21891`: Backport PR #21887 on branch v3.5.x (Make figure target links relative) +* :ghpull:`21883`: Backport PR #21872 on branch v3.5.x (FIX: colorbars with NoNorm) +* :ghpull:`21872`: FIX: colorbars with NoNorm +* :ghpull:`21869`: Backport PR #21866 on branch v3.5.x (Shorten some inset_locator docstrings.) +* :ghpull:`21866`: Shorten some inset_locator docstrings. +* :ghpull:`21865`: Backport PR #21864 on branch v3.5.x (Delete "Load converter" example) +* :ghpull:`21864`: Delete "Load converter" example +* :ghpull:`21857`: Backport PR #21837 on branch v3.5.x (Display example figures in a single column) +* :ghpull:`21856`: Backport PR #21853 on branch v3.5.x (DOC: Fix Annotation arrow style reference example) +* :ghpull:`21853`: DOC: Fix Annotation arrow style reference example +* :ghpull:`21852`: Backport PR #21818 on branch v3.5.x (Fix collections coerce float) +* :ghpull:`21818`: Fix collections coerce float +* :ghpull:`21849`: Backport PR #21845 on branch v3.5.x (FIX: bbox subfigures) +* :ghpull:`21845`: FIX: bbox subfigures +* :ghpull:`21832`: Backport PR #21820 on branch v3.5.x (Drop setuptools-scm requirement in wheels) +* :ghpull:`21820`: Drop setuptools-scm requirement in wheels +* :ghpull:`21829`: Backport PR #21823 on branch v3.5.x (DOC: Misc rst syntax fixes) +* :ghpull:`21823`: DOC: Misc rst syntax fixes +* :ghpull:`21826`: Backport PR #21800 on branch v3.5.x (DOC: Update Basic Usage tutorial) +* :ghpull:`21814`: Manual backport of #21794 +* :ghpull:`21812`: Backport #21641 +* :ghpull:`21810`: Backport PR #21743 on branch v3.5.x (Clarify Annotation arrowprops docs) +* :ghpull:`21808`: Backport PR #21785 on branch v3.5.x (Fix ConciseDateFormatter offset during zoom) +* :ghpull:`21807`: Backport PR #21791 on branch v3.5.x (Refix check for manager presence in deprecated blocking_input.) +* :ghpull:`21806`: Backport PR #21663 on branch v3.5.x (Use standard subplot window in macosx backend) +* :ghpull:`21785`: Fix ConciseDateFormatter offset during zoom +* :ghpull:`21804`: Backport PR #21659 on branch v3.5.x (Fix PDF contents) +* :ghpull:`21791`: Refix check for manager presence in deprecated blocking_input. +* :ghpull:`21793`: Backport PR #21787 on branch v3.5.x (Fixes row/column mixup in GridSpec height_ratios documentation.) +* :ghpull:`21787`: Fixes row/column mixup in GridSpec height_ratios documentation. +* :ghpull:`21778`: Backport PR #21705 on branch v3.5.x (MNT: make print_figure kwarg wrapper support py311) +* :ghpull:`21779`: Backport PR #21751 on branch v3.5.x (FIX: manual colorbars and tight layout) +* :ghpull:`21777`: Backport PR #21758 on branch v3.5.x (FIX: Make sure a renderer gets attached to figure after draw) +* :ghpull:`21751`: FIX: manual colorbars and tight layout +* :ghpull:`21705`: MNT: make print_figure kwarg wrapper support py311 +* :ghpull:`21758`: FIX: Make sure a renderer gets attached to figure after draw +* :ghpull:`21775`: Backport PR #21771 on branch v3.5.x (DOC: fix missing ref) +* :ghpull:`21770`: Backport of PR #21631 on v3.5.x +* :ghpull:`21765`: Backport PR #21741 on branch v3.5.x (Reduce do_3d_projection deprecation warnings in external artists) +* :ghpull:`21764`: Backport PR #21762 on branch v3.5.x (FIX: align_x/ylabels) +* :ghpull:`21741`: Reduce do_3d_projection deprecation warnings in external artists +* :ghpull:`21762`: FIX: align_x/ylabels +* :ghpull:`21759`: Backport PR #21757 on branch v3.5.x (Fix doc typo.) +* :ghpull:`21704`: FIX: deprecation of render keyword to do_3d_projection +* :ghpull:`21730`: Backport PR #21727 on branch v3.5.x (Doc fix colormap inaccuracy) +* :ghpull:`21663`: Use standard subplot window in macosx backend +* :ghpull:`21725`: Backport PR #21681 on branch v3.5.x (Bind subplot_tool more closely to target figure.) +* :ghpull:`21665`: Include test notebooks in test package +* :ghpull:`21721`: Backport PR #21720 on branch v3.5.x (Fix compiler configuration priority for FreeType build) +* :ghpull:`21720`: Fix compiler configuration priority for FreeType build +* :ghpull:`21715`: Backport PR #21714 on branch v3.5.x (DOC: note renaming of config.cfg.template to mplconfig.cfg.template) +* :ghpull:`21706`: Backport PR #21703 on branch v3.5.x (Changed the link to the correct citing example) +* :ghpull:`21691`: Backport PR #21686 on branch v3.5.x (FIX: colorbar for horizontal contours) +* :ghpull:`21689`: Backport PR #21676 on branch v3.5.x (Fix boundary norm negative) +* :ghpull:`21686`: FIX: colorbar for horizontal contours +* :ghpull:`21681`: Bind subplot_tool more closely to target figure. +* :ghpull:`21676`: Fix boundary norm negative +* :ghpull:`21685`: Backport PR #21658 on branch v3.5.x (Validate that input to Poly3DCollection is a list of 2D array-like) +* :ghpull:`21684`: Backport PR #21662 on branch v3.5.x (FIX: put newline in matplotlibrc when setting default backend) +* :ghpull:`21658`: Validate that input to Poly3DCollection is a list of 2D array-like +* :ghpull:`21662`: FIX: put newline in matplotlibrc when setting default backend +* :ghpull:`21651`: Backport PR #21626 on branch v3.5.x (Added the definition of Deprecation and made Deprecation Process clearer) +* :ghpull:`21626`: Added the definition of Deprecation and made Deprecation Process clearer +* :ghpull:`21137`: Small cleanups to colorbar. + +Issues (29): + +* :ghissue:`21909`: [Bug]: Matplotlib is unable to apply the boundaries in the colorbar after updating to 3.5.0 +* :ghissue:`21654`: [Bug]: test_nbagg_01.ipynb not installed +* :ghissue:`21885`: [Bug]: test decorator breaks with new pytest importlib mode +* :ghissue:`21772`: [Bug]: cannot reset label of axis to center +* :ghissue:`21669`: [Bug]: Matplotlib 3.5 breaks unyt integration of error bars +* :ghissue:`21649`: [Bug]: Startpoints in streamplot fail on right and upper edges +* :ghissue:`21870`: [Bug]: Colormap + NoNorm only plots one color under ``matplotlib`` 3.5.0 +* :ghissue:`21882`: [Bug]: Colorbar does not work for negative values with contour/contourf +* :ghissue:`21803`: [Bug]: using ``set_offsets`` on scatter object raises TypeError +* :ghissue:`21839`: [Bug]: Top of plot clipped when using Subfigures without suptitle +* :ghissue:`21841`: [Bug]: Wrong tick labels and colorbar of discrete normalizer +* :ghissue:`21783`: [MNT]: wheel of 3.5.0 apears to depend on setuptools-scm which apears to be unintentional +* :ghissue:`21733`: [Bug]: Possible bug on arrows in annotation +* :ghissue:`21749`: [Bug]: Regression on ``tight_layout`` when manually adding axes for colorbars +* :ghissue:`19197`: Unexpected error after using Figure.canvas.draw on macosx backend +* :ghissue:`13968`: ``ax.get_xaxis().get_minorticklabels()`` always returns list of empty strings +* :ghissue:`7550`: Draw not caching with macosx backend +* :ghissue:`21740`: [Bug]: unavoidable ``DeprecationWarning`` when using ``Patch3D`` +* :ghissue:`15884`: DOC: Error in colormap manipulation tutorial +* :ghissue:`21648`: [Bug]: subplot parameter window appearing 1/4 size on macosx +* :ghissue:`21702`: [Doc]: Wrong link to the ready-made citation entry +* :ghissue:`21683`: [Bug]: add_lines broken for horizontal colorbars +* :ghissue:`21680`: [MNT]: macosx subplot parameters multiple windows +* :ghissue:`21679`: [MNT]: Close subplot_parameters window when main figure closes +* :ghissue:`21671`: [Bug]: 3.5.0 colorbar ValueError: minvalue must be less than or equal to maxvalue +* :ghissue:`21652`: [Bug]: ax.add_collection3d throws warning Mean of empty slice +* :ghissue:`21660`: [Bug]: mplsetup.cfg parsing issue +* :ghissue:`21668`: [Bug]: New plot directive error in 3.5.0 +* :ghissue:`21393`: [Doc]: describe deprecation process more explicitly diff --git a/doc/users/prev_whats_new/github_stats_3.5.2.rst b/doc/users/prev_whats_new/github_stats_3.5.2.rst new file mode 100644 index 000000000000..66f53d8e3672 --- /dev/null +++ b/doc/users/prev_whats_new/github_stats_3.5.2.rst @@ -0,0 +1,335 @@ +.. _github-stats-3-5-2: + +GitHub statistics for 3.5.2 (May 02, 2022) +========================================== + +GitHub statistics for 2021/12/11 (tag: v3.5.1) - 2022/05/02 + +These lists are automatically generated, and may be incomplete or contain duplicates. + +We closed 61 issues and merged 222 pull requests. +The full list can be seen `on GitHub `__ + +The following 30 authors contributed 319 commits. + +* Adeel Hassan +* Aitik Gupta +* Andrew Fennell +* andrzejnovak +* Antony Lee +* Clément Phan +* daniilS +* David Poznik +* David Stansby +* dependabot[bot] +* Edouard Berthe +* Elliott Sales de Andrade +* Greg Lucas +* Hassan Kibirige +* Jake VanderPlas +* Jay Stanley +* Jody Klymak +* MAKOMO +* Matthias Bussonnier +* Niyas Sait +* Oscar Gustafsson +* Pieter P +* Qijia Liu +* Quentin Peter +* Raphael Quast +* richardsheridan +* root +* Steffen Rehberg +* Thomas A Caswell +* Tim Hoffmann + +GitHub issues and pull requests: + +Pull Requests (222): + +* :ghpull:`22963`: Backport PR #22957 on branch v3.5.x (fix "is" comparison for np.array) +* :ghpull:`22951`: Backport PR #22946: FIX: Handle no-offsets in collection datalim +* :ghpull:`22957`: fix "is" comparison for np.array +* :ghpull:`22962`: Backport PR #22961 on branch v3.5.x (Raised macosx memory leak threshold) +* :ghpull:`22961`: Raised macosx memory leak threshold +* :ghpull:`22945`: FIX: Handle no-offsets in collection datalim +* :ghpull:`22946`: FIX: Handle no-offsets in collection datalim (alternative) +* :ghpull:`22944`: Backport PR #22907 on branch v3.5.x (Fix quad mesh cursor data) +* :ghpull:`22943`: Backport PR #22923 on branch v3.5.x (Fixed _upcast_err docstring and comments in _axes.py) +* :ghpull:`22907`: Fix quad mesh cursor data +* :ghpull:`22923`: Fixed _upcast_err docstring and comments in _axes.py +* :ghpull:`22876`: Backport PR #22560 on branch v3.5.x (Improve pandas/xarray/... conversion) +* :ghpull:`22942`: Backport PR #22933 on branch v3.5.x (Adjusted wording in pull request guidelines) +* :ghpull:`22941`: Backport PR #22898 on branch v3.5.x (Only set Tk scaling-on-map for Windows systems) +* :ghpull:`22935`: Backport PR #22002: Fix TkAgg memory leaks and test for memory growth regressions +* :ghpull:`22898`: Only set Tk scaling-on-map for Windows systems +* :ghpull:`22933`: Adjusted wording in pull request guidelines +* :ghpull:`22002`: Fix TkAgg memory leaks and test for memory growth regressions +* :ghpull:`22924`: Fix gtk4 incorrect import. +* :ghpull:`22922`: Backport PR #22904 on branch v3.5.x (Fixed typo in triage acknowledgment) +* :ghpull:`22904`: Fixed typo in triage acknowledgment +* :ghpull:`22890`: DOC: add ipykernel to list of optional dependencies +* :ghpull:`22878`: Backport PR #22871 on branch v3.5.x (Fix year offset not always being added) +* :ghpull:`22871`: Fix year offset not always being added +* :ghpull:`22844`: Backport PR #22313 on branch v3.5.x (Fix colorbar exponents) +* :ghpull:`22560`: Improve pandas/xarray/... conversion +* :ghpull:`22846`: Backport PR #22284 on branch v3.5.x (Specify font number for TTC font subsetting) +* :ghpull:`22284`: Specify font number for TTC font subsetting +* :ghpull:`22845`: Backport PR #22199 on branch v3.5.x (DOC: git:// is deprecated.) +* :ghpull:`22837`: Backport PR #22807 on branch v3.5.x (Replace quiver dpi callback with reinit-on-dpi-changed.) +* :ghpull:`22838`: Backport PR #22806 on branch v3.5.x (FIX: callback for subfigure uses parent) +* :ghpull:`22832`: Backport PR #22767 on branch v3.5.x (Fixed bug in find_nearest_contour) +* :ghpull:`22767`: Fixed bug in find_nearest_contour +* :ghpull:`22807`: Replace quiver dpi callback with reinit-on-dpi-changed. +* :ghpull:`22806`: FIX: callback for subfigure uses parent +* :ghpull:`22737`: Backport PR #22138: Fix clearing subfigures +* :ghpull:`22735`: MNT: prefer Figure.clear() as canonical over Figure.clf() +* :ghpull:`22783`: Backport PR #22732: FIX: maybe improve renderer dance +* :ghpull:`22748`: Backport PR #22628 on branch v3.5.x (Add RuntimeWarning guard around division-by-zero) +* :ghpull:`22732`: FIX: maybe improve renderer dance +* :ghpull:`22764`: Backport PR #22756 on branch v3.5.x (Use system distutils instead of the setuptools copy) +* :ghpull:`22780`: Backport PR #22766 on branch v3.5.x (FIX: account for constant deprecations in Pillow 9.1) +* :ghpull:`22781`: Backport PR #22776 on branch v3.5.x (Fix colorbar stealing from a single axes and with panchor=False.) +* :ghpull:`22782`: Backport PR #22774 on branch v3.5.x (Remove outdated doc for pie chart) +* :ghpull:`22774`: Remove outdated doc for pie chart +* :ghpull:`22776`: Fix colorbar stealing from a single axes and with panchor=False. +* :ghpull:`22766`: FIX: account for deprecations of constant in Pillow 9.1 +* :ghpull:`22756`: Use system distutils instead of the setuptools copy +* :ghpull:`22750`: Backport PR #22743: Fix configure_subplots with tool manager +* :ghpull:`22743`: Fix configure_subplots with tool manager +* :ghpull:`22628`: Add RuntimeWarning guard around division-by-zero +* :ghpull:`22736`: Backport PR #22719 on branch v3.5.x (Fix incorrect deprecation warning) +* :ghpull:`22719`: Fix incorrect deprecation warning +* :ghpull:`22138`: Fix clearing subfigures +* :ghpull:`22729`: Backport PR #22711 on branch v3.5.x (RangeSlider handle set_val bugfix) +* :ghpull:`22711`: RangeSlider handle set_val bugfix +* :ghpull:`22701`: Backport PR #22691 on branch v3.5.x (FIX: remove toggle on QuadMesh cursor data) +* :ghpull:`22723`: Backport PR #22716 on branch v3.5.x (DOC: set canonical) +* :ghpull:`22703`: Backport PR #22689 on branch v3.5.x (Fix path_effects to work on text with spaces only) +* :ghpull:`22689`: Fix path_effects to work on text with spaces only +* :ghpull:`22691`: FIX: remove toggle on QuadMesh cursor data +* :ghpull:`22696`: Backport PR #22693 on branch v3.5.x (Remove QuadMesh from mouseover set.) +* :ghpull:`22693`: Remove QuadMesh from mouseover set. +* :ghpull:`22647`: Backport PR #22429 on branch v3.5.x (Enable windows/arm64 platform) +* :ghpull:`22653`: Simplify FreeType version check to avoid packaging +* :ghpull:`22646`: Manual backport of pr 22635 on v3.5.x +* :ghpull:`22429`: Enable windows/arm64 platform +* :ghpull:`22635`: FIX: Handle inverted colorbar axes with extensions +* :ghpull:`22313`: Fix colorbar exponents +* :ghpull:`22619`: Backport PR #22611 on branch v3.5.x (FIX: Colorbars check for subplotspec attribute before using) +* :ghpull:`22618`: Backport PR #22617 on branch v3.5.x (Bump actions/checkout from 2 to 3) +* :ghpull:`22611`: FIX: Colorbars check for subplotspec attribute before using +* :ghpull:`22617`: Bump actions/checkout from 2 to 3 +* :ghpull:`22595`: Backport PR #22005: Further defer backend selection +* :ghpull:`22602`: Backport PR #22596 on branch v3.5.x (Fix backend in matplotlibrc if unset in mplsetup.cfg) +* :ghpull:`22596`: Fix backend in matplotlibrc if unset in mplsetup.cfg +* :ghpull:`22597`: Backport PR #22594 on branch v3.5.x (FIX: do not pass dashes to collections in errorbar) +* :ghpull:`22594`: FIX: do not pass dashes to collections in errorbar +* :ghpull:`22593`: Backport PR #22559 on branch v3.5.x (fix: fill stairs should have lw=0 instead of edgecolor="none") +* :ghpull:`22005`: Further defer backend selection +* :ghpull:`22559`: fix: fill stairs should have lw=0 instead of edgecolor="none" +* :ghpull:`22592`: Backport PR #22141 on branch v3.5.x (Fix check 1d) +* :ghpull:`22141`: Fix check 1d +* :ghpull:`22588`: Backport PR #22445 on branch v3.5.x (Fix loading tk on windows when current process has >1024 modules.) +* :ghpull:`22445`: Fix loading tk on windows when current process has >1024 modules. +* :ghpull:`22575`: Backport PR #22572 on branch v3.5.x (Fix issue with unhandled Done exception) +* :ghpull:`22578`: Backport PR #22038 on branch v3.5.x (DOC: Include alternatives to deprecations in the documentation) +* :ghpull:`22572`: Fix issue with unhandled Done exception +* :ghpull:`22557`: Backport PR #22549 on branch v3.5.x (Really fix wheel building on CI) +* :ghpull:`22549`: Really fix wheel building on CI +* :ghpull:`22548`: Backport PR #22540 on branch v3.5.x (Reorder text api docs.) +* :ghpull:`22540`: Reorder text api docs. +* :ghpull:`22542`: Backport PR #22534 on branch v3.5.x (Fix issue with manual clabel) +* :ghpull:`22534`: Fix issue with manual clabel +* :ghpull:`22501`: Backport PR #22499 on branch v3.5.x (FIX: make the show API on webagg consistent with others) +* :ghpull:`22499`: FIX: make the show API on webagg consistent with others +* :ghpull:`22500`: Backport PR #22496 on branch v3.5.x (Fix units in quick start example) +* :ghpull:`22496`: Fix units in quick start example +* :ghpull:`22493`: Backport PR #22483 on branch v3.5.x (Tweak arrow demo size.) +* :ghpull:`22492`: Backport PR #22476: FIX: Include (0, 0) offsets in scatter autoscaling +* :ghpull:`22483`: Tweak arrow demo size. +* :ghpull:`22476`: FIX: Include (0, 0) offsets in scatter autoscaling +* :ghpull:`22481`: Backport PR #22479 on branch v3.5.x (adds _enum qualifier for QColorDialog.ShowAlphaChannel. Closes #22471.) +* :ghpull:`22479`: adds _enum qualifier for QColorDialog.ShowAlphaChannel. Closes #22471. +* :ghpull:`22475`: Backport PR #22474 on branch v3.5.x (Clarify secondary_axis documentation) +* :ghpull:`22474`: Clarify secondary_axis documentation +* :ghpull:`22462`: Backport PR #22458 on branch v3.5.x (Fix Radar Chart Gridlines for Non-Circular Charts) +* :ghpull:`22456`: Backport PR #22375 on branch v3.5.x (Re-enable cibuildwheel on push) +* :ghpull:`22375`: Re-enable cibuildwheel on push +* :ghpull:`22443`: Backport PR #22442 on branch v3.5.x (CI: skip test to work around gs bug) +* :ghpull:`22442`: CI: skip test to work around gs bug +* :ghpull:`22441`: Backport PR #22434 on branch v3.5.x (DOC: imbalanced backticks.) +* :ghpull:`22436`: Backport PR #22431 on branch v3.5.x (Update Scipy intersphinx inventory link) +* :ghpull:`22438`: Backport PR #22430 on branch v3.5.x (fix method name in doc) +* :ghpull:`22434`: DOC: imbalanced backticks. +* :ghpull:`22426`: Backport PR #22398 on branch v3.5.x (Pin coverage to fix CI) +* :ghpull:`22428`: Backport PR #22368 on branch v3.5.x (Pin dependencies to fix CI) +* :ghpull:`22427`: Backport PR #22396 on branch v3.5.x (Clarify note in get_cmap()) +* :ghpull:`22396`: Clarify note in get_cmap() +* :ghpull:`22398`: Pin coverage to fix CI +* :ghpull:`22368`: Pin dependencies to fix CI +* :ghpull:`22358`: Backport PR #22349 on branch v3.5.x (Use latex as the program name for kpsewhich) +* :ghpull:`22349`: Use latex as the program name for kpsewhich +* :ghpull:`22348`: Backport PR #22346 on branch v3.5.x (Remove invalid ```` tag in ``animation.HTMLWriter``) +* :ghpull:`22346`: Remove invalid ```` tag in ``animation.HTMLWriter`` +* :ghpull:`22328`: Backport PR #22288 on branch v3.5.x (update documentation after #18966) +* :ghpull:`22288`: update documentation after #18966 +* :ghpull:`22325`: Backport PR #22283: Fixed ``repr`` for ``SecondaryAxis`` +* :ghpull:`22322`: Backport PR #22077 on branch v3.5.x (Fix keyboard event routing in Tk backend (fixes #13484, #14081, and #22028)) +* :ghpull:`22321`: Backport PR #22290 on branch v3.5.x (Respect ``position`` and ``group`` argument in Tk toolmanager add_toolitem) +* :ghpull:`22318`: Backport PR #22293 on branch v3.5.x (Modify example for x-axis tick labels at the top) +* :ghpull:`22319`: Backport PR #22279 on branch v3.5.x (Remove Axes sublists from docs) +* :ghpull:`22327`: Backport PR #22326 on branch v3.5.x (CI: ban coverage 6.3 that may be causing random hangs in fork test) +* :ghpull:`22326`: CI: ban coverage 6.3 that may be causing random hangs in fork test +* :ghpull:`22077`: Fix keyboard event routing in Tk backend (fixes #13484, #14081, and #22028) +* :ghpull:`22290`: Respect ``position`` and ``group`` argument in Tk toolmanager add_toolitem +* :ghpull:`22293`: Modify example for x-axis tick labels at the top +* :ghpull:`22311`: Backport PR #22285 on branch v3.5.x (Don't warn on grid removal deprecation if grid is hidden) +* :ghpull:`22310`: Backport PR #22294 on branch v3.5.x (Add set_cursor method to FigureCanvasTk) +* :ghpull:`22285`: Don't warn on grid removal deprecation if grid is hidden +* :ghpull:`22294`: Add set_cursor method to FigureCanvasTk +* :ghpull:`22309`: Backport PR #22301 on branch v3.5.x (FIX: repositioning axes labels: use get_window_extent instead for spines.) +* :ghpull:`22301`: FIX: repositioning axes labels: use get_window_extent instead for spines. +* :ghpull:`22307`: Backport PR #22306 on branch v3.5.x (FIX: ensure that used sub-packages are actually imported) +* :ghpull:`22306`: FIX: ensure that used sub-packages are actually imported +* :ghpull:`22283`: Fixed ``repr`` for ``SecondaryAxis`` +* :ghpull:`22275`: Backport PR #22254 on branch v3.5.x (Disable QuadMesh cursor data by default) +* :ghpull:`22254`: Disable QuadMesh cursor data by default +* :ghpull:`22269`: Backport PR #22265 on branch v3.5.x (Fix Qt enum access.) +* :ghpull:`22265`: Fix Qt enum access. +* :ghpull:`22259`: Backport PR #22256 on branch v3.5.x (Skip tests on the -doc branches) +* :ghpull:`22238`: Backport PR #22235 on branch v3.5.x (Run wheel builds on PRs when requested by a label) +* :ghpull:`22241`: Revert "Backport PR #22179 on branch v3.5.x (FIX: macosx check case-insensitive app name)" +* :ghpull:`22248`: Backport PR #22206 on branch v3.5.x (Improve formatting of "Anatomy of a figure") +* :ghpull:`22235`: Run wheel builds on PRs when requested by a label +* :ghpull:`22206`: Improve formatting of "Anatomy of a figure" +* :ghpull:`22220`: Backport PR #21833: Enforce backport conditions on v*-doc branches +* :ghpull:`22219`: Backport PR #22218 on branch v3.5.x (Fix typo in ``tutorials/intermediate/arranging_axes.py``) +* :ghpull:`22218`: Fix typo in ``tutorials/intermediate/arranging_axes.py`` +* :ghpull:`22217`: Backport PR #22209 on branch v3.5.x (DOC: Document default join style) +* :ghpull:`22209`: DOC: Document default join style +* :ghpull:`22214`: Backport PR #22208 on branch v3.5.x (Stop sorting artists in Figure Options dialog) +* :ghpull:`22215`: Backport PR #22177 on branch v3.5.x (Document ArtistList) +* :ghpull:`22177`: Document ArtistList +* :ghpull:`22208`: Stop sorting artists in Figure Options dialog +* :ghpull:`22199`: DOC: git:// is deprecated. +* :ghpull:`22210`: Backport PR #22202 on branch v3.5.x (PR: Fix merge of 18966) +* :ghpull:`22202`: PR: Fix merge of 18966 +* :ghpull:`22201`: Backport PR #22053 on branch v3.5.x (DOC: Document default cap styles) +* :ghpull:`22053`: DOC: Document default cap styles +* :ghpull:`22195`: Backport PR #22179 on branch v3.5.x (FIX: macosx check case-insensitive app name) +* :ghpull:`22192`: Backport PR #22190 on branch v3.5.x (DOC: Fix upstream URL for merge in CircleCI) +* :ghpull:`22188`: Backport PR #22187 on branch v3.5.x (Fix typo in ``axhline`` docstring) +* :ghpull:`22187`: Fix typo in ``axhline`` docstring +* :ghpull:`22185`: Backport PR #22184 on branch v3.5.x (Removed dev from 3.10-version) +* :ghpull:`22186`: Backport PR #21943 on branch v3.5.x (DOC: explain too many ticks) +* :ghpull:`21943`: DOC: explain too many ticks +* :ghpull:`22184`: Removed dev from 3.10-version +* :ghpull:`22168`: Backport PR #22144 on branch v3.5.x (Fix cl subgridspec) +* :ghpull:`22144`: Fix cl subgridspec +* :ghpull:`22155`: Backport PR #22082 on branch v3.5.x (Update both zoom/pan states on wx when triggering from keyboard.) +* :ghpull:`22082`: Update both zoom/pan states on wx when triggering from keyboard. +* :ghpull:`22153`: Backport PR #22147 on branch v3.5.x (Fix loading user-defined icons for Tk toolbar) +* :ghpull:`22152`: Backport PR #22135 on branch v3.5.x (Fix loading user-defined icons for Qt plot window) +* :ghpull:`22151`: Backport PR #22078 on branch v3.5.x (Prevent tooltips from overlapping buttons in NavigationToolbar2Tk (fixes issue mentioned in #22028)) +* :ghpull:`22135`: Fix loading user-defined icons for Qt plot window +* :ghpull:`22078`: Prevent tooltips from overlapping buttons in NavigationToolbar2Tk (fixes issue mentioned in #22028) +* :ghpull:`22147`: Fix loading user-defined icons for Tk toolbar +* :ghpull:`22136`: Backport PR #22132 on branch v3.5.x (TST: Increase fp tolerances for some images) +* :ghpull:`22132`: TST: Increase fp tolerances for some images +* :ghpull:`22121`: Backport PR #22116 on branch v3.5.x (FIX: there is no add_text method, fallback to add_artist) +* :ghpull:`22117`: Backport PR #21860 on branch v3.5.x (DOC: Update style sheet reference) +* :ghpull:`22116`: FIX: there is no add_text method, fallback to add_artist +* :ghpull:`22038`: DOC: Include alternatives to deprecations in the documentation +* :ghpull:`22074`: Backport PR #22066 on branch v3.5.x (FIX: Remove trailing zeros from offset significand) +* :ghpull:`22106`: Backport PR #22089: FIX: squash memory leak in colorbar +* :ghpull:`22089`: FIX: squash memory leak in colorbar +* :ghpull:`22101`: Backport PR #22099 on branch v3.5.x (CI: Disable numpy avx512 instructions) +* :ghpull:`22099`: CI: Disable numpy avx512 instructions +* :ghpull:`22095`: Backport PR #22083 on branch v3.5.x (Fix reference to Matplotlib FAQ in doc/index.rst) +* :ghpull:`22066`: FIX: Remove trailing zeros from offset significand +* :ghpull:`22072`: Backport PR #22071 on branch v3.5.x (Fix a small typo in docstring ("loation" --> "location")) +* :ghpull:`22071`: Fix a small typo in docstring ("loation" --> "location") +* :ghpull:`22070`: Backport PR #22069 on branch v3.5.x ([Doc] Fix typo in ``units.py`` documentation example) +* :ghpull:`22069`: [Doc] Fix typo in ``units.py`` documentation example +* :ghpull:`22067`: Backport PR #22064 on branch v3.5.x (DOC: Clarify y parameter in Axes.set_title) +* :ghpull:`22064`: DOC: Clarify y parameter in Axes.set_title +* :ghpull:`22049`: Backport PR #22048 on branch v3.5.x (Document how to prevent TeX from treating ``&``, ``#`` as special.) +* :ghpull:`22048`: Document how to prevent TeX from treating ``&``, ``#`` as special. +* :ghpull:`22047`: Backport PR #22044 on branch v3.5.x (Get correct source code link for decorated functions) +* :ghpull:`22044`: Get correct source code link for decorated functions +* :ghpull:`22024`: Backport PR #22009 on branch v3.5.x (FIX: Prevent set_alpha from changing color of legend patch) +* :ghpull:`22009`: FIX: Prevent set_alpha from changing color of legend patch +* :ghpull:`22019`: Backport PR #22018 on branch v3.5.x (BUG: fix handling of zero-dimensional arrays in cbook._reshape_2D) +* :ghpull:`22018`: BUG: fix handling of zero-dimensional arrays in cbook._reshape_2D +* :ghpull:`21996`: Backport PR #21990 on branch v3.5.x (Fix rubberbanding on wx+py3.10.) +* :ghpull:`21990`: Fix rubberbanding on wx+py3.10. +* :ghpull:`21987`: Backport PR #21862 on branch v3.5.x (DOC: Simplify markevery demo) +* :ghpull:`21969`: Backport PR #21948 on branch v3.5.x (Distinguish AbstractMovieWriter and MovieWriter in docs.) +* :ghpull:`21948`: Distinguish AbstractMovieWriter and MovieWriter in docs. +* :ghpull:`21953`: Backport PR #21946 on branch v3.5.x (DOC: fix interactive to not put Event Handling and Interactive Guide …) +* :ghpull:`21946`: DOC: fix interactive to not put Event Handling and Interactive Guide … + +Issues (61): + +* :ghissue:`22954`: [Doc]: v3.5.1 github stats are missing +* :ghissue:`22959`: [MNT]: macos-latest memory leak over threshold +* :ghissue:`22921`: [Bug]: Regression in animation from #22175 +* :ghissue:`22908`: [Bug]: QuadMesh get_cursor_data errors if no array is set +* :ghissue:`21901`: Suggested clarification of comments in errorbar helpers +* :ghissue:`22932`: [Doc]: small edits to the Pull request guidelines +* :ghissue:`22858`: [Bug]: FigureCanvasTkAgg call creates memory leak +* :ghissue:`20490`: Memory leaks on matplotlib 3.4.2 (and 3.4.0) +* :ghissue:`22900`: [Doc]: Typo in triage acknowledgment +* :ghissue:`22341`: [Bug]: GridSpec or related change between 3.4.3 and 3.5.1 +* :ghissue:`22472`: [Bug]: ConciseDateFormatter not showing year anywhere when plotting <12 months +* :ghissue:`22874`: [Bug]: Textbox doesn't accept input +* :ghissue:`21893`: [Bug]: ``backend_pdf`` gives ``TTLibError`` with ``pdf.fonttype : 42`` +* :ghissue:`22840`: [Bug]: Blank output EPS file when using latex and figure.autolayout = True +* :ghissue:`22762`: [Bug]: Issue with find_nearest_contour in contour.py +* :ghissue:`22823`: [Bug]: Changing Linestyle in plot window swaps some plotted lines +* :ghissue:`22804`: [Bug]: Quiver not working with subfigure? +* :ghissue:`22673`: [Bug]: tight_layout (version 3.5+) +* :ghissue:`21930`: [Bug]: EPS savefig messed up by 'figure.autolayout' rcParam on 3.5.0 +* :ghissue:`22753`: windows CI broken on azure +* :ghissue:`22088`: [Bug]: Tool Manager example broken +* :ghissue:`22624`: [Bug]: invalid value encountered with 'ortho' projection mode +* :ghissue:`22640`: [Bug]: Confusing deprecation warning when empty data passed to axis with category units +* :ghissue:`22137`: [Bug]: Cannot clear figure of subfigures +* :ghissue:`22706`: [Bug]: RangeSlider.set_val does not move the slider (only poly and value) +* :ghissue:`22727`: MAtplolib pan and zoom dead slow on new PC +* :ghissue:`22687`: [Bug]: Empty text or text with a newline at either end + path_effects crashes +* :ghissue:`22694`: Revert set_show_cursor_data +* :ghissue:`22520`: [Bug]: Slow lasso selector over QuadMesh collection +* :ghissue:`22648`: Add packaging to setup_requires? +* :ghissue:`22052`: [Bug]: invert_yaxis function cannot invert the "over value" in colorbar axes +* :ghissue:`22576`: [Bug]: ``inset_axes`` colorbar + ``tight_layout`` raises ``AttributeError`` +* :ghissue:`22590`: [Bug]: ValueError: Do not know how to convert "list" to dashes; when using axes errorbar. +* :ghissue:`21998`: [Bug]: Working with PyQt5, the different import order will make different result. +* :ghissue:`22330`: [Bug]: possible regression with pandas 1.4 with plt.plot when using a single column dataframe as the x argument +* :ghissue:`22125`: [Bug]: ``plt.plot`` thinks ``pandas.Series`` is 2-dimensional when nullable data type is used +* :ghissue:`22378`: [Bug]: TkAgg fails to find Tcl/Tk libraries in Windows for processes with a large number of modules loaded +* :ghissue:`22577`: [Bug]: Erroneous deprecation warning help message +* :ghissue:`21798`: [Bug]: Unhandled _get_renderer.Done exception in wxagg backend +* :ghissue:`22532`: [Issue]: Manually placing contour labels using ``clabel`` not working +* :ghissue:`22470`: [Bug]: Subsequent scatter plots work incorrectly +* :ghissue:`22471`: [Bug]: formlayout fails on PyQt6 due to the unqualified enum ShowAlphaChannel in class ColorButton +* :ghissue:`22473`: [Bug]: Secondary axis does not accept python builtins for transform +* :ghissue:`22384`: [Bug]: Curve styles gets mixed up when edited in the Curves Tab of Figure Options (Edit Axis) +* :ghissue:`22028`: [Bug]: mpl with py3.10.1 - Interactive figures - Constrain pan/zoom to x/y axis not work +* :ghissue:`13484`: Matplotlib keymap stop working after pressing tab +* :ghissue:`20130`: tk toolmanager add_toolitem fails to add tool to group other than the last one +* :ghissue:`21723`: [Bug]: Some styles trigger pcolormesh grid deprecation +* :ghissue:`22300`: [Bug]: Saving a fig with a colorbar using a ``TwoSlopeNorm`` sometimes results in 'posx and posy should be finite values' +* :ghissue:`22305`: [Bug]: Import Error in Matplotlib 3.5.1 +* :ghissue:`21917`: [Bug]: pcolormesh is not responsive in Matplotlib 3.5 +* :ghissue:`22094`: [Doc]: No documentation on ArtistList +* :ghissue:`21979`: [Doc]: Clarify default capstyle +* :ghissue:`22143`: [Bug]: ``constrained_layout`` merging similar subgrids +* :ghissue:`22131`: [Bug]: png icon image fails to load for manually defined tool buttons +* :ghissue:`22093`: [Bug]: AttributeError: 'AxesSubplot' object has no attribute 'add_text' +* :ghissue:`22085`: [Bug]: Memory leak with colorbar.make_axes +* :ghissue:`22065`: [Bug]: Additive offset with trailing zeros +* :ghissue:`15493`: common_texification misses & (ampersand) +* :ghissue:`22039`: [Doc]: [source] link for deprecated functions leads to _api/deprecation.py +* :ghissue:`22016`: [Bug]: matplotlib 3.3 changed how plt.hist handles iterables of zero-dimensional arrays. diff --git a/doc/users/prev_whats_new/github_stats_3.5.3.rst b/doc/users/prev_whats_new/github_stats_3.5.3.rst new file mode 100644 index 000000000000..bafd6d5c27eb --- /dev/null +++ b/doc/users/prev_whats_new/github_stats_3.5.3.rst @@ -0,0 +1,127 @@ +.. _github-stats-3-5-3: + +GitHub statistics for 3.5.3 (Aug 10, 2022) +========================================== + +GitHub statistics for 2022/05/03 (tag: v3.5.2) - 2022/08/10 + +These lists are automatically generated, and may be incomplete or contain duplicates. + +We closed 19 issues and merged 66 pull requests. +The full list can be seen `on GitHub `__ + +The following 20 authors contributed 99 commits. + +* Antony Lee +* Biswapriyo Nath +* David Gilbertson +* DWesl +* Elliott Sales de Andrade +* GavinZhang +* Greg Lucas +* Jody Klymak +* Kayran Schmidt +* Matthew Feickert +* Nickolaos Giannatos +* Oscar Gustafsson +* Ruth Comer +* SaumyaBhushan +* Scott Jones +* Scott Shambaugh +* tfpf +* Thomas A Caswell +* Tim Hoffmann +* wsykala + +GitHub issues and pull requests: + +Pull Requests (66): + +* :ghpull:`23591`: Backport PR #23549 on branch v3.5.x (Don't clip colorbar dividers) +* :ghpull:`23593`: STY: Fix whitespace error from new flake8 +* :ghpull:`23549`: Don't clip colorbar dividers +* :ghpull:`23528`: Backport PR #23523 on branch v3.5.x (TST: Update Quantity test class) +* :ghpull:`23523`: TST: Update Quantity test class +* :ghpull:`23508`: Add explicit registration of units in examples +* :ghpull:`23515`: Backport PR #23462: Fix AttributeError for pickle load of Figure class +* :ghpull:`23518`: Backport PR #23514 on branch v3.5.x (Fix doc build) +* :ghpull:`23517`: Backport PR #23511 on branch v3.5.x (supporting IBM i OS) +* :ghpull:`23511`: supporting IBM i OS +* :ghpull:`23462`: Fix AttributeError for pickle load of Figure class +* :ghpull:`23488`: Backport PR #23066 on branch v3.5.x (BLD: Define PyErr_SetFromWindowsErr on Cygwin.) +* :ghpull:`23066`: BLD: Define PyErr_SetFromWindowsErr on Cygwin. +* :ghpull:`23479`: Pin setuptools_scm on v3.5.x +* :ghpull:`22998`: Backport PR #22987 on branch v3.5.x (CI: bump test limit from tkagg on osx) +* :ghpull:`23478`: Backport PR #23476: FIX: reset to original DPI in getstate +* :ghpull:`23476`: FIX: reset to original DPI in getstate +* :ghpull:`23458`: Backport PR #23445 on branch v3.5.x (Compare thread native ids when checking whether running on main thread.) +* :ghpull:`23440`: Backport PR #23430 on branch v3.5.x (Fix divide by 0 runtime warning) +* :ghpull:`23430`: Fix divide by 0 runtime warning +* :ghpull:`23344`: Backport PR #23333: Fix errorbar handling of nan. +* :ghpull:`23333`: Fix errorbar handling of nan. +* :ghpull:`23338`: Backport PR #23278: Remove internal use of get/set dpi +* :ghpull:`23331`: Backport PR #22835 on branch v3.5.x (Fix BoundaryNorm cursor data output) +* :ghpull:`22835`: Fix BoundaryNorm cursor data output +* :ghpull:`23292`: Backport PR #23232 on branch v3.5.x (Fix passing stem markerfmt positionally when locs are not given) +* :ghpull:`23275`: Backport PR #23260 on branch v3.5.x (Fix Colorbar extend patches to have correct alpha) +* :ghpull:`23312`: Pin to an older pydata-sphinx-theme for v3.5.x +* :ghpull:`23278`: Remove internal use of get/set dpi +* :ghpull:`23232`: Fix passing stem markerfmt positionally when locs are not given +* :ghpull:`22865`: Fix issue with colorbar extend and drawedges +* :ghpull:`23260`: Fix Colorbar extend patches to have correct alpha +* :ghpull:`23245`: Backport PR #23144 on branch v3.5.x (Only import setuptools_scm when we are in a matplotlib git repo) +* :ghpull:`23144`: Only import setuptools_scm when we are in a matplotlib git repo +* :ghpull:`23242`: Backport PR #23203 on branch v3.5.x (Honour ``panchor`` keyword for colorbar on subplot) +* :ghpull:`23203`: Honour ``panchor`` keyword for colorbar on subplot +* :ghpull:`23228`: Backport PR #23209 on branch v3.5.x (Fix the vertical alignment of overunder symbols.) +* :ghpull:`23209`: Fix the vertical alignment of overunder symbols. +* :ghpull:`23184`: Backport PR #23174: Make sure SubFigure has _cachedRenderer +* :ghpull:`23194`: Backport PR #23095: Try to unbreak CI by xfailing OSX Tk tests +* :ghpull:`23113`: Backport PR #23057 and #23106 +* :ghpull:`23185`: Backport PR #23168 on branch v3.5.x (Corrected docstring for artist.Artist.set_agg_filter) +* :ghpull:`23168`: Corrected docstring for artist.Artist.set_agg_filter +* :ghpull:`23174`: Make sure SubFigure has _cachedRenderer +* :ghpull:`23110`: Tweak subprocess_run_helper. +* :ghpull:`23138`: Backport PR #23137 on branch v3.5.x (DOC fix typo) +* :ghpull:`23137`: DOC fix typo +* :ghpull:`23125`: Backport PR #23122 on branch v3.5.x (Remove redundant rcparam default) +* :ghpull:`23120`: Backport PR #23115 on branch v3.5.x (DOC fixed duplicate/wrong default) +* :ghpull:`23095`: Try to unbreak CI by xfailing OSX Tk tests +* :ghpull:`23106`: Reuse subprocess_run_helper in test_pylab_integration. +* :ghpull:`23112`: Backport PR #23111 on branch v3.5.x (Fix _g_sig_digits for value<0 and delta=0.) +* :ghpull:`23111`: Fix _g_sig_digits for value<0 and delta=0. +* :ghpull:`23057`: FIX: ensure switching the backend installs repl hook +* :ghpull:`23075`: Backport PR #23069 on branch v3.5.x (TST: forgive more failures on pyside2 / pyside6 cross imports) +* :ghpull:`23069`: TST: forgive more failures on pyside2 / pyside6 cross imports +* :ghpull:`22981`: Backport PR #22979 on branch v3.5.x (Skip additional backend tests on import error) +* :ghpull:`23064`: Backport PR #22975 on branch v3.5.x (MNT: fix __array__ to numpy) +* :ghpull:`22975`: MNT: fix __array__ to numpy +* :ghpull:`23058`: Backport PR #23051 on branch v3.5.x (Fix variable initialization due to jump bypassing it) +* :ghpull:`23051`: Fix variable initialization due to jump bypassing it +* :ghpull:`23010`: Backport PR #23000 on branch v3.5.x (Additional details on VS install on installation page) +* :ghpull:`22995`: Backport PR #22994 on branch v3.5.x (Docs: ignore >>> on code prompts on documentation prompts) +* :ghpull:`23001`: CI: Add trivial pre-commit.ci config to avoid CI failure +* :ghpull:`22987`: CI: bump test limit from tkagg on osx +* :ghpull:`22979`: Skip additional backend tests on import error + +Issues (19): + +* :ghissue:`22864`: [Bug]: Colorbar with drawedges=True and extend='both' does not draw edges at extremities +* :ghissue:`23382`: [TST] Upcoming dependency test failures +* :ghissue:`23470`: [Bug]: fig.canvas.mpl_connect in 3.5.2 not registering events in jupyter lab unless using widget pan or zoom controls +* :ghissue:`22997`: [Bug]: Cygwin build fails due to use of Windows-only functions in _tkagg.cpp +* :ghissue:`23471`: [Bug]: DPI of a figure is doubled after unpickling on M1 Mac +* :ghissue:`23050`: [Doc]: Docstring for artist.Artist.set_agg_filter is incorrect +* :ghissue:`23307`: [Bug]: PEX warns about missing ``setuptools`` from ``install_requires`` in matplotlib +* :ghissue:`23330`: [Bug]: Missing values cause exception in errorbar plot +* :ghissue:`21915`: [Bug]: scalar mappable format_cursor_data crashes on BoundarNorm +* :ghissue:`22970`: [Bug]: Colorbar extend patches do not have correct alpha +* :ghissue:`23114`: [Bug]: matplotlib __init__.py checks for .git folder 2 levels up, then errors due to setup tools_scm +* :ghissue:`23157`: [Bug]: colorbar ignores keyword panchor=False +* :ghissue:`23229`: [Bug]: matplotlib==3.5.2 breaks ipywidgets +* :ghissue:`18085`: vertical alignment of \sum depends on the presence of subscripts and superscripts +* :ghissue:`23173`: [Bug]: Crash when adding clabels to subfigures +* :ghissue:`23108`: [Bug]: Imshow with all negative values leads to math domain errors. +* :ghissue:`23042`: [Bug]: Figures fail to redraw with IPython +* :ghissue:`23004`: [Bug]: test failure of test_cross_Qt_imports in 3.5.2 +* :ghissue:`22973`: [Bug]: v3.5.2 causing plot to crash when plotting object with ``__array__`` method diff --git a/doc/users/prev_whats_new/github_stats_3.6.0.rst b/doc/users/prev_whats_new/github_stats_3.6.0.rst new file mode 100644 index 000000000000..aac0c0445fd3 --- /dev/null +++ b/doc/users/prev_whats_new/github_stats_3.6.0.rst @@ -0,0 +1,1292 @@ +.. _github-stats-3-6-0: + +GitHub statistics for 3.6.0 (Sep 15, 2022) +========================================== + +GitHub statistics for 2021/11/16 (tag: v3.5.0) - 2022/09/15 + +These lists are automatically generated, and may be incomplete or contain duplicates. + +We closed 202 issues and merged 894 pull requests. +The full list can be seen `on GitHub `__ + +The following 174 authors contributed 4425 commits. + +* Abhishek K M +* Adeel Hassan +* agra +* Aitik Gupta +* ambi7 +* Andras Deak +* Andres Martinez +* Andrew Fennell +* andrzejnovak +* Andrés Martínez +* Anna Mastori +* AnnaMastori +* Ante Sikic +* Antony Lee +* arndRemy +* Ben Root +* Biswapriyo Nath +* cavesdev +* Clément Phan +* Clément Walter +* code-review-doctor +* Connor Cozad +* Constantine Evans +* Croadden +* daniilS +* Danilo Palumbo +* David Gilbertson +* David Ketcheson +* David Matos +* David Poznik +* David Stansby +* Davide Sandonà +* dependabot[bot] +* dermasugita +* Diego Solano +* Dimitri Papadopoulos +* dj4t9n +* Dmitriy Fishman +* DWesl +* Edouard Berthe +* eindH +* Elliott Sales de Andrade +* Eric Firing +* Eric Larson +* Eric Prestat +* Federico Ariza +* Felix Nößler +* Fernando +* Gajendra Pal +* gajendra0180 +* GavinZhang +* Greg Lucas +* hannah +* Hansin Ahuja +* Harshal Prakash Patankar +* Hassan Kibirige +* Haziq Khurshid +* Henry +* henrybeUM +* Hood +* Hood Chatham +* Ian Hunt-Isaak +* Ian Thomas +* igurin-invn +* ikhebgeenaccount +* Isha Mehta +* Jake Bowhay +* Jake Li +* Jake Lishman +* Jake VanderPlas +* Jakub Klus +* James Tocknell +* Jan-Hendrik Müller +* Jay Joshi +* Jay Stanley +* jayjoshi112711 +* Jeff Beck +* Jody Klymak +* Joel Frederico +* Joseph Fox-Rabinovitz +* Josh Soref +* Jouni K. Seppänen +* Kayran Schmidt +* kdpenner +* Kian Eliasi +* Kinshuk Dua +* kislovskiy +* KIU Shueng Chuan +* kjain +* kolibril13 +* krassowski +* Krish-sysadmin +* Leeh Peter +* lgfunderburk +* Liam Toney +* Lucas Ricci +* Luke Davis +* luz paz +* mackopes +* MAKOMO +* MalikIdreesHasa +* Marcin Swaltek +* Mario +* Mario Sergio Valdés Tresanco +* martinRenou +* Matthew Feickert +* Matthias Bussonnier +* Mauricio Collares +* MeeseeksMachine +* melissawm +* Mr-Milk +* Navid C. Constantinou +* Nickolaos Giannatos +* Nicolas P. Rougier +* Niyas Sait +* noatamir +* ojeda-e +* Olivier Gauthé +* Oscar Gustafsson +* patquem +* Philipp Rohde +* Pieter Eendebak +* Pieter P +* Péter Leéh +* Qijia Liu +* Quentin Peter +* Raphael Quast +* rditlar9 +* Richard Penney +* richardsheridan +* Rike-Benjamin Schuppner +* Robert Cimrman +* Roberto Toro +* root +* Ruth Comer +* Ruth G. N +* Ruth Nainggolan +* Ryan May +* Rémi Achard +* SaumyaBhushan +* Scott Jones +* Scott Shambaugh +* selormtamakloe +* Simon Hoxbro +* skywateryang +* Stefanie Molin +* Steffen Rehberg +* stone +* Sven Eschlbeck +* sveneschlbeck +* takimata +* tfpf +* Thomas A Caswell +* Tim Hoffmann +* Tobias Megies +* Tomas Hrnciar +* Tomasz KuliÅ„ski +* trichter +* unknown +* Uwe Hubert +* vfdev-5 +* Vishal Chandratreya +* Vishal Pankaj Chandratreya +* Vishnu V K +* vk0812 +* Vlad Korolev +* Will Qian +* William Qian +* wqh17101 +* wsykala +* yaaun +* Yannic Schroeder +* yuanx749 +* 渡邉 美希 + +GitHub issues and pull requests: + +Pull Requests (894): + +* :ghpull:`23814`: Consolidate release notes for 3.6 +* :ghpull:`23899`: Backport PR #23885 on branch v3.6.x (DOC: Rearrange navbar-end elements) +* :ghpull:`23898`: Backport PR #23892 on branch v3.6.x (DOC: Fix docs for linestyles in contour) +* :ghpull:`23885`: DOC: Rearrange navbar-end elements +* :ghpull:`23894`: Backport PR #23881 on branch v3.6.x (Fix Pillow compatibility in example) +* :ghpull:`23897`: Backport PR #23887 on branch v3.6.x (Add missing label argument to barh docs) +* :ghpull:`23892`: DOC: Fix docs for linestyles in contour +* :ghpull:`23887`: Add missing label argument to barh docs +* :ghpull:`23893`: Backport PR #23886 on branch v3.6.x (CI: prefer (older) binaries over (newer) sdists) +* :ghpull:`23881`: Fix Pillow compatibility in example +* :ghpull:`23886`: CI: prefer (older) binaries over (newer) sdists +* :ghpull:`23880`: Backport PR #23862 on branch v3.6.x (Remove triggering of deprecation warning in AnchoredEllipse) +* :ghpull:`23862`: Remove triggering of deprecation warning in AnchoredEllipse +* :ghpull:`23879`: Backport PR #23864 on branch v3.6.x (Correct and improve documentation for anchored artists) +* :ghpull:`23877`: Backport PR #23841 on branch v3.6.x (clarified that hist computes histogram on unbinned data) +* :ghpull:`23872`: Backport PR #23871 on branch v3.6.x (DOC: Fix formatting of pick event demo example) +* :ghpull:`23841`: clarified that hist computes histogram on unbinned data +* :ghpull:`23864`: Correct and improve documentation for anchored artists +* :ghpull:`23871`: DOC: Fix formatting of pick event demo example +* :ghpull:`23869`: Backport PR #23867 on branch v3.6.x (DOC: fix deprecation warnings in examples) +* :ghpull:`23867`: DOC: fix deprecation warnings in examples +* :ghpull:`23858`: Backport PR #23855 on branch v3.6.x (DOC: fix deprecation warnings) +* :ghpull:`23859`: Backport PR #23844 on branch v3.6.x (Further improve dev setup instructions) +* :ghpull:`23844`: Further improve dev setup instructions +* :ghpull:`23855`: DOC: fix deprecation warnings +* :ghpull:`23854`: Backport PR #23852 on branch v3.6.x (Fix cross-compiling internal freetype) +* :ghpull:`23852`: Fix cross-compiling internal freetype +* :ghpull:`23853`: Backport PR #23830 on branch v3.6.x (Start testing on Python 3.11) +* :ghpull:`23830`: Start testing on Python 3.11 +* :ghpull:`23851`: Backport PR #23850 on branch v3.6.x (removed single word in documenting doc) +* :ghpull:`23850`: removed single word in documenting doc +* :ghpull:`23848`: Backport PR #23843 on branch v3.6.x (Clarify that pycairo>=1.14.0 is needed.) +* :ghpull:`23843`: Clarify that pycairo>=1.14.0 is needed. +* :ghpull:`23842`: Backport PR #23840 on branch v3.6.x (Remove documentation for axes_grid) +* :ghpull:`23838`: Backport PR #23834 on branch v3.6.x (Revert "Refactor handling of tick and ticklabel visibility in Axis.clear") +* :ghpull:`23840`: Remove documentation for axes_grid +* :ghpull:`23837`: Backport PR #23833 on branch v3.6.x (Remove search field from sidebar) +* :ghpull:`23836`: Backport PR #23823 on branch v3.6.x ([DOC] Improve dev setup description) +* :ghpull:`23834`: Revert "Refactor handling of tick and ticklabel visibility in Axis.clear" +* :ghpull:`23833`: Remove search field from sidebar +* :ghpull:`23823`: [DOC] Improve dev setup description +* :ghpull:`23822`: Backport PR #23813 on branch v3.6.x (Triplot duplicated label) +* :ghpull:`23813`: Triplot duplicated label +* :ghpull:`23811`: Backport PR #23805 on branch v3.6.x (sphinxext: Do not copy plot_directive.css's metadata) +* :ghpull:`23805`: sphinxext: Do not copy plot_directive.css's metadata +* :ghpull:`23800`: Backport PR #23785 on branch v3.6.x (FIX: ensure type stability for missing cmaps in ``set_cmap``) +* :ghpull:`23799`: Backport PR #23790 on branch v3.6.x (DOC: Add cache busting to all static assets) +* :ghpull:`23785`: FIX: ensure type stability for missing cmaps in ``set_cmap`` +* :ghpull:`23790`: DOC: Add cache busting to all static assets +* :ghpull:`23791`: Backport PR #23774 on branch v3.6.x (Correct rcParams-name in AutoDateFormatter doc-string) +* :ghpull:`23792`: Backport PR #23781 on branch v3.6.x (ci: Add plot types to sphinx-gallery artifacts) +* :ghpull:`23789`: Backport PR #23786 on branch v3.6.x (DOC: fontfallback works for most of the backends) +* :ghpull:`23788`: Backport PR #23784 on branch v3.6.x (DOC: Fix num2date docstring) +* :ghpull:`23786`: DOC: fontfallback works for most of the backends +* :ghpull:`23784`: DOC: Fix num2date docstring +* :ghpull:`23781`: ci: Add plot types to sphinx-gallery artifacts +* :ghpull:`23783`: Backport PR #23782 on branch v3.6.x (Remove ``Axes.cla`` from examples) +* :ghpull:`23782`: Remove ``Axes.cla`` from examples +* :ghpull:`23774`: Correct rcParams-name in AutoDateFormatter doc-string +* :ghpull:`23773`: Backport PR #23772 on branch v3.6.x (3d plots what's new cleanups) +* :ghpull:`23772`: 3d plots what's new cleanups +* :ghpull:`23765`: Backport PR #23762 on branch v3.6.x (FIX: legend handler warning too liberal) +* :ghpull:`23762`: FIX: legend handler warning too liberal +* :ghpull:`23759`: Backport PR #23686 on branch v3.6.x (Improve matplotlib.pyplot importtime by caching ArtistInspector) +* :ghpull:`23686`: Improve matplotlib.pyplot importtime by caching ArtistInspector +* :ghpull:`23756`: Backport PR #23569 on branch v3.6.x (Fix hidden xlabel bug in colorbar) +* :ghpull:`23755`: Backport PR #23742 on branch v3.6.x (FIX: unbreak ipympl) +* :ghpull:`23569`: Fix hidden xlabel bug in colorbar +* :ghpull:`23742`: FIX: unbreak ipympl +* :ghpull:`23752`: Backport PR #23750 on branch v3.6.x (Fix rcParams documentation) +* :ghpull:`23749`: Backport PR #23735 on branch v3.6.x (Correctly handle Axes subclasses that override cla) +* :ghpull:`23735`: Correctly handle Axes subclasses that override cla +* :ghpull:`23748`: Backport PR #23746 on branch v3.6.x (DOC: add numpydoc docstring + commentary to Axis.get_ticklocs) +* :ghpull:`23747`: Backport PR #23721 on branch v3.6.x (3d plot view angle documentation) +* :ghpull:`23746`: DOC: add numpydoc docstring + commentary to Axis.get_ticklocs +* :ghpull:`23721`: 3d plot view angle documentation +* :ghpull:`23744`: Backport PR #23740 on branch v3.6.x (Clarify error for colorbar with unparented mappable) +* :ghpull:`23741`: Backport PR #23674 on branch v3.6.x (Re-rename builtin seaborn styles to not include a dot.) +* :ghpull:`23740`: Clarify error for colorbar with unparented mappable +* :ghpull:`23674`: Re-rename builtin seaborn styles to not include a dot. +* :ghpull:`23738`: Backport PR #23639 on branch v3.6.x (Adding the new contributor meeting) +* :ghpull:`23739`: Backport PR #23712 on branch v3.6.x (FIX: do not try to help CPython with garbage collection) +* :ghpull:`23712`: FIX: do not try to help CPython with garbage collection +* :ghpull:`23639`: Adding the new contributor meeting +* :ghpull:`23732`: Backport PR #23729 on branch v3.6.x (Use cleaner recursion check in PyQt FigureCanvas' resizeEvent.) +* :ghpull:`23734`: Backport PR #23733 on branch v3.6.x (DOC: Update theme configuration for upcoming changes) +* :ghpull:`23733`: DOC: Update theme configuration for upcoming changes +* :ghpull:`23728`: Backport PR #23722 on branch v3.6.x (Restore deprecation class aliases in cbook) +* :ghpull:`23729`: Use cleaner recursion check in PyQt FigureCanvas' resizeEvent. +* :ghpull:`23726`: Backport PR #23711 on branch v3.6.x (Fix deprecation messages for vendoring unused things) +* :ghpull:`23722`: Restore deprecation class aliases in cbook +* :ghpull:`23727`: Backport PR #23724 on branch v3.6.x (Fix/harmonize spacing in dependencies.rst.) +* :ghpull:`23724`: Fix/harmonize spacing in dependencies.rst. +* :ghpull:`23711`: Fix deprecation messages for vendoring unused things +* :ghpull:`23715`: Backport PR #23708 on branch v3.6.x (Loosen up test_Normalize test) +* :ghpull:`23713`: Backport PR #23710 on branch v3.6.x (Fix cmap deprecations) +* :ghpull:`23708`: Loosen up test_Normalize test +* :ghpull:`23710`: Fix cmap deprecations +* :ghpull:`23696`: Backport PR #23695 on branch v3.6.x (Document polar handling of _interpolation_steps.) +* :ghpull:`23706`: Backport PR #23705 on branch v3.6.x (DOC: Added link to class under discussion) +* :ghpull:`23705`: DOC: Added link to class under discussion +* :ghpull:`23695`: Document polar handling of _interpolation_steps. +* :ghpull:`23668`: Api deprecate cmap functions +* :ghpull:`23049`: Add ``minor`` keyword argument to ``plt.x/yticks`` +* :ghpull:`23665`: Harmonize docstrings for boxstyle/connectionstyle/arrowstyle. +* :ghpull:`23636`: FIX: macosx flush_events should process all events +* :ghpull:`23555`: Uncamelcase offsetTrans in draw_path_collection. +* :ghpull:`23682`: Fix generated documentation for deprecated modules +* :ghpull:`23678`: Get rcParams from mpl +* :ghpull:`23571`: Simplify _bind_draw_path_function. +* :ghpull:`23673`: DOC: Highlight information about avoiding labels in legend +* :ghpull:`22506`: Replace MathtextBackend mechanism. +* :ghpull:`23340`: Set correct path for Arc +* :ghpull:`23562`: Fix issue with get_edgecolor and get_facecolor in 3D plots +* :ghpull:`23634`: make.bat: Don't override SPHINXOPTS/O from the environment +* :ghpull:`23675`: Deprecate helper functions in axis3d +* :ghpull:`23676`: MNT: Get rcParams from mpl +* :ghpull:`23677`: TST: Use article class when checking for pgf +* :ghpull:`23669`: CI: Azure update from ubuntu-18.04 to ubuntu-latest and ubuntu-20.04 +* :ghpull:`23670`: Add bar color demo. +* :ghpull:`23644`: Standardize edge-on axis locations when viewing primary 3d axis planes +* :ghpull:`23563`: Fix issue with drawing 3D lines where points are from nparray +* :ghpull:`23666`: MNT: Deprecate macosx prepare subplots tool +* :ghpull:`23572`: Deprecate ``get_grid_positions(..., raw=True)``. +* :ghpull:`23525`: Add functionality to label individual bars with Axes.bar() +* :ghpull:`23667`: Fix flake8 errors introduced by crossed PRs +* :ghpull:`23554`: MNT: Remove unused imports +* :ghpull:`23659`: Simplify/fix save_diff_image. +* :ghpull:`23663`: Small cleanups to _find_fonts_by_props. +* :ghpull:`23662`: Add tolerance to test failing on ppc64le +* :ghpull:`23623`: MNT: remove _gridspecs attribute on Figure classes +* :ghpull:`23654`: Reverts macosx change to ARC +* :ghpull:`23661`: Remove unused fontsize argument from private mathtext _get_info. +* :ghpull:`23655`: Merge branch v3.5.x into main +* :ghpull:`23658`: Increase tolerance on multi-font tests +* :ghpull:`23657`: Add eps to extension list in image triager +* :ghpull:`23656`: Fix broken link to MathML torture tests. +* :ghpull:`23649`: CI: Use anaconda-client v1.10.0 for upload of nightlies +* :ghpull:`23647`: Allow any color format to be used for axis3d.Axis.set_pane_color +* :ghpull:`23643`: Enable wheels for PyPy 3.8+ +* :ghpull:`23621`: DOC: update and extend fonts explanation +* :ghpull:`23612`: CI: try installing a different version of noto on OSX +* :ghpull:`23619`: add pikepdf and visual c++ dependency +* :ghpull:`23631`: Leave out ``barh`` from the basic plot types. +* :ghpull:`23637`: BLD: Add Python 3.11 builds to CI +* :ghpull:`23632`: Add discouraged admonitions +* :ghpull:`23620`: Doc update deps +* :ghpull:`23627`: Bump pypa/cibuildwheel from 2.8.1 to 2.9.0 +* :ghpull:`23628`: Change Title Case to Upper lower in templates +* :ghpull:`23206`: Change exception type for incorrect SVG date metadata +* :ghpull:`23387`: Remove setuptools_scm_git_archive dependency and add sdist test +* :ghpull:`23605`: Fix issues in examples, docs, and tutorials +* :ghpull:`23618`: [Doc]: Document the position parameter in apply_aspect() +* :ghpull:`23355`: Revert "Try to unbreak CI by xfailing OSX Tk tests" +* :ghpull:`23610`: TST: be more forgiving about IDing Noto +* :ghpull:`23609`: print version number when building docs +* :ghpull:`20832`: Implement multi-font embedding for PS Backend +* :ghpull:`20804`: Implement multi-font embedding for PDF Backend +* :ghpull:`23202`: MNT: Remove cached renderer from figure +* :ghpull:`23497`: Avoid gridspec in more examples +* :ghpull:`23602`: Editing "issues for new contributors" +* :ghpull:`23600`: DOC: view_init docstring for 3d axes primary view angles +* :ghpull:`23587`: BUG:datetime list starting with none +* :ghpull:`23559`: re-base of font fallback for pdf and eps output + SVG support +* :ghpull:`23557`: BLD: update the manylinux versions used +* :ghpull:`23596`: Minor cleanup of axes_grid1 +* :ghpull:`23594`: Expire deprecation on passing bytes to FT2Font.set_text +* :ghpull:`23435`: Add conda env to setup instructions +* :ghpull:`23574`: Move colorbar() doc to method itself. +* :ghpull:`23584`: Bump Ubuntu to 20.04 on GitHub Actions +* :ghpull:`23561`: Clean up code in tri +* :ghpull:`23582`: Cleanup axis3d.Axis.draw +* :ghpull:`23510`: Refactor Widget tests +* :ghpull:`20718`: Circle: Build docs in parallel. +* :ghpull:`22452`: ENH: add ability to remove layout engine +* :ghpull:`23516`: warning when scatter plot color settings discarded +* :ghpull:`23577`: apply_aspect cleanups +* :ghpull:`23575`: Cleanup parasite_simple example. +* :ghpull:`23567`: Remove noop setattr_cm. +* :ghpull:`23412`: Fix dash offset bug in Patch +* :ghpull:`21756`: MNT: Clean up some UTF strings and memory autorelease +* :ghpull:`23558`: MNT: Use UTF-8 string in macosx backend +* :ghpull:`23550`: Change exception types, improve argument checking, and cleanups in mpl_toolkits +* :ghpull:`23196`: Unify set_pickradius argument +* :ghpull:`20740`: Implement Font-Fallback in Matplotlib +* :ghpull:`22566`: Add rcparam for figure label size and weight +* :ghpull:`23551`: Remove transform arguments from _iter_collection +* :ghpull:`23444`: Deduplicate common parts in LatexManager.{__init__,_setup_latex_process} +* :ghpull:`23017`: [ENH] : Provide axis('equal') for Axes3D (replace PR #22705) +* :ghpull:`22950`: Simplify definition of mathtext symbols & correctly end tokens in mathtext parsing +* :ghpull:`23409`: Provide axis('equal') for Axes3D (replaces PR #23017) +* :ghpull:`23434`: Fix array-like linewidth for 3d scatter +* :ghpull:`23500`: Move the common implementation of Axes.set_x/y/zscale to Axis. +* :ghpull:`23533`: Add tests for sankey and minor fixes +* :ghpull:`23535`: Make margins error as claimed in doc-string +* :ghpull:`23546`: Simplify impl. of functions optionally used as context managers. +* :ghpull:`23494`: Fix various issues from SonarQube +* :ghpull:`23529`: Add workflow dispatch GitHub CI +* :ghpull:`23539`: Small improvements to WebAgg example +* :ghpull:`23541`: Change doc-build CI install order +* :ghpull:`23526`: DOC: make "family" less ambiguous in FontProperties docs +* :ghpull:`23537`: Move the deprecated RendererGTK{3,4}Cairo to a single place. +* :ghpull:`23140`: [Features] Allow setting legend title alignment +* :ghpull:`23538`: Fix imprecise docs re: backend dependencies. +* :ghpull:`23532`: Add test for RGBAxes +* :ghpull:`23453`: Add more tests for mplot3d +* :ghpull:`23501`: Let Axes.clear iterate over Axises. +* :ghpull:`23469`: Inline _init_axis_artists & _init_gridlines into clear. +* :ghpull:`23475`: Add markerfacealt to pass-through arguments for error bar lines +* :ghpull:`23527`: STY: fix whitespace on an assert +* :ghpull:`23495`: Fix sgskip'd examples +* :ghpull:`23404`: Restore matplotlib.__doc__ in Sphinx docs +* :ghpull:`23507`: Add hint when More than {max_open_warning} figures have been opened +* :ghpull:`23499`: Fix outdated comment re: event handlers in test_backends_interactive. +* :ghpull:`23498`: Fix direct instantiation of webagg_core managers. +* :ghpull:`23504`: Clarify formatting of the code-for-reproduction field in bug reports. +* :ghpull:`23489`: Add missing test data to install +* :ghpull:`23482`: Mathtext spaces must be independent of font style. +* :ghpull:`23486`: Bump pypa/cibuildwheel from 2.8.0 to 2.8.1 +* :ghpull:`23461`: Tweak Axes repr. +* :ghpull:`16931`: Make it easier to improve UI event metadata. +* :ghpull:`23468`: Display grid in floating axes example. +* :ghpull:`23467`: Remove old handling for factor=None in axisartist. +* :ghpull:`23443`: Try running the pgf backend off the article class. +* :ghpull:`23373`: Fix pan/zoom crashing when widget lock is unavailable +* :ghpull:`23466`: Update filename in example. +* :ghpull:`23464`: Deprecate macos close handler. +* :ghpull:`23463`: Deprecate Tick.label +* :ghpull:`23455`: Deprecate properties w_xaxis, w_yaxis, and w_zaxis +* :ghpull:`23448`: Tweak callbacks to generate pick events. +* :ghpull:`23233`: Default stem marker color follows the linecolor +* :ghpull:`23452`: Generalize Axes __repr__ to 3D +* :ghpull:`23445`: Compare thread native ids when checking whether running on main thread. +* :ghpull:`20752`: Set norms using scale names. +* :ghpull:`23438`: DOC: numpydoc-ify date Locator classes +* :ghpull:`23427`: Tweak pgf escapes. +* :ghpull:`23432`: Fixed typo in docs animation api +* :ghpull:`23420`: Clean up test_chunksize_fails() +* :ghpull:`23415`: Minor improvements to units_sample example +* :ghpull:`21339`: Added linear scaling test to Hexbin marginals +* :ghpull:`23414`: Bump pypa/cibuildwheel from 2.7.0 to 2.8.0 +* :ghpull:`23413`: Combine chunk size tests into one +* :ghpull:`23403`: Small cleanup to VertexSelector. +* :ghpull:`23291`: In the new/simplified backend API, don't customize draw_if_interactive. +* :ghpull:`23350`: Fixed SVG-as-text image comparison tests. +* :ghpull:`23406`: DOC: Fix calculation of bin centers in multi-histogram +* :ghpull:`23407`: TST: Add missing warning type to pytest.warns +* :ghpull:`23402`: Link 3D animation examples to one another. +* :ghpull:`23401`: Upload wheel artifacts from the correct directory +* :ghpull:`23374`: GOV: point CoC reports at CoC steering council subcomittee mailing list +* :ghpull:`23393`: Clean up formatting of custom cmap example +* :ghpull:`23146`: Update cibuildwheel +* :ghpull:`23368`: Add a helper to generate closed paths. +* :ghpull:`20220`: DOC: add mission statement +* :ghpull:`22364`: Tweak mathtext/tex docs. +* :ghpull:`23377`: Use tick_params more often over tick iteration +* :ghpull:`22820`: [Doc] consolidate ``rect`` documentation +* :ghpull:`23371`: Default animation.convert_args to ["-layers", "OptimizePlus"]. +* :ghpull:`23148`: DOC: change address to send security issues to +* :ghpull:`23365`: DOC: add new showcase example, replace gendered one +* :ghpull:`23033`: Fix issue with tex-encoding on non-Unicode platforms +* :ghpull:`23358`: Shorten/clarify definition of extension types. +* :ghpull:`23370`: Small cleanups to animation. +* :ghpull:`23364`: Rename/change signature of PyGlyph_new. +* :ghpull:`23363`: Simplify FigureCanvas multiple inheritance init by swapping bases order. +* :ghpull:`23366`: MNT: use devel version of theme +* :ghpull:`23357`: Fixed decimal points not appearing at end of Mathtext string. +* :ghpull:`23351`: DOC/MNT install docs with dev version of sphinx theme +* :ghpull:`23349`: CI: Remove old scipy-wheels-nightly uploads to ensure space +* :ghpull:`23348`: Support multi-figure MultiCursor; prepare improving its signature. +* :ghpull:`23360`: embedding_in_tk_sgskip.py: use root.destroy +* :ghpull:`23354`: MNT: Use list comprehension +* :ghpull:`23299`: FIX/API: do not reset backend key in rc_context +* :ghpull:`23191`: ENH: add width_ratios and height_ratios to subplots +* :ghpull:`23060`: MNT: Change objective C code to Automatic Reference Counting (ARC) +* :ghpull:`23347`: Simplify/improve check for pycairo in Gtk-based backends. +* :ghpull:`23316`: DOC: improve spines crosslinking +* :ghpull:`23100`: Remove custom backend_nbagg.show(), putting logic in manager show. +* :ghpull:`23342`: FIX: make sure addFont test removes the test font +* :ghpull:`23266`: negative_linestyles kwarg in contour.py +* :ghpull:`23332`: Validate Text linespacing on input. +* :ghpull:`23336`: Remove ineffective exclusion of Arcs without parent Axes. +* :ghpull:`23341`: MNT: Use '--pytest-test-first' option for naming clarity +* :ghpull:`23337`: Remove now inexistent "datapath" rcParam from style blacklist. +* :ghpull:`22004`: Make RendererCairo auto-infer surface size. +* :ghpull:`23208`: ENH: enable stripey lines +* :ghpull:`23288`: Correct URL area with rotated texts in PDFs +* :ghpull:`23197`: Add tests for pan +* :ghpull:`22167`: Deprecate selector ``visible`` attribute +* :ghpull:`23322`: Cleanup FontProperties examples. +* :ghpull:`23321`: Tweak examples capitalization/punctuation. +* :ghpull:`23270`: Fix handling of nonmath hyphens in mathtext. +* :ghpull:`23310`: Move Cursor demo from examples/misc to examples/event_handling +* :ghpull:`23313`: Drop CSS styles that are in mpl-sphinx-theme +* :ghpull:`23314`: Don't draw invisible 3D Axes +* :ghpull:`23302`: Deprecate stem(..., use_line_collection=False) +* :ghpull:`23309`: Remove front page examples +* :ghpull:`23282`: Backport PR #22865 on branch v3.5.x (Fix issue with colorbar extend and drawedges) +* :ghpull:`23231`: Add pytest-xvfb as test dependency +* :ghpull:`23318`: No need to return OrderedDict from _gen_axes_spines. +* :ghpull:`23295`: Replace re.sub by the faster str.translate. +* :ghpull:`23300`: Modify example of "Fig Axes Customize Simple" +* :ghpull:`23014`: Improve consistency in LogLocator and LogFormatter API +* :ghpull:`23286`: Refactor URL handling in PDF backend +* :ghpull:`23065`: Fix test_image_comparison_expect_rms +* :ghpull:`23294`: Simplify binary data handling in ps backend. +* :ghpull:`23284`: DOC: Switch to HTML5 and cleanup CSS +* :ghpull:`23276`: Add get/set methods for DPI in SubFigure +* :ghpull:`23207`: Update build environment and improve test +* :ghpull:`23213`: DEV: Add name-tests-test to pre-commit hooks +* :ghpull:`23289`: Properly make Name.hexify go through a deprecation cycle. +* :ghpull:`23177`: Deprecate positional passing of most Artist constructor parameters +* :ghpull:`23287`: Minor tweaks to pdf Name. +* :ghpull:`23285`: In mathtext, replace manual caching (via ``glyphd``) by lru_cache. +* :ghpull:`23034`: Correctly read the 'style' argument while processing 'genfrac'. +* :ghpull:`23247`: Support inverted parentheses in mathtext. +* :ghpull:`23190`: Deprecate unused methods in axis.py +* :ghpull:`23219`: MNT: Rename example files with 'test' in name +* :ghpull:`23277`: MNT: Remove dead code in SVG backend +* :ghpull:`23261`: Bump actions/setup-python from 3 to 4 +* :ghpull:`23264`: Changing environment.yml for it to work on Windows +* :ghpull:`23269`: MNT: Remove dead code in Colorbar +* :ghpull:`23262`: Simplify qt_compat, in particular post-removal of qt4 support. +* :ghpull:`23263`: Private helper to get requested backend without triggering resolution. +* :ghpull:`23243`: Fix spacing after mathtext operators with sub/superscripts +* :ghpull:`22839`: Fix spacing after mathtext operators with sub/superscripts +* :ghpull:`23256`: DOC: Add note about Inkscape install on Windows +* :ghpull:`23258`: DOC: remove Blue Book url +* :ghpull:`23255`: Add a helper to generate mathtext error strings. +* :ghpull:`23246`: Fix argument checking for set_interpolation_stage +* :ghpull:`22881`: Support not embedding glyphs in svg mathtests. +* :ghpull:`23198`: Rename ncol parameter in legend to ncols +* :ghpull:`23251`: Small simplifications to mathtext tests. +* :ghpull:`23249`: Don't allow ``r"$\left\\|\right.$"``, as in TeX. +* :ghpull:`23248`: Rename test markers +* :ghpull:`22507`: Remove *math* parameter of various mathtext internal APIs. +* :ghpull:`23192`: Add tests, improve error messages in axis/_base, and code cleanup +* :ghpull:`23241`: Fix invalid value in radio buttons example +* :ghpull:`23187`: Correct docs and use keyword arguments in _mathtext.py +* :ghpull:`23045`: MNT: Merge locally defined test marks +* :ghpull:`22289`: ENH: compressed layout +* :ghpull:`23237`: Expire BoxStyle._Base deprecation. +* :ghpull:`23225`: DOC: Fix version switcher links to documentation +* :ghpull:`23221`: DOC: recommend numpy random number generator class +* :ghpull:`23223`: Changed offset reference, add small doc +* :ghpull:`23215`: DOC: link the transforms tutorial from the module +* :ghpull:`23201`: Rework tricontour and tricontourf documentation +* :ghpull:`23013`: Add tests for date module +* :ghpull:`23188`: Mnt new default dates +* :ghpull:`22745`: MNT: Don't require renderer for window_extent and tightbbox +* :ghpull:`23077`: MNT: Remove keyword arguments to gca() +* :ghpull:`23182`: Simplify webagg blitting. +* :ghpull:`23181`: Init FigureCanvasAgg._lastKey in ``__init__``. +* :ghpull:`23175`: Point the version switcher to a name listed in switcher.json +* :ghpull:`22669`: Cleanup documentation generation for pyplot +* :ghpull:`22519`: fix markevery plot option with nans in data +* :ghpull:`21584`: Move towards having get_shared_{x,y}_axes return immutable views. +* :ghpull:`23170`: ENH: update ticks when requesting labels +* :ghpull:`23169`: DOC: Migrate to sphinx-design +* :ghpull:`23180`: Improve docstring of triplot() and PatchCollection +* :ghpull:`23153`: Restore accidentally removed pytest.ini and tests.py. +* :ghpull:`23166`: Deprecate passing most Legend arguments positionally +* :ghpull:`23165`: DOCS Fix a few typos +* :ghpull:`23167`: DOCS fix typo +* :ghpull:`23062`: Add stackplot to plot types listing +* :ghpull:`23161`: Added my (open access) book +* :ghpull:`23141`: Minor fix for astropy units support broken in earlier PR +* :ghpull:`23156`: No longer call draw_if_interactive in parasite_axes. +* :ghpull:`23150`: DOC fix typo +* :ghpull:`23149`: DOCS remove duplicate text +* :ghpull:`23145`: Fix format error in switcher.json +* :ghpull:`21755`: MNT: Clean up macosx backend set_message +* :ghpull:`23128`: DOCS Fix typos +* :ghpull:`23130`: Drop pytest warning config in nightly tests +* :ghpull:`23135`: Unpin coverage again +* :ghpull:`23133`: Make module deprecation messages consistent +* :ghpull:`23134`: Remove newline from start of deprecation warnings +* :ghpull:`22964`: Fix spelling errors +* :ghpull:`22929`: Handle NaN in bar labels and error bars +* :ghpull:`23093`: MNT: Removing 3.4 deprecations +* :ghpull:`23090`: Derive new_figure_manager from FigureCanvas.new_manager. +* :ghpull:`23099`: Remove unneeded cutout for webagg in show(). +* :ghpull:`23097`: Tweak check for IPython pylab mode. +* :ghpull:`23088`: Improve error for invalid format strings / misspelled data keys. +* :ghpull:`23092`: Ensure updated monkey-patching of sphinx-gallery EXAMPLE_HEADER +* :ghpull:`23087`: Fix width/height inversion in dviread debug helper. +* :ghpull:`23089`: Normalize tk load failures to ImportErrors. +* :ghpull:`23091`: Move test that fig.add_axes() needs parameters +* :ghpull:`23067`: more explicit in windows doc build instructions +* :ghpull:`23081`: MNT: Deprecate date_ticker_factory +* :ghpull:`23079`: MNT: Remove key_press and button_press from FigureManager +* :ghpull:`23076`: MNT: Remove positional argument handling in LineCollection +* :ghpull:`23078`: MNT: Remove deprecated axis.cla() +* :ghpull:`23054`: Slightly simplify tcl/tk load in extension. +* :ghpull:`23073`: MNT: Remove dummy_threading because threading is always available +* :ghpull:`22405`: DOC: put the gallery keywords in the meta tag +* :ghpull:`23071`: Fix installing contourpy on CI +* :ghpull:`23068`: Slight refactor of _c_internal_utils to linewrap it better. +* :ghpull:`23070`: Pathlibify autotools invocation in build. +* :ghpull:`22755`: Maybe run autogen as part of freetype install +* :ghpull:`23063`: doc: mathtext example: use axhspan() instead of fill_between() for backdrop rectangle shading +* :ghpull:`23055`: Cleanup Annotation.update_position. +* :ghpull:`22567`: Use contourpy for quad contour calculations +* :ghpull:`22801`: TST: fully parameterize test_lazy_linux_headless +* :ghpull:`22180`: ENH: Use rcParams savefig.directory on macosx backend +* :ghpull:`23048`: Add rrulewrapper to docs +* :ghpull:`23047`: Fix issue with hist and float16 data +* :ghpull:`23044`: Fix missing section header for nightly builds +* :ghpull:`23029`: Demonstrate both usetex and non-usetex in demo_text_path.py. +* :ghpull:`23038`: Factor out errorevery parsing for 2D and 3D errorbars. +* :ghpull:`23036`: Suppress traceback chaining for tex subprocess failures. +* :ghpull:`23037`: Suppress exception chaining in FontProperties. +* :ghpull:`23020`: Add test to close legend issue +* :ghpull:`23031`: Specify that style files are utf-8. +* :ghpull:`22991`: Enable ``plt.sca`` on subfigure's axes +* :ghpull:`23030`: DOC: Fix charset declaration in redirects +* :ghpull:`23022`: Fix some possible encoding issues for non-utf8 systems. +* :ghpull:`23023`: Bump docker/setup-qemu-action from 1 to 2 +* :ghpull:`23024`: DOC: do not suggest to sudo pip install Matplotlib +* :ghpull:`23018`: Fix typo in font family +* :ghpull:`22627`: ENH: rect for constrained_layout +* :ghpull:`22891`: Font example monospace +* :ghpull:`23006`: docs: add subplot-mosaic string compact notation +* :ghpull:`23009`: Fixed installation guide command typo +* :ghpull:`22926`: Fix RangeSlider for same init values #22686 +* :ghpull:`22989`: Merge v3.5.x back into main +* :ghpull:`22993`: STY: Fix typos in colormap +* :ghpull:`22777`: DEV: Add codespell to pre-commit hooks +* :ghpull:`22940`: Fixed dpi bug in rainbow text example +* :ghpull:`22298`: MNT: Remove cmap_d colormap access +* :ghpull:`22387`: Add a registry for color sequences +* :ghpull:`21594`: Document text alignment +* :ghpull:`22967`: TST: Add some tests for QuadMesh contains function +* :ghpull:`22936`: ENH: Add full-screen toggle to the macosx backend +* :ghpull:`22886`: MNT: remove mpl_toolkits.axes_grid +* :ghpull:`22952`: Make MarkerStyle immutable +* :ghpull:`22953`: MNT: Move set_cursor to the FigureCanvas +* :ghpull:`18854`: Standardize creation of FigureManager from a given FigureCanvas class. +* :ghpull:`22925`: Standardize creation of FigureManager from a given FigureCanvas class. +* :ghpull:`22875`: Remove Forward definitions where possible. +* :ghpull:`22928`: ENH: Add option to disable raising the window for macosx +* :ghpull:`22912`: DOC: Better doc of colors +* :ghpull:`22931`: BUG: Fix regression with ls=(0, ()) +* :ghpull:`22909`: FIX: skip sub directories when finding fonts on windows +* :ghpull:`22911`: Clarify docstring of [un]install_repl_displayhook() +* :ghpull:`22919`: CI: Add concurrency skips for GH Actions +* :ghpull:`22899`: Fix documentation markup issues +* :ghpull:`22906`: Clarify logic for repl displayhook. +* :ghpull:`22892`: Remove support for IPython<4. +* :ghpull:`22896`: Remove python-dateutil as test requirement +* :ghpull:`22885`: Deprecate two-layered backend_pdf.Op enum. +* :ghpull:`22883`: Tweak argument checking in tripcolor(). +* :ghpull:`22884`: Missing ``f`` prefix on f-strings fix +* :ghpull:`22877`: Small cleanups to mathtext. +* :ghpull:`21374`: Snap selectors +* :ghpull:`22824`: Remove some unnecessary extra boundaries for colorbars with extensions. +* :ghpull:`21448`: Use named groups in mathtext parser. +* :ghpull:`22609`: Improve usability of dviread.Text by third parties. +* :ghpull:`22809`: STY: Apply pre-commit hooks to codebase +* :ghpull:`22730`: Fix removed cross-references +* :ghpull:`22857`: Slightly simplify twin axes detection in MEP22 zoom. +* :ghpull:`22813`: MNT: Deprecate figure callbacks +* :ghpull:`22802`: MNT: make Axes.cla an alias for Axes.clear in all cases +* :ghpull:`22855`: Remove non-needed remove_text=False. +* :ghpull:`22854`: TST: Avoid floating point errors in asinh ticker +* :ghpull:`22850`: Simplify tick creation +* :ghpull:`22841`: Fix Tk error when updating toolbar checkbutton images +* :ghpull:`22707`: Proposed ENH: Allow user to turn off breaking of streamlines in streamplot (rebased) +* :ghpull:`22826`: Bump actions/upload-artifact from 2 to 3 +* :ghpull:`22825`: Bump codecov/codecov-action from 2 to 3 +* :ghpull:`22821`: Use bool for bool keyword arguments +* :ghpull:`22815`: Fix pickling of globally available, dynamically generated norm classes. +* :ghpull:`22702`: Doc tweak transform tutorial +* :ghpull:`22613`: DOC: Add links to explicit vs implicit API everywhere "OO" is used +* :ghpull:`22712`: Use repr in error messages +* :ghpull:`22794`: Fix ps export of colored hatches with no linewidth +* :ghpull:`22797`: Deprecate functions in backends +* :ghpull:`22608`: Axes.inset_axes: enable Axes subclass creation +* :ghpull:`22795`: Replace "marker simplification" by "marker subsampling" in docs. +* :ghpull:`22768`: Fix inkscape tests +* :ghpull:`22791`: Tweak _ConverterError reporting. +* :ghpull:`22447`: Improve bar_label annotation +* :ghpull:`22710`: Fix the error- TypeError: 'float' object is not iterable +* :ghpull:`22444`: Revert "CI: skip test to work around gs bug" +* :ghpull:`22785`: CI: Update weekly dependency test job +* :ghpull:`22784`: Fix 'misspelled' transform variable +* :ghpull:`22778`: Fix LaTeX formatting in examples +* :ghpull:`22779`: Improve mlab documentation (and example) +* :ghpull:`22759`: MNT: Skip existing wheels during nightly wheel upload +* :ghpull:`22751`: BLD: do not put an upper bound on pyparsing +* :ghpull:`22752`: DOC: Correct nightly wheels pip install command +* :ghpull:`22742`: Fix deprecation of backend_tools.ToolBase.destroy +* :ghpull:`22725`: Move towards making texmanager stateless. +* :ghpull:`22734`: Added clim support to tripcolor +* :ghpull:`22733`: CI: Add GHA workflow to upload nightly wheels +* :ghpull:`21637`: Also upload a subset of nightly wheels +* :ghpull:`22698`: Correct cross-references in documentation +* :ghpull:`22263`: DOC: condense version switcher +* :ghpull:`22361`: Revert datetime usetex ticklabels to use default tex font. +* :ghpull:`22721`: Small style fixes. +* :ghpull:`22356`: Cleanup tripcolor() +* :ghpull:`22360`: Let TeX handle multiline strings itself. +* :ghpull:`22418`: Deprecate auto-removal of overlapping Axes by plt.subplot{,2grid}. +* :ghpull:`22722`: Rename confusingly-named cm_fallback. +* :ghpull:`22697`: Deprecate in testing.decorators +* :ghpull:`22556`: Add text.parse_math rcParams +* :ghpull:`22163`: Change colour of Tk toolbar icons on dark backgrounds +* :ghpull:`22704`: Small simplification to textpath. +* :ghpull:`22498`: TST: increase coverage on tk tests +* :ghpull:`21425`: Make Axis3D constructor signature closer to the one of 2D axis. +* :ghpull:`22665`: Improve error message for incorrect color string +* :ghpull:`22685`: Rewrite plot format detection from sphinx build target +* :ghpull:`22670`: Update deprecated vmImage 'vs2017-win2016' in azure pipelines +* :ghpull:`22503`: Deprecate backend_qt.qApp. +* :ghpull:`22683`: Add missing space before : for parameters +* :ghpull:`22591`: Fix Path/str-discrepancy in FontManager.addpath and improve documentation +* :ghpull:`22680`: Bump actions/cache from 2 to 3 +* :ghpull:`22659`: Add description on quiver head parameters +* :ghpull:`22668`: Raise on missing closing quotes in matplotlibrc +* :ghpull:`22675`: Tweak colorbar_placement example. +* :ghpull:`22276`: Merge "Scatter Symbol" and "Scatter Custom Symbol" examples +* :ghpull:`22658`: Remove reference to now-deleted reminder note. +* :ghpull:`22652`: Update documentation example and fix See also +* :ghpull:`22587`: Refactor handling of tick and ticklabel visibility in Axis.clear() +* :ghpull:`22148`: MNT: Deprecate ``docstring`` +* :ghpull:`22170`: Add example to polygon selector docstring showing how to set vertices programmatically +* :ghpull:`22650`: Fix new leak in ft2font introduced in #22604 +* :ghpull:`22644`: FIX: Flush events after closing figures in macosx backend +* :ghpull:`22643`: Suppress exception chaining in colormap lookup. +* :ghpull:`22639`: ENH: MacOSX backend to use sRGB instead of GenericRGB colorspace +* :ghpull:`22509`: Simplifications to ToolManager.{add,remove}_tool. +* :ghpull:`22633`: DOC: remove space in directive. +* :ghpull:`22631`: Add space between individual transform components in svg output. +* :ghpull:`22523`: MNT: Use a context manager to change the norm in colorbar code +* :ghpull:`22615`: FIX: Change get_axis_map to axis_map now +* :ghpull:`22508`: Move tracking of autoscale status to Axis. +* :ghpull:`22547`: Small cleanups around TexManager usage. +* :ghpull:`22511`: Remove redundant rcParam-lookup in patches +* :ghpull:`22516`: Expire deprecations in backends +* :ghpull:`22612`: Updated grammar to reflect more common usage of output vs outputted in animation.py +* :ghpull:`22589`: Support quoted strings in matplotlibrc +* :ghpull:`22604`: MNT: Fix types in C-code to reduce warnings +* :ghpull:`22610`: Fix alternative suggestion in epoch2num() deprecation +* :ghpull:`22554`: Prepare for making create_dummy_axis not necessary. +* :ghpull:`22607`: ENH: Add dark/light mode theme to the buttons +* :ghpull:`21790`: FIX: Update blitting and drawing on the macosx backend +* :ghpull:`22175`: FIX: Update macosx animation handling +* :ghpull:`22569`: Require non-zero dash value +* :ghpull:`22544`: Correct paper sizes +* :ghpull:`20470`: Issues warnings for legend handles without handlers +* :ghpull:`22558`: MNT: Simplify imports +* :ghpull:`22580`: fix doc for annotation_clip parameter +* :ghpull:`22581`: DOC: fix various typos +* :ghpull:`22573`: Bump actions/setup-python from 2 to 3 +* :ghpull:`22568`: Rename qhull source to _qhull_wrapper.cpp. +* :ghpull:`22561`: FIX: Handle stopped animation figure resize +* :ghpull:`22562`: TST: Add a frame test for animations +* :ghpull:`22514`: Expire deprecations in cbook.deprecation +* :ghpull:`22555`: Use picklable callbacks for DraggableBase. +* :ghpull:`22552`: Tweak dependency checking in doc/conf.py. +* :ghpull:`22550`: Require sphinx>=3 & numpydoc>=1.0 for building docs. +* :ghpull:`22539`: Deprecate toplevel mpl.text.get_rotation; normalize rotations early. +* :ghpull:`22502`: Cleanup unused imports and variables in backends +* :ghpull:`20071`: Document, test, and simplify impl. of auto_adjustable_area. +* :ghpull:`22366`: Deprecation removal/updates in axes3d +* :ghpull:`22484`: Simplify the internal API to connect picklable callbacks. +* :ghpull:`22417`: Support passing rgbaFace as an array to agg's draw_path. +* :ghpull:`22412`: Turn _get_axis_map() into a property and remove _get_axis_list() +* :ghpull:`22486`: Expire deprecations in lines and patches +* :ghpull:`22512`: Increase coverage +* :ghpull:`22504`: Simplify FontProperties init. +* :ghpull:`22497`: Remove entries of MathTextParser._backend_mapping deprecated in 3.4. +* :ghpull:`22487`: Don't key MathTextParser cache off a mutable FontProperties. +* :ghpull:`22468`: Turn _mathtext.ship into a plain function. +* :ghpull:`22490`: Deprecate unused, untested Affine2D.identity(). +* :ghpull:`22491`: Linewrap setupext to 79 character lines. +* :ghpull:`22488`: Some more maintenance for mathtext internal implementation. +* :ghpull:`22485`: Change string representation of AxesImage +* :ghpull:`22240`: Add minimum macosx version +* :ghpull:`22480`: Remove _point_size_reduction. +* :ghpull:`22204`: Cleanup _mathtext internal API +* :ghpull:`22469`: Improve readability of mathtext internal structures. +* :ghpull:`22477`: Un-pyplot some examples which were already explicitly referencing axes. +* :ghpull:`22467`: Small cleanup to font handling in agg. +* :ghpull:`21178`: Add asinh axis scaling (*smooth* symmetric logscale) +* :ghpull:`22411`: Move cbook._define_aliases() to _api.define_aliases() +* :ghpull:`22465`: Deprecate unused AddList. +* :ghpull:`22451`: Clarify error message for bad keyword arguments. +* :ghpull:`21267`: Cleanup AnnotationBbox. +* :ghpull:`22464`: Small improvements related to radar_chart example. +* :ghpull:`22421`: Make most params to figure()/Figure() kwonly. +* :ghpull:`22457`: Copy arrowprops argument to FancyAnnotationBbox. +* :ghpull:`22454`: move ``_toolbar_2`` from webagg_core to webagg +* :ghpull:`22413`: Remove some trivial private getters/setters in axisartist +* :ghpull:`21634`: TST: Add future dependency tests as a weekly CI job +* :ghpull:`22079`: Share FigureManager class between gtk3 and gtk4. +* :ghpull:`22440`: Clarify warning about labels with leading underscores. +* :ghpull:`17488`: Make error message explicit in legend.py +* :ghpull:`22453`: Simplify impl. of polar limits setting API. +* :ghpull:`22449`: Small cleanup to quiver. +* :ghpull:`22415`: Make emit and auto args of set_{x,y,z}lim keyword only. +* :ghpull:`22422`: Deprecate backend_ps.convert_psfrags. +* :ghpull:`22194`: Drop support for Python 3.7 +* :ghpull:`22234`: Partial fix for grid alpha +* :ghpull:`22433`: Fix ambiguous link targets in docs. +* :ghpull:`22420`: Update plt.figure() docstring. +* :ghpull:`22388`: Make signature of Axes.annotate() more explicit. +* :ghpull:`22419`: Remove "Matplotlib version" from docs issue template +* :ghpull:`22423`: Avoid indiscriminate glob-remove in xpdf_distill. +* :ghpull:`22406`: [DOC]: Removed a redundant 'The' +* :ghpull:`21442`: Factor out common limits handling for x/y/z axes. +* :ghpull:`22397`: Axes capitalization in widgets and axes3d +* :ghpull:`22394`: Tweak Axes3D docstrings that refer to 2D plotting methods. +* :ghpull:`22383`: TST: fix doc build +* :ghpull:`21877`: DOC: attempt to explain the main different APIs +* :ghpull:`21238`: Raise when unknown signals are connected to CallbackRegistries. +* :ghpull:`22345`: MNT: make layout deprecations pending +* :ghpull:`21597`: FIX: Remove the deepcopy override from transforms +* :ghpull:`22370`: Replace tabs with spaces in C code. +* :ghpull:`22371`: Corrected a mistake in comments (Issue #22369) +* :ghpull:`21352`: Refactor hexbin(). +* :ghpull:`19214`: Improve autoscaling for high order Bezier curves +* :ghpull:`22268`: Deprecated is_decade and is_close_to_int +* :ghpull:`22359`: Slightly refactor TeX source generation. +* :ghpull:`22365`: Remove deprecated ``MovieWriter.cleanup`` +* :ghpull:`22363`: Properly capitalize "Unicode". +* :ghpull:`22025`: Deprecate various custom FigureFrameWx attributes/methods. +* :ghpull:`21391`: Reuse imsave()'s background-blending code in FigureCanvasAgg.print_jpeg. +* :ghpull:`22026`: Simplify wxframe deletion. +* :ghpull:`22351`: Fix "trailing" whitespace in C docstrings. +* :ghpull:`22342`: Docstrings for _qhull. +* :ghpull:`21836`: Slightly shorten ft2font init. +* :ghpull:`21962`: Privatize various internal APIs of backend_pgf. +* :ghpull:`22114`: Rewrite AxesStack independently of cbook.Stack. +* :ghpull:`22332`: Let TransformedPatchPath inherit most functionality from TransformedPath. +* :ghpull:`22292`: Cleanup Axis._translate_tick_kw +* :ghpull:`22339`: wx.App() should be init'ed in new_figure_manager_given_figure +* :ghpull:`22315`: More standardization of floating point slop in mpl_toolkits. +* :ghpull:`22337`: DOC: More cleanup axes -> Axes +* :ghpull:`22323`: Replace sole use of maxdict by lru_cache. +* :ghpull:`22229`: FIX: make safe to add / remove artists during ArtistList iteration +* :ghpull:`22196`: ``dates`` classes and functions support ``tz`` both as string and ``tzinfo`` +* :ghpull:`22161`: Add box when setting ``PolygonSelector.verts`` +* :ghpull:`19368`: Raise warning and downsample if data given to _image.resample is too large +* :ghpull:`22250`: Unify toolbar init across backends. +* :ghpull:`22304`: Added tests for ContourSet.legend_elements +* :ghpull:`21583`: Add pre-commit config and dev instructions +* :ghpull:`21547`: Custom cap widths in box and whisker plots in bxp() and boxplot() +* :ghpull:`20887`: Implement a consistent behavior in TkAgg backend for bad blit bbox +* :ghpull:`22317`: Rename outdated seaborn styles. +* :ghpull:`22271`: Rework/fix Text layout cache. +* :ghpull:`22097`: In mpl_toolkits, use the same floating point slop as for standard ticks. +* :ghpull:`22295`: Display bad format string in error message. +* :ghpull:`22287`: Removed unused code and variables +* :ghpull:`22244`: MNT: colorbar locators properties +* :ghpull:`22270`: Expanded documentation of Axis.set_ticks as per discussion in issue #22262 +* :ghpull:`22280`: Simplify FontProperties.copy(). +* :ghpull:`22174`: Give the Tk toolbar buttons a flat look +* :ghpull:`22046`: Add the ability to change the focal length of the camera for 3D plots +* :ghpull:`22251`: Colorbar docstring reorg +* :ghpull:`21933`: MNT: privatize colorbar attr +* :ghpull:`22258`: DOC: fix version switcher +* :ghpull:`22261`: DOC: fix switcher json +* :ghpull:`22154`: Add some tests for minspan{x,y} in RectangleSelector +* :ghpull:`22246`: DOC: add dropdown +* :ghpull:`22133`: Deprecated ``afm``, ``fontconfig_pattern``, and ``type1font`` +* :ghpull:`22249`: DOC: More capitalization of Axes +* :ghpull:`22021`: Ensure that all toolbar (old/new) subclasses can be init'ed consistently +* :ghpull:`22213`: Improve ft2font error reporting. +* :ghpull:`22245`: Deprecate cleared kwarg to get_renderer. +* :ghpull:`22239`: Fix typos +* :ghpull:`22216`: turn off the grid after creating colorbar axes +* :ghpull:`22055`: FIX: Return value instead of enum in get_capstyle/_joinstyle +* :ghpull:`22228`: Remove some unnecessary getattrs. +* :ghpull:`20426`: ENH: Layout engine +* :ghpull:`22224`: Trivial doc fix to annotations tutorial. +* :ghpull:`21894`: Jointly track x and y in PolygonSelector. +* :ghpull:`22205`: Bump minimum NumPy to 1.19 +* :ghpull:`22203`: Factor out underline-thickness lookups in mathtext. +* :ghpull:`22189`: DOC: Add hatch API to reference +* :ghpull:`22084`: Clean up 3d plot box_aspect zooming +* :ghpull:`22098`: Expire axes_grid1/axisartist deprecations. +* :ghpull:`22013`: Use standard toolbar in wx. +* :ghpull:`22160`: Removed unused variables etc. +* :ghpull:`22179`: FIX: macosx check case-insensitive app name +* :ghpull:`22157`: Improved coverage of mathtext and removed unused code +* :ghpull:`21781`: Use a fixture to get widget testing axes +* :ghpull:`22140`: Ensure log formatters use Unicode minus +* :ghpull:`21342`: Fix drawing animated artists changed in selector callback +* :ghpull:`22134`: Deprecated ``tight_bbox`` and ``tight_layout`` modules +* :ghpull:`21965`: Switch transOffset to offset_transform. +* :ghpull:`22145`: Make Tk windows use the same icon as other backends +* :ghpull:`22107`: Expire mathttext-related deprecations +* :ghpull:`22139`: FIX: width/height were reversed in macosx rectangle creation +* :ghpull:`22123`: Deprecate accepting arbitrary parameters in some get_window_extent() methods +* :ghpull:`22122`: Hint at draw_without_rendering() in Text.get_window_extent +* :ghpull:`22120`: Drop dependency on scipy in the docs. +* :ghpull:`22063`: FIX: Autoposition title when yaxis has offset +* :ghpull:`22119`: Micro-optimize skew(). +* :ghpull:`22109`: Remove unnecessary null checks in macosx.m, and some more maintenance +* :ghpull:`21977`: Add corner coordinate helper methods to Ellipse/Rectangle +* :ghpull:`21830`: Add option of bounding box for PolygonSelector +* :ghpull:`22115`: Turn _localaxes into a plain list. +* :ghpull:`22108`: Micro-optimize rotation transform. +* :ghpull:`22043`: Cleanup differential equations examples. +* :ghpull:`22080`: Simple style(ish) fixes. +* :ghpull:`22110`: Right-aligned status text in backends +* :ghpull:`21873`: DOC: Update and consolidate Custom Tick Formatter for Time Series example +* :ghpull:`22112`: Fix a small typo +* :ghpull:`20117`: Very soft-deprecate AxesDivider.new_{horizontal,vertical}. +* :ghpull:`22034`: Update lines_with_ticks_demo.py +* :ghpull:`22102`: DOC: rename usage tutorial to quick_start +* :ghpull:`19228`: Validate text rotation in setter +* :ghpull:`22081`: Expire colorbar-related deprecations. +* :ghpull:`22008`: Added color keyword argument to math_to_image +* :ghpull:`22058`: Remove exprired mplot3d deprecations for 3.6 +* :ghpull:`22073`: DOC: Add new tutorial to external resources. +* :ghpull:`22054`: MNT: Set CapStyle member names automatically +* :ghpull:`22061`: De-duplicate mplot3D API docs +* :ghpull:`22075`: Remove unnecessary ``.figure`` qualifier in docs. +* :ghpull:`22051`: Make required_interactive_framework required on FigureCanvas. +* :ghpull:`22050`: Deprecate the noop, unused FigureCanvasBase.resize. +* :ghpull:`22030`: Add explanatory comments to "broken" horizontal bar plot example +* :ghpull:`22001`: Fix: [Bug]: triplot with 'ls' argument yields TypeError #21995 +* :ghpull:`22045`: Fill in missing Axes3D box_aspect argument docstring +* :ghpull:`22042`: Keep FontEntry helpers private. +* :ghpull:`21042`: Make rcParams.copy() return a new RcParams instance. +* :ghpull:`22032`: flipy only affects the drawing of texts, not of images. +* :ghpull:`21993`: Added docstring to rrulewrapper class +* :ghpull:`21935`: Significantly improve tight layout performance for cartopy axes +* :ghpull:`22000`: Some gtk cleanups. +* :ghpull:`21983`: Simplify canvas class control in FigureFrameWx. +* :ghpull:`21985`: Slightly tighten the _get_layout_cache_key API. +* :ghpull:`22020`: Simplify wx _print_image. +* :ghpull:`22010`: Fix syntax highlighting in contrib guide. +* :ghpull:`22003`: Initialize RendererCairo.{width,height} in constructor. +* :ghpull:`21992`: Use _make_classic_style_pseudo_toolbar more. +* :ghpull:`21916`: Fix picklability of make_norm_from_scale norms. +* :ghpull:`21981`: FigureCanvasCairo can init RendererCairo; kill RendererCairo subclasses. +* :ghpull:`21986`: InvLogTransform should only return masked arrays for masked inputs. +* :ghpull:`21991`: PEP8ify wx callback names. +* :ghpull:`21975`: DOC: remove experimental tag from CL +* :ghpull:`21989`: Autoinfer norm bounds. +* :ghpull:`21980`: Removed loaded modules logging +* :ghpull:`21982`: Deprecate duplicated FigureManagerGTK{3,4}Agg classes. +* :ghpull:`21963`: Clarify current behavior of draw_path_collection. +* :ghpull:`21974`: Reword inset axes example. +* :ghpull:`21835`: Small improvements to interactive examples +* :ghpull:`21050`: Store dash_pattern as single attribute, not two. +* :ghpull:`21557`: Fix transparency when exporting to png via pgf backend. +* :ghpull:`21904`: Added _repr_html_ for fonts +* :ghpull:`21696`: Use cycling iterators in RendererBase. +* :ghpull:`21955`: Refactor common parts of ImageMagick{,File}Writer. +* :ghpull:`21952`: Clarify coordinates for RectangleSelector properties +* :ghpull:`21964`: Fix some more missing references. +* :ghpull:`21516`: Make _request_autoscale_view more generalizable to 3D. +* :ghpull:`21947`: Slightly cleanup RendererBase docs. +* :ghpull:`21961`: Privatize various internal APIs of backend_pgf. +* :ghpull:`21956`: Remove tests for avconv animation writers. +* :ghpull:`21954`: DOC: Move Animation and MovieWriter inheritance diagrams ... +* :ghpull:`21780`: Add a click_and_move widget test helper +* :ghpull:`21941`: Merge branch v3.5.x into main +* :ghpull:`21936`: Small ``__getstate__`` cleanups. +* :ghpull:`21939`: Update comment re: register_at_fork. +* :ghpull:`21910`: Fold _rgbacache into _imcache. +* :ghpull:`21921`: Clean up RectangleSelector move code +* :ghpull:`21925`: Drop labelling from PR welcome action +* :ghpull:`14930`: Set Dock icon on the macosx backend +* :ghpull:`21920`: Improve square state calculation in RectangleSelector +* :ghpull:`21919`: Fix use_data_coordinates docstring +* :ghpull:`21881`: Add a PolygonSelector.verts setter +* :ghpull:`20839`: Fix centre and square state and add rotation for rectangle selector +* :ghpull:`21874`: DOC: Add Date Tick Locators and Formatters example +* :ghpull:`21799`: Added get_font_names() to fontManager +* :ghpull:`21871`: DOC: Code from markevery_prop_cycle moved to test. +* :ghpull:`21395`: Expire _check_savefig_extra_args-related deprecations. +* :ghpull:`21867`: Remove unused bbox arg to _convert_agg_to_wx_bitmap. +* :ghpull:`21868`: Use partialmethod for better signatures in backend_ps. +* :ghpull:`21520`: Shorten some inset_locator docstrings. +* :ghpull:`21737`: Update the "Rotating a 3D plot" gallery example to show all 3 rotation axes +* :ghpull:`21851`: Re-order a widget test function +* :ghpull:`10762`: Normalization of elevation and azimuth angles for surface plots +* :ghpull:`21426`: Add ability to roll the camera in 3D plots +* :ghpull:`21822`: Replace NSDictionary by switch-case. +* :ghpull:`21512`: MNT: Add modifier key press handling to macosx backend +* :ghpull:`21784`: Set macOS icon when using Qt backend +* :ghpull:`21748`: Shorten PyObjectType defs in macosx.m. +* :ghpull:`21809`: MNT: Turn all macosx warnings into errors while building +* :ghpull:`21792`: Fix missing return value in closeButtonPressed. +* :ghpull:`21767`: Inherit many macos backend docstrings. +* :ghpull:`21766`: Don't hide build log on GHA. +* :ghpull:`21728`: Factor out some macosx gil handling for py-method calls from callbacks. +* :ghpull:`21754`: Update gitattributes so that objc diffs are correctly contextualized. +* :ghpull:`21752`: Add a helper for directly output pdf streams. +* :ghpull:`21750`: Don't sort pdf dicts. +* :ghpull:`21745`: DOC: Clarify Coords Report Example +* :ghpull:`21746`: Fix/add docstring signatures to many C++ methods. +* :ghpull:`21631`: DOC: change gridspec tutorial to arranging_axes tutorial +* :ghpull:`21318`: FIX: better error message for shared axes and axis('equal') +* :ghpull:`21519`: mark_inset should manually unstale axes limits before drawing itself. +* :ghpull:`21724`: Fix copyright date with SOURCE_DATE_EPOCH set +* :ghpull:`21398`: FIX: logic of title repositioning +* :ghpull:`21717`: Simplify macosx toolbar init. +* :ghpull:`21690`: Whitespace/braces/#defines cleanup to macosx. +* :ghpull:`21695`: Use _api.check_shape more. +* :ghpull:`21698`: Small code cleanups and style fixes. +* :ghpull:`21529`: Delay-load keymaps in toolmanager. +* :ghpull:`21525`: Fix support for clim in scatter. +* :ghpull:`21697`: Drop non-significant zeros from ps output. +* :ghpull:`21692`: CI: Remove CI test runs from forks of matplotlib +* :ghpull:`21591`: Make ToolFullScreen a Tool, not a ToolToggle. +* :ghpull:`21677`: Simplify test for negative xerr/yerr. +* :ghpull:`21657`: Replace some image_comparisons by return-value-tests/check_figures_e… +* :ghpull:`21664`: Merge 3.5.x into main +* :ghpull:`21490`: Make Line2D copy its inputs +* :ghpull:`21639`: Skip some uses of packaging's PEP440 version for non-Python versions. +* :ghpull:`21604`: Fix centre square rectangle selector part 1 +* :ghpull:`21593`: Check for images added-and-modified in a same PR +* :ghpull:`20750`: Shorten issue templates +* :ghpull:`21590`: Make gtk3 full_screen_toggle more robust against external changes. +* :ghpull:`21582`: Organize checklist in PR template +* :ghpull:`21580`: Rename/remove _lastCursor, as needed. +* :ghpull:`21567`: Removed the range parameter from the validate_whiskers function's err… +* :ghpull:`21565`: Further remove remnants of offset_position. +* :ghpull:`21542`: [ENH]: Use new style format strings for colorbar ticks +* :ghpull:`21564`: Skip invisible artists when doing 3d projection. +* :ghpull:`21558`: Various small fixes for streamplot(). +* :ghpull:`21544`: Return minorticks as array, not as list. +* :ghpull:`21546`: Added links to the mosaic docs in figure and pyplot module docstrings +* :ghpull:`21545`: Turn mouseover into a mpl-style getset_property. +* :ghpull:`21537`: Remove unnecessary False arg when constructing wx.App. +* :ghpull:`21536`: Reword margins docstrings, and fix bounds on zmargin values. +* :ghpull:`21535`: typo-correction-on-line-185 +* :ghpull:`21534`: Do not use space in directive calling. +* :ghpull:`21494`: Adding tutorial links for blitting in widgets.py +* :ghpull:`21407`: Stash exceptions when FT2Font closes the underlying stream. +* :ghpull:`21431`: set_ticks([single_tick]) should also expand view limits. +* :ghpull:`21444`: Make pipong example self-contained. +* :ghpull:`21392`: Add label about workflow to new contributor PRs +* :ghpull:`21440`: Install sphinx-panels along with development setup +* :ghpull:`21434`: Remove coords_flat variable +* :ghpull:`21415`: Move gui_support.macosx option to packages section. +* :ghpull:`21412`: Privatize some SVG internal APIs. +* :ghpull:`21401`: Uncamelcase some internal variables in axis.py; rename _get_tick_bboxes. +* :ghpull:`21417`: Use Bbox.unit() more. +* :ghpull:`20253`: Simplify parameter handling in FloatingAxesBase. +* :ghpull:`21379`: Simplify filename tracking in FT2Font. +* :ghpull:`21278`: Clear findfont cache when calling addfont(). +* :ghpull:`21400`: Use bbox.{size,bounds,width,height,p0,...} where appropriate. +* :ghpull:`21408`: Reword annotations tutorial section titles. +* :ghpull:`21371`: Rename default branch +* :ghpull:`21389`: Log pixel coordinates in event_handling coords_demo example on terminal/console +* :ghpull:`21376`: Factor common parts of saving to different formats using pillow. +* :ghpull:`21377`: Enable tests for text path based markers +* :ghpull:`21283`: Demonstrate inset_axes in scatter_hist example. +* :ghpull:`21356`: Raise an exception when find_tex_file fails to find a file. +* :ghpull:`21362`: Simplify wording of allowed errorbar() error values +* :ghpull:`21274`: ENH: Add support to save images in WebP format +* :ghpull:`21289`: Simplify _init_legend_box. +* :ghpull:`21256`: Make image_comparison work even without the autoclose fixture. +* :ghpull:`21343`: Fix type1font docstring markup/punctuation. +* :ghpull:`21341`: Fix trivial docstring typo. +* :ghpull:`21301`: Simplify ``Colormap.__call__`` a bit. +* :ghpull:`21280`: Make ``Path.__deepcopy__`` interact better with subclasses, e.g. TextPath. +* :ghpull:`21266`: Fix #21101 Add validator to errorbar method +* :ghpull:`20921`: Fix problem with (deep)copy of TextPath +* :ghpull:`20914`: 19195 rotated markers +* :ghpull:`21276`: Add language about not assigning issues +* :ghpull:`20715`: Improve Type-1 font parsing +* :ghpull:`21218`: Parametrize/simplify test_missing_psfont. +* :ghpull:`21213`: Compress comments in make_image. +* :ghpull:`21187`: Deprecate error_msg_foo helpers. +* :ghpull:`21190`: Deprecate mlab.stride_windows. +* :ghpull:`21152`: Rename ``**kw`` to ``**kwargs``. +* :ghpull:`21087`: Move colormap examples from userdemo to images_contours_and_fields. +* :ghpull:`21074`: Deprecate MarkerStyle(None). +* :ghpull:`20990`: Explicit registration of canvas-specific tool subclasses. +* :ghpull:`21049`: Simplify setting Legend attributes +* :ghpull:`21056`: Deprecate support for no-args MarkerStyle(). +* :ghpull:`21059`: Remove dummy test command from setup.py +* :ghpull:`21015`: Prepare for rcParams.copy() returning a new RcParams instance in the future +* :ghpull:`21021`: Factor out for_layout_only backcompat support in get_tightlayout. +* :ghpull:`21023`: Inline ToolManager._trigger_tool to its sole call site. +* :ghpull:`21005`: Test the rcParams deprecation machinery. +* :ghpull:`21010`: Avoid TransformedBbox where unneeded. +* :ghpull:`21019`: Reword custom_ticker1 example. +* :ghpull:`20995`: Deprecate some backend_gtk3 helper globals. +* :ghpull:`21004`: Remove now-unused rcParams _deprecated entries. +* :ghpull:`20986`: Make HandlerLine2D{,Compound} inherit constructors from HandlerNpoints. +* :ghpull:`20974`: Rename symbol_name to glyph_name where appropriate. +* :ghpull:`20961`: Small cleanups to math_to_image. +* :ghpull:`20957`: legend_handler_map cleanups. +* :ghpull:`20955`: Remove unused HostAxes._get_legend_handles. +* :ghpull:`20851`: Try to install the Noto Sans CJK font + +Issues (202): + +* :ghissue:`23827`: backend_gtk3agg.py calls set_device_scale +* :ghissue:`23560`: [Doc]: mpl_toolkits.axes_grid still mentioned as maintained +* :ghissue:`23794`: [Doc]: Version switcher broken in devdocs +* :ghissue:`23806`: [Bug]: possible regression in axis ticks handling in matplotlib 3.6.0rc2 +* :ghissue:`22965`: [Bug]: triplot duplicates label legend +* :ghissue:`23807`: streamplot raises ValueError when the input is zeros +* :ghissue:`23761`: [Bug]: False positive legend handler warnings in 3.6.0.rc1 +* :ghissue:`23398`: [Bug]: Newer versions of matplotlib ignore xlabel on colorbar axis +* :ghissue:`23699`: [Bug]: Bug with toolbar instantiation in notebook +* :ghissue:`23745`: [Doc]: Minor rcParams/matplotlibrc doc issues +* :ghissue:`23717`: [Bug]: AxesSubplot.get_yticks not returning the actual printed ticks +* :ghissue:`21508`: [Doc]: Create diagram to show rotation directions for 3D plots +* :ghissue:`23709`: [Bug]: colorbar with unattached mappables can't steal space +* :ghissue:`23701`: [Bug]: plt.figure(), plt.close() leaks memory +* :ghissue:`22409`: [Bug]: AttributeError: 'QResizeEvent' object has no attribute 'pos' +* :ghissue:`19609`: DeprecationWarning when changing color maps +* :ghissue:`23716`: MatplotlibDeprecationWarning removal hard-breaks seaborn in 3.6rc1 +* :ghissue:`23719`: [Bug]: register_cmap deprecation message seems wrong +* :ghissue:`23707`: test_Normalize fails on aarch64/ppc64le/s390x +* :ghissue:`21107`: [MNT]: Should plt.xticks() get a minor keyword argument +* :ghissue:`23679`: [Doc]: Deprecated modules not in docs +* :ghissue:`19550`: Arc and pathpatch_2d_to_3d plots full ellipse +* :ghissue:`23329`: [Bug]: ``plt.autoscale()`` fails for partial ``Arc`` +* :ghissue:`11266`: Arc patch ignoring theta1/theta2 when added to Axes via PatchCollection +* :ghissue:`4067`: 'Poly3DCollection' object has no attribute '_facecolors2d' +* :ghissue:`23622`: [MNT]: make.bat not parsing sphinxopt +* :ghissue:`23459`: [Bug]: 'Line3D' object has no attribute '_verts3d' +* :ghissue:`23653`: [Bug]: macosx subplot tool causes segfault when window closed +* :ghissue:`23660`: [Bug]: Test test_figure.py::test_subfigure_ss[png] FAILED on ppc64le +* :ghissue:`23645`: [MNT]: Python 3.11 manylinux wheels +* :ghissue:`23650`: TTF fonts loaded from file are not embedded/displayed properly when saved to pdf +* :ghissue:`23583`: [Doc]: Document the position parameter in apply_aspect() +* :ghissue:`23386`: setuptools_scm-git-archive is obsolete +* :ghissue:`23220`: [Doc]: Clarify ``offset`` parameter in linestyle +* :ghissue:`22746`: [Doc]: Document that rcParams['font.family'] can be a list +* :ghissue:`8187`: Axes doesn't have ````legends```` attribute? +* :ghissue:`23580`: [Bug]: TypeError when plotting against list of datetime.date where 0th element of list is None +* :ghissue:`15514`: Relevant methods are only documented in base classes and thus not easily discoverable +* :ghissue:`21611`: DOC: Add conda environment instructions to developers guide +* :ghissue:`23487`: [Bug]: scatter plot color settings discarded unless c given +* :ghissue:`22977`: [Bug]: offset dash linestyle has no effect in patch objects +* :ghissue:`18883`: Matplotlib would not try to apply all the font in font list to draw all characters in the given string. +* :ghissue:`22570`: [ENH]: Provide ``axis('equal')`` for ``Axes3D``. +* :ghissue:`23433`: [Bug]: array-like linewidth raises an error for scatter3D +* :ghissue:`12388`: Legend Title Left Alignment +* :ghissue:`23375`: [Bug]: markerfacecoloralt not supported when drawing errorbars +* :ghissue:`17973`: DOC: matplotlib.__doc__ not included in online docs ? +* :ghissue:`23474`: [Bug]: ``\,`` and ``\mathrm{\,}`` are not identical in Mathtext when using CM and STIX +* :ghissue:`8715`: event handlers have different signatures across backends +* :ghissue:`18271`: PGF uses the minimal document class +* :ghissue:`23324`: [Bug]: Exception not handled in widgetlock() +* :ghissue:`15710`: doc for type of tz parameter is inconsistent throughout dates.py +* :ghissue:`21165`: Hexbin marginals need a test for linear scaling +* :ghissue:`23105`: [MNT]: Deprecate per-backend customization of draw_if_interactive +* :ghissue:`23147`: [Bug]: with setuptools>=60, cannot find msbuild +* :ghissue:`23379`: [Bug]: Offset notation on y-axis can overlap with a long title +* :ghissue:`22819`: [Doc]: Make rect argument consistent in the docstrings +* :ghissue:`23172`: [Bug]: Calling matplotlib.pyplot.show() outside of matplotlib.pyplot.rc_context no longer works +* :ghissue:`23019`: [Bug]: ``UnicodeDecodeError`` when using some special and accented characters in TeX +* :ghissue:`23334`: [Doc]: Tk embedding example crashes Spyder +* :ghissue:`23298`: [Bug]: get_backend() clears figures from Gcf.figs if they were created under rc_context +* :ghissue:`21942`: [ENH]: add width/height_ratios to subplots and friends +* :ghissue:`23028`: [ENH]: contour kwarg for negative_linestyle +* :ghissue:`19223`: Certain non-hashable parameters to text() give cryptic error messages +* :ghissue:`18351`: Add the ability to plot striped lines +* :ghissue:`23205`: [Bug]: URL-area not rotated in PDFs +* :ghissue:`23268`: [Bug]: hyphen renders different length depending on presence of MathText +* :ghissue:`23308`: [Bug]: set_visible() not working for 3d projection +* :ghissue:`23296`: Set_color method for line2d object in latest document not work +* :ghissue:`22992`: [Bug]: test_image_comparison_expect_rms nondeterministic failure +* :ghissue:`23008`: [ENH]: Use ``\genfrac`` in display style? +* :ghissue:`23214`: [MNT]: Rename examples with "test" in the name +* :ghissue:`17852`: Thin space missing after mathtext operators +* :ghissue:`12078`: Inconsistency in keyword-arguments ncol/ncols, nrow/nrows +* :ghissue:`23239`: [Doc]: steps is not implemented in line styles. +* :ghissue:`23151`: [MNT]: default date limits... +* :ghissue:`9462`: Misaligned bottoms of subplots for png output with bbox_inches='tight' +* :ghissue:`21369`: [Bug]: ax.invert_xaxis() and ax.invert_yaxis() both flip the X axis +* :ghissue:`20797`: ``macosx`` cursors break with images +* :ghissue:`23084`: [TST] Upcoming dependency test failures +* :ghissue:`22910`: [Bug]: bar_label fails with nan errorbar values +* :ghissue:`23074`: [Bug]: matplotlib crashes if ``_tkinter`` doesn't have ``__file__`` +* :ghissue:`23083`: [Bug]: Confusing error messages +* :ghissue:`22391`: [Doc]: Remove "keywords" line at the bottom of all examples +* :ghissue:`20202`: Daylocator causes frozen computer when used with FuncAnimation +* :ghissue:`22529`: Replace C++ quad contouring code with use of ContourPy +* :ghissue:`21710`: [ENH]: macosx backend does not respect rcParams["savefig.directory"] +* :ghissue:`21880`: [Doc]: rrulewrapper not included in API docs +* :ghissue:`22622`: [Bug]: Gaps and overlapping areas between bins when using float16 +* :ghissue:`23043`: [TST] Upcoming dependency test failures +* :ghissue:`17960`: Line2D object markers are lost when retrieved from legend.get_lines() when linestyle='None' +* :ghissue:`23026`: [MNT]: Require that matplotlibrc/style files use utf-8 (or have an encoding cookie) +* :ghissue:`22947`: [Bug]: Can't use ``plt.sca()`` on axes created using subfigures +* :ghissue:`22623`: [ENH]: support rect with constrained_layout ("layout only to part of the figure") +* :ghissue:`22917`: "ab;cd" missing in subplot_mosaic tutorial +* :ghissue:`22686`: [Bug]: can not give init value for RangeSlider widget +* :ghissue:`22740`: [MNT]: Add codespell to pre-commit hooks +* :ghissue:`22893`: rainbow text example is broken +* :ghissue:`21571`: [Doc]: Clarify text positioning +* :ghissue:`22092`: [Bug]: Configure subplots dialog freezes for TkAgg with toolmanager +* :ghissue:`22760`: [Bug]: Macosx legend picker doesn't work anymore +* :ghissue:`16369`: Call to input blocks slider input on osx with the default agg 'MacOSX'. It works fine on when TkAgg is used. +* :ghissue:`22915`: [Bug]: figure.raise_window rcParam does not work on MacOSX backend +* :ghissue:`22930`: [Bug]: Regression in dashes due to #22569 +* :ghissue:`22859`: [Bug]: findSystemFonts should not look in subdirectories of C:\Windows\Fonts\ +* :ghissue:`22882`: Missing ``f`` prefix on f-strings +* :ghissue:`22738`: [MNT]: make Axes.cla an alias for Axes.clear in all cases +* :ghissue:`22708`: [TST] Upcoming dependency test failures +* :ghissue:`8388`: Proposed ENH: Allow user to turn off breaking of streamlines in streamplot +* :ghissue:`20755`: [Bug]: make_norm_from_scale should create picklable classes even when used in-line. +* :ghissue:`18249`: Expand the explanation of the Object-Oriented interface +* :ghissue:`22792`: [Bug]: .eps greyscale hatching of patches when lw=0 +* :ghissue:`22630`: [ENH]: enable passing of projection keyword to Axes.inset_axes +* :ghissue:`22414`: [Bug]: bar_label overlaps bars when y-axis is inverted +* :ghissue:`22726`: [Bug]: tripcolor ignores clim +* :ghissue:`21635`: [ENH]: Add a nightly wheel build +* :ghissue:`9994`: document where nightly wheels are published +* :ghissue:`22350`: [Bug]: text.usetex Vs. DateFormatter +* :ghissue:`4976`: missing imshow() subplots when using tight_layout() +* :ghissue:`22150`: [ENH]: Tool icons are hardly visible in Tk when using a dark theme +* :ghissue:`22662`: Leave color parameter empty should be fine[ENH]: +* :ghissue:`22671`: [Doc]: plot_format adaption invalidates sphinx cache +* :ghissue:`22582`: [Bug]: FontManager.addfont doesn't accept pathlib.Path of TTF font +* :ghissue:`22657`: [ENH]: vector map +* :ghissue:`16181`: The great API cleanup +* :ghissue:`22636`: [Bug]: Infinite loop when there is single double quote in matplotlibrc +* :ghissue:`22266`: [Doc]: Improve examples in documentation +* :ghissue:`11861`: Figure does not close until script finishes execution +* :ghissue:`19288`: Escape # character in matplotlibrc +* :ghissue:`22579`: [Bug]: Replacement for epoch2num behaves differently (does not accept arrays) +* :ghissue:`22605`: [Bug]: Tool contrast low with dark theme on macosx backend +* :ghissue:`17642`: bring osx backend flush_events to feature parity with other backend +* :ghissue:`19268`: Drawing the canvas does not populate ticklabels on MacOSX backend +* :ghissue:`17445`: MacOSX does not render frames in which new artists are added when blitting +* :ghissue:`10980`: Current versions cannot reproduce rotate_axes_3d_demo.py +* :ghissue:`18451`: MacOSX backend fails with animation in certain scripts +* :ghissue:`22603`: [MNT]: Replace str(n)cpy etc with safe versions (C++) +* :ghissue:`19121`: Handle and label not created for Text with label +* :ghissue:`22563`: [Doc]: annotation_clip=None not correctly documented +* :ghissue:`12528`: Empty axes on draw after blitted animation finishes +* :ghissue:`20991`: [Bug]: Error when using path effect with a PolyCollection +* :ghissue:`19563`: path_effects kwarg triggers exception on 3D scatterplot +* :ghissue:`8650`: System Error in backend_agg. (with a fix!) +* :ghissue:`20294`: ``AxesImage.__str__`` is wrong if the image does not span the full Axes. +* :ghissue:`18066`: Document minimum supported OSX version for macos backend +* :ghissue:`17018`: Add documentation about transparency of frame +* :ghissue:`22403`: [MNT]: Confusing prompt in docs issue template +* :ghissue:`8839`: mpl_connect silently does nothing when passed an invalid event type string +* :ghissue:`22343`: [MNT]: Delay (or make pending) the deprecation of set_constrained_layout/set_tight_layout +* :ghissue:`21554`: [Bug]: ``ValueError`` upon deepcopy of a ``Figure`` object +* :ghissue:`22369`: [Doc]: Incorrect comment in example code for creating adjacent subplots +* :ghissue:`19174`: connectionstyle arc3 with high rad value pushes up data interval of x-axis and y-axis. +* :ghissue:`8351`: seaborn styles make "+", "x" markers invisible; proposed workaround for shipped styles +* :ghissue:`22278`: Deprecate/remove maxdict +* :ghissue:`19276`: imshow with very large arrays not working as expected +* :ghissue:`22035`: [ENH]: Specify a custom focal length / FOV for the 3d camera +* :ghissue:`22264`: [Bug]: new constrained_layout causes axes to go invisible(?) +* :ghissue:`21774`: [MNT]: Improvements to widget tests +* :ghissue:`18722`: Consider removing AFM+mathtext support +* :ghissue:`21540`: [Bug]: cm fontset in log scale does not use Unicode minus +* :ghissue:`22062`: [Bug]: Autopositioned title overlaps with offset text +* :ghissue:`22093`: [Bug]: AttributeError: 'AxesSubplot' object has no attribute 'add_text' +* :ghissue:`22012`: [Bug]: Mouseover coordinate/value text should be right aligned +* :ghissue:`21995`: [Bug]: triplot with 'ls' argument yields TypeError +* :ghissue:`20249`: MatplotlibDeprecationWarning when updating rcparams +* :ghissue:`15781`: MatplotlibDeprecationWarning examples.directory is deprecated +* :ghissue:`13118`: No MatplotlibDeprecationWarning for default rcParams +* :ghissue:`21978`: Remove logging debug of loaded modules +* :ghissue:`11738`: pgf backend doesn't make background transparent +* :ghissue:`18039`: Add ``_repr_html_`` for fonts +* :ghissue:`21970`: [Bug]: tight layout breaks with toolbar.push_current() +* :ghissue:`14850`: No icon showing up with macosx backend +* :ghissue:`17283`: Create Date Formatter/Locator Reference +* :ghissue:`21761`: [Doc]: add how to know available fonts... +* :ghissue:`21863`: [Doc]: Remove example "prop_cycle property markevery in rcParams" +* :ghissue:`10241`: Axes3D.view_init elevation issue between 270 and 360 degrees +* :ghissue:`14453`: add third angle to view_init() +* :ghissue:`20486`: Modifier key press events not recognized on MacOSX backend +* :ghissue:`9837`: MacOS: Key modifiers deprecated +* :ghissue:`11416`: RuntimeError: adjustable='datalim' is not allowed when both axes are shared. +* :ghissue:`17711`: inset_locator.mark_inset() misplaces box connectors +* :ghissue:`20854`: [Doc]: Incorrect copyright start year at the bottom of devdocs page +* :ghissue:`21394`: [Bug]: Subplot title does not obey padding +* :ghissue:`20998`: [Bug]: ToolManager does not respect rcParams["keymap."] set after import time +* :ghissue:`7075`: Superscripts in axis label cut when saving .eps with bbox_inches="tight" +* :ghissue:`21514`: [Doc]: Error message of validate_whiskers is not updated +* :ghissue:`21532`: [Doc]: subplot_mosaic docstring should link to the tutorial +* :ghissue:`16550`: Docs: performance discussion of tight_layout +* :ghissue:`21378`: [ENH]: use new style format strings for colorbar ticks +* :ghissue:`19323`: Streamplot color mapping fails on (near-)empty array. +* :ghissue:`19559`: Axes.get_xticks() returns a numpy array but Axes.get_xticks(minor=True) returns a plain list +* :ghissue:`21526`: [Doc]: Little Typo on Introductory Tutorial +* :ghissue:`19195`: Rotate Markers in functions like plot, scatter, etcetera +* :ghissue:`21364`: [Bug]: double free when FT2Font constructor is interrupted by KeyboardInterrupt +* :ghissue:`16581`: Can't not refresh new font in running interpreter +* :ghissue:`21162`: [ENH]: saving images in webp format +* :ghissue:`18168`: The example of the testing decorator does not work. +* :ghissue:`20943`: [Bug]: Deepcopy of TextPath fails +* :ghissue:`21101`: [Bug]: Errorbars separated from markers with negative errors +* :ghissue:`17986`: MEP22 per-backend tool registration +* :ghissue:`4938`: Feature request: add option to disable mathtext parsing +* :ghissue:`11435`: plt.subplot eats my subplots diff --git a/doc/users/prev_whats_new/github_stats_3.6.1.rst b/doc/users/prev_whats_new/github_stats_3.6.1.rst new file mode 100644 index 000000000000..d47dc28fa076 --- /dev/null +++ b/doc/users/prev_whats_new/github_stats_3.6.1.rst @@ -0,0 +1,143 @@ +.. _github-stats-3-6-1: + +GitHub statistics for 3.6.1 (Oct 08, 2022) +========================================== + +GitHub statistics for 2022/09/16 (tag: v3.6.0) - 2022/10/08 + +These lists are automatically generated, and may be incomplete or contain duplicates. + +We closed 22 issues and merged 80 pull requests. +The full list can be seen `on GitHub `__ + +The following 19 authors contributed 129 commits. + +* Antony Lee +* baharev +* David Stansby +* dependabot[bot] +* Eli Rykoff +* Elliott Sales de Andrade +* erykoff +* Greg Lucas +* hannah +* Ian Hunt-Isaak +* Jody Klymak +* melissawm +* Oscar Gustafsson +* Ruth Comer +* slackline +* Steffen Rehberg +* Thomas A Caswell +* Tim Hoffmann +* مهدي شينون (Mehdi Chinoune) + +GitHub issues and pull requests: + +Pull Requests (80): + +* :ghpull:`24124`: Backport PR #24111 on branch v3.6.x (FIX: add missing method to ColormapRegistry) +* :ghpull:`24111`: FIX: add missing method to ColormapRegistry +* :ghpull:`24117`: Backport PR #24113 on branch v3.6.x (Add exception class to pytest.warns calls) +* :ghpull:`24116`: Backport PR #24115 on branch v3.6.x (Fix mask lookup in fill_between for NumPy 1.24+) +* :ghpull:`24113`: Add exception class to pytest.warns calls +* :ghpull:`24115`: Fix mask lookup in fill_between for NumPy 1.24+ +* :ghpull:`24112`: Backport PR #24109 on branch v3.6.x (DOC: add API change note for colorbar deprecation) +* :ghpull:`24109`: DOC: add API change note for colorbar deprecation +* :ghpull:`24107`: Backport PR #24088 on branch v3.6.x (MNT: make orphaned colorbar deprecate versus raise) +* :ghpull:`24088`: MNT: make orphaned colorbar deprecate versus raise +* :ghpull:`24103`: Backport PR #23684 on branch v3.6.x (Fix rectangle and hatches for colorbar) +* :ghpull:`23684`: Fix rectangle and hatches for colorbar +* :ghpull:`24087`: Backport PR #24084 on branch v3.6.x (Revert argument checking for label_mode) +* :ghpull:`24084`: Revert argument checking for label_mode +* :ghpull:`24078`: Backport PR #24047 on branch v3.6.x (Revert #22360: Let TeX handle multiline strings itself) +* :ghpull:`24047`: Revert #22360: Let TeX handle multiline strings itself +* :ghpull:`24077`: Backport PR #24054 on branch v3.6.x ( DOC: Move OO-examples from pyplot section) +* :ghpull:`24054`: DOC: Move OO-examples from pyplot section +* :ghpull:`24072`: Backport PR #24069 on branch v3.6.x (Clarification of marker size in scatter) +* :ghpull:`24073`: Backport PR #24070 on branch v3.6.x (DOC: colorbar may steal from array of axes) +* :ghpull:`24070`: DOC: colorbar may steal from array of axes +* :ghpull:`24069`: Clarification of marker size in scatter +* :ghpull:`24059`: Backport PR #23638 on branch v3.6.x (FIX: correctly handle generic font families in svg text-as-text mode) +* :ghpull:`23638`: FIX: correctly handle generic font families in svg text-as-text mode +* :ghpull:`24048`: Backport PR #24045 on branch v3.6.x (Fix _FigureManagerGTK.resize on GTK4) +* :ghpull:`24055`: Backport PR #24046 on branch v3.6.x (Ignore 'CFMessagePort: bootstrap_register' messages) +* :ghpull:`24046`: Ignore 'CFMessagePort: bootstrap_register' messages +* :ghpull:`24051`: Backport PR #24037 on branch v3.6.x ([DOC]: make spanselector example codeblock continuous) +* :ghpull:`24037`: [DOC]: make spanselector example codeblock continuous +* :ghpull:`24045`: Fix _FigureManagerGTK.resize on GTK4 +* :ghpull:`24043`: Backport PR #24041 on branch v3.6.x (DOC: Fix incorrect redirect) +* :ghpull:`24030`: Backport PR #24019 on branch v3.6.x (Don't require FigureCanvas on backend module more) +* :ghpull:`24040`: Backport PR #24018 on branch v3.6.x (When comparing eps images, run ghostscript with -dEPSCrop.) +* :ghpull:`24018`: When comparing eps images, run ghostscript with -dEPSCrop. +* :ghpull:`24033`: Backport PR #24032 on branch v3.6.x (Reword SpanSelector example.) +* :ghpull:`24029`: Backport PR #24026 on branch v3.6.x (Don't modify Axes property cycle in stackplot) +* :ghpull:`23994`: Backport PR #23964 on branch v3.6.x (Fix issue with empty line in ps backend) +* :ghpull:`24019`: Don't require FigureCanvas on backend module more +* :ghpull:`24026`: Don't modify Axes property cycle in stackplot +* :ghpull:`24027`: Backport PR #23904 on branch v3.6.x (added a reversing section to colormap reference) +* :ghpull:`24017`: Backport PR #24014 on branch v3.6.x (Bump pypa/cibuildwheel from 2.10.1 to 2.10.2) +* :ghpull:`24014`: Bump pypa/cibuildwheel from 2.10.1 to 2.10.2 +* :ghpull:`24007`: Backport PR #24004 on branch v3.6.x (Increase consistency in tutorials and examples) +* :ghpull:`23964`: Fix issue with empty line in ps backend +* :ghpull:`23904`: added a reversing section to colormap reference +* :ghpull:`23990`: Backport PR #23978 on branch v3.6.x (DOC: Suppress IPython output in examples and tutorials where not needed) +* :ghpull:`23978`: DOC: Suppress IPython output in examples and tutorials where not needed +* :ghpull:`23916`: Backport PR #23912 on branch v3.6.x (FIX: only expect FigureCanvas on backend module if using new style) +* :ghpull:`23989`: Backport PR #23944 on branch v3.6.x (FIX: ValueError when hexbin is run with empty arrays and log scaling.) +* :ghpull:`23944`: FIX: ValueError when hexbin is run with empty arrays and log scaling. +* :ghpull:`23988`: Backport PR #23987 on branch v3.6.x (FIX: do not set constrained layout on false-y values) +* :ghpull:`23987`: FIX: do not set constrained layout on false-y values +* :ghpull:`23982`: Backport PR #23980 on branch v3.6.x (DOC: Move Quick Start Tutorial to first position) +* :ghpull:`23979`: Backport PR #23975 on branch v3.6.x (Reword docstring of reset_position.) +* :ghpull:`23975`: Reword docstring of reset_position. +* :ghpull:`23966`: Backport PR #23930 on branch v3.6.x (Fix edge color, links, wording; closes matplotlib/matplotlib#23895) +* :ghpull:`23971`: Backport PR #23906 on branch v3.6.x (Edit mplot3d examples for correctness and consistency) +* :ghpull:`23906`: Edit mplot3d examples for correctness and consistency +* :ghpull:`23963`: Backport PR #23957 on branch v3.6.x (Bump pypa/cibuildwheel from 2.9.0 to 2.10.1) +* :ghpull:`23930`: Fix edge color, links, wording; closes matplotlib/matplotlib#23895 +* :ghpull:`23910`: FIX: do not append None to stream in ps +* :ghpull:`23957`: Bump pypa/cibuildwheel from 2.9.0 to 2.10.1 +* :ghpull:`23960`: Backport PR #23947 on branch v3.6.x (Fix building on MINGW) +* :ghpull:`23942`: DOC: fix versions in v3.6.x doc switcher +* :ghpull:`23961`: Backport PR #23958 on branch v3.6.x (DOC: Remove Adding Animations section) +* :ghpull:`23958`: DOC: Remove Adding Animations section +* :ghpull:`23947`: Fix building on MINGW +* :ghpull:`23945`: Backport PR #23941 on branch v3.6.x (consistent notation for minor/patch branches) +* :ghpull:`23956`: Backport PR #23751 on branch v3.6.x (FIX: show bars when the first location is nan) +* :ghpull:`23751`: FIX: show bars when the first location is nan +* :ghpull:`23938`: Backport PR #23919 on branch v3.6.x (DOC: remove dead "Show Source" links) +* :ghpull:`23952`: Backport PR #23951 on branch v3.6.x (DOC: Make animation continuous) +* :ghpull:`23949`: DOC: Display "dev" instead of "devdocs" in the version switcher +* :ghpull:`23940`: Fix typos in github_stats.rst +* :ghpull:`23936`: Backport PR #23935 on branch v3.6.x (DOC: fix versions is doc switcher) +* :ghpull:`23933`: Backport PR #23932 on branch v3.6.x (DOC: Fix formatting in image tutorial) +* :ghpull:`23932`: DOC: Fix formatting in image tutorial +* :ghpull:`23926`: Backport PR #23925 on branch v3.6.x (FIX: use process_event in dpi changes on macosx backend) +* :ghpull:`23925`: FIX: use process_event in dpi changes on macosx backend +* :ghpull:`23912`: FIX: only expect FigureCanvas on backend module if using new style + +Issues (22): + +* :ghissue:`23981`: [ENH]: Default ``matplotlib.colormaps[None]`` to call ``matplotlib.colormaps[matplotlib.rcParams['image.cmap']]``? +* :ghissue:`24106`: [Bug]: fill_between gives IndexError with numpy 1.24.0.dev +* :ghissue:`24053`: Cartopy axes_grid_basic example broken by Matplotlib 3.6 +* :ghissue:`23977`: [Bug]: Eqnarray in AnchoredText results in misplaced text (new in v3.6.0) +* :ghissue:`23973`: [Bug]: ValueError: Unable to determine Axes to steal space for Colorbar. +* :ghissue:`23456`: [Bug]: Horizontal colorbars drawn incorrectly with hatches +* :ghissue:`15922`: Pyplot gallery section is mostly OO examples +* :ghissue:`23700`: [Doc]: scatter points +* :ghissue:`23492`: [Bug]: svg backend does not use configured generic family lists +* :ghissue:`22528`: [Bug]: problem with font property in text elements of svg figures +* :ghissue:`23911`: [Bug]: 3.6.0 doesn't interact well with pycharm throwing "backend_interagg" exception +* :ghissue:`24024`: stackplot should not change Axes cycler +* :ghissue:`23954`: [Bug]: Text label with empty line causes a "TypeError: cannot unpack non-iterable NoneType object" in PostScript backend +* :ghissue:`23922`: [Bug]: Refactor of hexbin for 3.6.0 crashes with empty arrays and log scaling +* :ghissue:`23986`: [Bug]: Constrained layout UserWarning even when False +* :ghissue:`23895`: [Bug]: 3D surface is not plotted for the contour3d_3 example in the gallery +* :ghissue:`23955`: [Doc]: Adding animations to Youtube channel +* :ghissue:`23943`: [Bug]: Couldn't build matplotlib 3.6.0 with both Clang-15 and GCC-12 +* :ghissue:`23687`: [Bug]: barplot does not show anything when x or bottom start and end with NaN +* :ghissue:`23876`: [Doc]: Missing source files +* :ghissue:`23909`: [Doc]: add animation examples to show animated subplots +* :ghissue:`23921`: [Bug]: resize_event deprecation warnings when creating figure on macOS with version 3.6.0 diff --git a/doc/users/prev_whats_new/whats_new_0.98.4.rst b/doc/users/prev_whats_new/whats_new_0.98.4.rst index 96f8768a99d8..88d376cf79bf 100644 --- a/doc/users/prev_whats_new/whats_new_0.98.4.rst +++ b/doc/users/prev_whats_new/whats_new_0.98.4.rst @@ -1,7 +1,7 @@ .. _whats-new-0-98-4: -New in matplotlib 0.98.4 -======================== +What's new in Matplotlib 0.98.4 +=============================== .. contents:: Table of Contents :depth: 2 @@ -30,12 +30,18 @@ multiple columns and rows, as well as fancy box drawing. See :func:`~matplotlib.pyplot.legend` and :class:`matplotlib.legend.Legend`. -.. figure:: ../../gallery/pyplots/images/sphx_glr_whats_new_98_4_legend_001.png - :target: ../../gallery/pyplots/whats_new_98_4_legend.html - :align: center - :scale: 50 +.. plot:: + + ax = plt.subplot() + t1 = np.arange(0.0, 1.0, 0.01) + for n in [1, 2, 3, 4]: + plt.plot(t1, t1**n, label=f"n={n}") + + leg = plt.legend(loc='best', ncol=2, mode="expand", shadow=True, fancybox=True) + leg.get_frame().set_alpha(0.5) + + plt.show() - What's New 98 4 Legend .. _fancy-annotations: @@ -145,12 +151,20 @@ can pass an *x* array and a *ylower* and *yupper* array to fill between, and an optional *where* argument which is a logical mask where you want to do the filling. -.. figure:: ../../gallery/pyplots/images/sphx_glr_whats_new_98_4_fill_between_001.png - :target: ../../gallery/pyplots/whats_new_98_4_fill_between.html - :align: center - :scale: 50 +.. plot:: + + x = np.arange(-5, 5, 0.01) + y1 = -5*x*x + x + 10 + y2 = 5*x*x + x + + fig, ax = plt.subplots() + ax.plot(x, y1, x, y2, color='black') + ax.fill_between(x, y1, y2, where=(y2 > y1), facecolor='yellow', alpha=0.5) + ax.fill_between(x, y1, y2, where=(y2 <= y1), facecolor='red', alpha=0.5) + ax.set_title('Fill Between') + + plt.show() - What's New 98 4 Fill Between Lots more --------- diff --git a/doc/users/prev_whats_new/whats_new_0.99.rst b/doc/users/prev_whats_new/whats_new_0.99.rst index 2a74e39e6b55..c2d761a25031 100644 --- a/doc/users/prev_whats_new/whats_new_0.99.rst +++ b/doc/users/prev_whats_new/whats_new_0.99.rst @@ -1,7 +1,7 @@ .. _whats-new-0-99: -New in matplotlib 0.99 -====================== +What's new in Matplotlib 0.99 (Aug 29, 2009) +============================================ .. contents:: Table of Contents :depth: 2 @@ -22,19 +22,29 @@ working with paths and transformations: :doc:`/tutorials/advanced/path_tutorial` mplot3d -------- - Reinier Heeres has ported John Porter's mplot3d over to the new matplotlib transformations framework, and it is now available as a toolkit mpl_toolkits.mplot3d (which now comes standard with all mpl installs). See :ref:`mplot3d-examples-index` and -:ref:`toolkit_mplot3d-tutorial` +:doc:`/tutorials/toolkits/mplot3d`. + +.. plot:: + + from matplotlib import cm + from mpl_toolkits.mplot3d import Axes3D + + X = np.arange(-5, 5, 0.25) + Y = np.arange(-5, 5, 0.25) + X, Y = np.meshgrid(X, Y) + R = np.sqrt(X**2 + Y**2) + Z = np.sin(R) -.. figure:: ../../gallery/pyplots/images/sphx_glr_whats_new_99_mplot3d_001.png - :target: ../../gallery/pyplots/whats_new_99_mplot3d.html - :align: center - :scale: 50 + fig = plt.figure() + ax = Axes3D(fig, auto_add_to_figure=False) + fig.add_axes(ax) + ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.viridis) - What's New 99 Mplot3d + plt.show() .. _whats-new-axes-grid: @@ -48,12 +58,49 @@ new mpl installs. See :ref:`axes_grid1-examples-index`, :ref:`axisartist-examples-index`, :ref:`axes_grid1_users-guide-index` and :ref:`axisartist_users-guide-index` -.. figure:: ../../gallery/pyplots/images/sphx_glr_whats_new_99_axes_grid_001.png - :target: ../../gallery/pyplots/whats_new_99_axes_grid.html - :align: center - :scale: 50 +.. plot:: + + from mpl_toolkits.axes_grid1.axes_rgb import RGBAxes + + + def get_demo_image(): + # prepare image + delta = 0.5 + + extent = (-3, 4, -4, 3) + 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 = (Z1 - Z2) * 2 + + return Z, extent + + + def get_rgb(): + Z, extent = get_demo_image() + + Z[Z < 0] = 0. + Z = Z / Z.max() + + R = Z[:13, :13] + G = Z[2:, 2:] + B = Z[:13, 2:] + + return R, G, B - What's New 99 Axes Grid + + fig = plt.figure() + ax = RGBAxes(fig, [0.1, 0.1, 0.8, 0.8]) + + r, g, b = get_rgb() + ax.imshow_rgb(r, g, b, origin="lower") + + ax.RGB.set_xlim(0., 9.5) + ax.RGB.set_ylim(0.9, 10.6) + + plt.show() .. _whats-new-spine: @@ -65,12 +112,50 @@ that denote the data limits -- in various arbitrary locations. No longer are your axis lines constrained to be a simple rectangle around the figure -- you can turn on or off left, bottom, right and top, as well as "detach" the spine to offset it away from the data. See -:doc:`/gallery/ticks_and_spines/spine_placement_demo` and +:doc:`/gallery/spines/spine_placement_demo` and :class:`matplotlib.spines.Spine`. -.. figure:: ../../gallery/pyplots/images/sphx_glr_whats_new_99_spines_001.png - :target: ../../gallery/pyplots/whats_new_99_spines.html - :align: center - :scale: 50 +.. plot:: + + def adjust_spines(ax, spines): + for loc, spine in ax.spines.items(): + if loc in spines: + spine.set_position(('outward', 10)) # outward by 10 points + else: + spine.set_color('none') # don't draw spine + + # turn off ticks where there is no spine + if 'left' in spines: + ax.yaxis.set_ticks_position('left') + else: + # no yaxis ticks + ax.yaxis.set_ticks([]) + + if 'bottom' in spines: + ax.xaxis.set_ticks_position('bottom') + else: + # no xaxis ticks + ax.xaxis.set_ticks([]) + + fig = plt.figure() + + x = np.linspace(0, 2*np.pi, 100) + y = 2*np.sin(x) + + ax = fig.add_subplot(2, 2, 1) + ax.plot(x, y) + adjust_spines(ax, ['left']) + + ax = fig.add_subplot(2, 2, 2) + ax.plot(x, y) + adjust_spines(ax, []) + + ax = fig.add_subplot(2, 2, 3) + ax.plot(x, y) + adjust_spines(ax, ['left', 'bottom']) + + ax = fig.add_subplot(2, 2, 4) + ax.plot(x, y) + adjust_spines(ax, ['bottom']) - What's New 99 Spines + plt.show() diff --git a/doc/users/prev_whats_new/whats_new_1.0.rst b/doc/users/prev_whats_new/whats_new_1.0.rst index cafa8917d518..ab902977cb1e 100644 --- a/doc/users/prev_whats_new/whats_new_1.0.rst +++ b/doc/users/prev_whats_new/whats_new_1.0.rst @@ -1,7 +1,7 @@ .. _whats-new-1-0: -New in matplotlib 1.0 -===================== +What's new in Matplotlib 1.0 (Jul 06, 2010) +=========================================== .. contents:: Table of Contents :depth: 2 @@ -12,7 +12,7 @@ HTML5/Canvas backend -------------------- Simon Ratcliffe and Ludwig Schwardt have released an `HTML5/Canvas -`__ backend for matplotlib. The +`__ backend for matplotlib. The backend is almost feature complete, and they have done a lot of work comparing their html5 rendered images with our core renderer Agg. The backend features client/server interactive navigation of matplotlib @@ -23,15 +23,14 @@ Sophisticated subplot grid layout Jae-Joon Lee has written :mod:`~matplotlib.gridspec`, a new module for doing complex subplot layouts, featuring row and column spans and -more. See :doc:`/tutorials/intermediate/gridspec` for a tutorial overview. +more. See :doc:`/tutorials/intermediate/arranging_axes` for a tutorial +overview. .. figure:: ../../gallery/userdemo/images/sphx_glr_demo_gridspec01_001.png :target: ../../gallery/userdemo/demo_gridspec01.html :align: center :scale: 50 - Demo Gridspec01 - Easy pythonic subplots ----------------------- @@ -44,9 +43,9 @@ indexing (starts with 0). e.g.:: fig, axarr = plt.subplots(2, 2) axarr[0,0].plot([1,2,3]) # upper, left -See :doc:`/gallery/subplots_axes_and_figures/subplot_demo` for several code examples. +See :doc:`/gallery/subplots_axes_and_figures/subplot` for several code examples. -Contour fixes and and triplot +Contour fixes and triplot ----------------------------- Ian Thomas has fixed a long-standing bug that has vexed our most @@ -63,8 +62,6 @@ plotting unstructured triangular grids. :align: center :scale: 50 - Triplot Demo - multiple calls to show supported -------------------------------- @@ -92,12 +89,29 @@ supporting mixing of 2D and 3D graphs in the same figure, and/or multiple 3D graphs in a single figure, using the "projection" keyword argument to add_axes or add_subplot. Thanks Ben Root. -.. figure:: ../../gallery/pyplots/images/sphx_glr_whats_new_1_subplot3d_001.png - :target: ../../gallery/pyplots/whats_new_1_subplot3d.html - :align: center - :scale: 50 +.. plot:: + + from mpl_toolkits.mplot3d.axes3d import get_test_data + + fig = plt.figure() + + X = np.arange(-5, 5, 0.25) + Y = np.arange(-5, 5, 0.25) + X, Y = np.meshgrid(X, Y) + R = np.sqrt(X**2 + Y**2) + Z = np.sin(R) + ax = fig.add_subplot(1, 2, 1, projection='3d') + surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='viridis', + linewidth=0, antialiased=False) + ax.set_zlim3d(-1.01, 1.01) + + fig.colorbar(surf, shrink=0.5, aspect=5) + + X, Y, Z = get_test_data(0.05) + ax = fig.add_subplot(1, 2, 2, projection='3d') + ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10) - What's New 1 Subplot3d + plt.show() tick_params ----------- diff --git a/doc/users/prev_whats_new/whats_new_1.1.rst b/doc/users/prev_whats_new/whats_new_1.1.rst index f4c9cd42d31d..901351466fac 100644 --- a/doc/users/prev_whats_new/whats_new_1.1.rst +++ b/doc/users/prev_whats_new/whats_new_1.1.rst @@ -1,7 +1,7 @@ .. _whats-new-1-1: -New in matplotlib 1.1 -===================== +What's new in Matplotlib 1.1 (Nov 02, 2011) +=========================================== .. contents:: Table of Contents :depth: 2 @@ -26,9 +26,6 @@ Kevin Davies has extended Yannick Copin's original Sankey example into a module :align: center :scale: 50 - Sankey Rankine - - Animation --------- @@ -42,7 +39,7 @@ pendulum ` which uses .. raw:: html - + This should be considered as a beta release of the framework; please try it and provide feedback. @@ -54,7 +51,7 @@ Tight Layout A frequent issue raised by users of matplotlib is the lack of a layout engine to nicely space out elements of the plots. While matplotlib still adheres to the philosophy of giving users complete control over the placement -of plot elements, Jae-Joon Lee created the :mod:`~matplotlib.tight_layout` +of plot elements, Jae-Joon Lee created the ``matplotlib.tight_layout`` module and introduced a new command :func:`~matplotlib.pyplot.tight_layout` to address the most common layout issues. @@ -125,8 +122,6 @@ examples. :align: center :scale: 50 - Legend Demo4 - mplot3d ------- @@ -138,7 +133,7 @@ as 2D plotting, Ben Root has made several improvements to the improved to bring the class towards feature-parity with regular Axes objects -* Documentation for :ref:`toolkit_mplot3d-tutorial` was significantly expanded +* Documentation for :doc:`/tutorials/toolkits/mplot3d` was significantly expanded * Axis labels and orientation improved @@ -151,8 +146,6 @@ as 2D plotting, Ben Root has made several improvements to the :align: center :scale: 50 - Offset - * :meth:`~mpl_toolkits.mplot3d.axes3d.Axes3D.contourf` gains *zdir* and *offset* kwargs. You can now do this: @@ -161,8 +154,6 @@ as 2D plotting, Ben Root has made several improvements to the :align: center :scale: 50 - Contourf3d 2 - Numerix support removed ----------------------- diff --git a/doc/users/prev_whats_new/whats_new_1.2.2.rst b/doc/users/prev_whats_new/whats_new_1.2.2.rst index 51c43403d22c..ab81018925cd 100644 --- a/doc/users/prev_whats_new/whats_new_1.2.2.rst +++ b/doc/users/prev_whats_new/whats_new_1.2.2.rst @@ -1,7 +1,7 @@ .. _whats-new-1-2-2: -New in matplotlib 1.2.2 -======================= +What's new in Matplotlib 1.2.2 +============================== .. contents:: Table of Contents :depth: 2 diff --git a/doc/users/prev_whats_new/whats_new_1.2.rst b/doc/users/prev_whats_new/whats_new_1.2.rst index fc6039314b9f..3bfa20e671be 100644 --- a/doc/users/prev_whats_new/whats_new_1.2.rst +++ b/doc/users/prev_whats_new/whats_new_1.2.rst @@ -1,8 +1,8 @@ .. _whats-new-1-2: -New in matplotlib 1.2 -===================== +What's new in Matplotlib 1.2 (Nov 9, 2012) +========================================== .. contents:: Table of Contents :depth: 2 @@ -67,8 +67,6 @@ Damon McDougall added a new plotting method for the :align: center :scale: 50 - Trisurf3d - Control the lengths of colorbar extensions ------------------------------------------ @@ -133,9 +131,6 @@ median and confidence interval. :align: center :scale: 50 - Boxplot Demo3 - - New RC parameter functionality ------------------------------ @@ -168,9 +163,6 @@ local intensity of the vector field. :align: center :scale: 50 - Plot Streamplot - - New hist functionality ---------------------- @@ -187,7 +179,7 @@ Updated shipped dependencies The following dependencies that ship with matplotlib and are optionally installed alongside it have been updated: -- `pytz `_ 2012d +- `pytz `_ 2012d - `dateutil `_ 1.5 on Python 2.x, and 2.1 on Python 3.x @@ -204,8 +196,6 @@ a triangulation. :align: center :scale: 50 - Tripcolor Demo - Hatching patterns in filled contour plots, with legends ------------------------------------------------------- @@ -218,8 +208,6 @@ to use a legend to identify contoured ranges. :align: center :scale: 50 - Contourf Hatching - Known issues in the matplotlib 1.2 release ------------------------------------------ diff --git a/doc/users/prev_whats_new/whats_new_1.3.rst b/doc/users/prev_whats_new/whats_new_1.3.rst index ddc35661dc74..855235069917 100644 --- a/doc/users/prev_whats_new/whats_new_1.3.rst +++ b/doc/users/prev_whats_new/whats_new_1.3.rst @@ -1,7 +1,7 @@ .. _whats-new-1-3: -New in matplotlib 1.3 -===================== +What's new in Matplotlib 1.3 (Aug 01, 2013) +=========================================== .. contents:: Table of Contents :depth: 2 @@ -96,22 +96,18 @@ to modify each artist's sketch parameters individually with :align: center :scale: 50 - xkcd - Updated Axes3D.contour methods ------------------------------ Damon McDougall updated the :meth:`~mpl_toolkits.mplot3d.axes3d.Axes3D.tricontour` and :meth:`~mpl_toolkits.mplot3d.axes3d.Axes3D.tricontourf` methods to allow 3D -contour plots on abitrary unstructured user-specified triangulations. +contour plots on arbitrary unstructured user-specified triangulations. .. figure:: ../../gallery/mplot3d/images/sphx_glr_tricontour3d_001.png :target: ../../gallery/mplot3d/tricontour3d.html :align: center :scale: 50 - Tricontour3d - New eventplot plot type ``````````````````````` Todd Jennings added a :func:`~matplotlib.pyplot.eventplot` function to @@ -122,8 +118,6 @@ create multiple rows or columns of identical line segments :align: center :scale: 50 - Eventplot Demo - As part of this feature, there is a new :class:`~matplotlib.collections.EventCollection` class that allows for plotting and manipulating rows or columns of identical line segments. @@ -146,8 +140,6 @@ added (:class:`~matplotlib.tri.TriAnalyzer`). :align: center :scale: 50 - Tricontour Smooth User - Baselines for stackplot ``````````````````````` Till Stensitzki added non-zero baselines to @@ -159,8 +151,6 @@ weighted. :align: center :scale: 50 - Stackplot Demo2 - Rectangular colorbar extensions ``````````````````````````````` Andrew Dawson added a new keyword argument *extendrect* to @@ -204,8 +194,6 @@ Thanks to Jae-Joon Lee, path effects now also work on plot lines. :align: center :scale: 50 - Patheffect Demo - Easier creation of colormap and normalizer for levels with colors ````````````````````````````````````````````````````````````````` Phil Elson added the :func:`matplotlib.colors.from_levels_and_colors` @@ -299,7 +287,7 @@ hard-coded to default to 0, default value of both rcParam values is 0. Changes to font rcParams ```````````````````````` -The `font.*` rcParams now affect only text objects created after the +The ``font.*`` rcParams now affect only text objects created after the rcParam has been set, and will not retroactively affect already existing text objects. This brings their behavior in line with most other rcParams. @@ -389,7 +377,7 @@ information. XDG base directory support `````````````````````````` On Linux, matplotlib now uses the `XDG base directory specification -`_ to +`_ to find the :file:`matplotlibrc` configuration file. :file:`matplotlibrc` should now be kept in :file:`~/.config/matplotlib`, rather than :file:`~/.matplotlib`. If your configuration is found in the old location, it will still be used, but diff --git a/doc/users/prev_whats_new/whats_new_1.4.rst b/doc/users/prev_whats_new/whats_new_1.4.rst index e9df55993cc2..febcb93047b9 100644 --- a/doc/users/prev_whats_new/whats_new_1.4.rst +++ b/doc/users/prev_whats_new/whats_new_1.4.rst @@ -1,8 +1,8 @@ .. _whats-new-1-4: -New in matplotlib 1.4 -===================== +What's new in Matplotlib 1.4 (Aug 25, 2014) +=========================================== Thomas A. Caswell served as the release manager for the 1.4 release. @@ -20,7 +20,7 @@ New colormap ------------ In heatmaps, a green-to-red spectrum is often used to indicate intensity of activity, but this can be problematic for the red/green colorblind. A new, -colorblind-friendly colormap is now available at :class:`matplotlib.cm.Wistia`. +colorblind-friendly colormap is now available at ``matplotlib.cm.Wistia``. This colormap maintains the red/green symbolism while achieving deuteranopic legibility through brightness variations. See `here `__ @@ -142,7 +142,7 @@ Added the kwarg 'which' to `.Axes.get_xticklabels`, `.Axes.get_yticklabels` and `.Axis.get_ticklabels`. 'which' can be 'major', 'minor', or 'both' select which ticks to return, like -:func:`~matplotlib.Axis.set_ticks_position`. If 'which' is `None` then the old +`~.XAxis.set_ticks_position`. If 'which' is `None` then the old behaviour (controlled by the bool *minor*). Separate horizontal/vertical axes padding support in ImageGrid @@ -165,8 +165,6 @@ specifically the Skew-T used in meteorology. :align: center :scale: 50 - Skewt - Support for specifying properties of wedge and text in pie charts. `````````````````````````````````````````````````````````````````` Added the kwargs 'wedgeprops' and 'textprops' to `~.Axes.pie` @@ -209,9 +207,9 @@ to their liking. This feature was implemented for a software engineering course at the University of Toronto, Scarborough, run in Winter 2014 by Anya Tafliovich. -More `markevery` options to show only a subset of markers +More *markevery* options to show only a subset of markers ````````````````````````````````````````````````````````` -Rohan Walker extended the `markevery` property in +Rohan Walker extended the *markevery* property in :class:`~matplotlib.lines.Line2D`. You can now specify a subset of markers to show with an int, slice object, numpy fancy indexing, or float. Using a float shows markers at approximately equal display-coordinate-distances along the @@ -220,7 +218,7 @@ line. Added size related functions to specialized `.Collection`\s ``````````````````````````````````````````````````````````` -Added the `get_size` and `set_size` functions to control the size of +Added the ``get_size`` and ``set_size`` functions to control the size of elements of specialized collections ( :class:`~matplotlib.collections.AsteriskPolygonCollection` :class:`~matplotlib.collections.BrokenBarHCollection` @@ -234,7 +232,7 @@ elements of specialized collections ( Fixed the mouse coordinates giving the wrong theta value in Polar graph ``````````````````````````````````````````````````````````````````````` Added code to -:func:`~matplotlib.InvertedPolarTransform.transform_non_affine` +`~.polar.InvertedPolarTransform.transform_non_affine` to ensure that the calculated theta value was between the range of 0 and 2 * pi since the problem was that the value can become negative after applying the direction and rotation to the theta calculation. @@ -244,7 +242,7 @@ Simple quiver plot for mplot3d toolkit A team of students in an *Engineering Large Software Systems* course, taught by Prof. Anya Tafliovich at the University of Toronto, implemented a simple version of a quiver plot in 3D space for the mplot3d toolkit as one of their -term project. This feature is documented in :func:`~mpl_toolkits.mplot3d.Axes3D.quiver`. +term project. This feature is documented in `~.Axes3D.quiver`. The team members are: Ryan Steve D'Souza, Victor B, xbtsw, Yang Wang, David, Caradec Bisesar and Vlad Vassilovski. @@ -253,8 +251,6 @@ Caradec Bisesar and Vlad Vassilovski. :align: center :scale: 50 - Quiver3d - polar-plot r-tick locations ``````````````````````````` Added the ability to control the angular position of the r-tick labels diff --git a/doc/users/prev_whats_new/whats_new_1.5.rst b/doc/users/prev_whats_new/whats_new_1.5.rst index 97e4f81fd306..b94355b97b93 100644 --- a/doc/users/prev_whats_new/whats_new_1.5.rst +++ b/doc/users/prev_whats_new/whats_new_1.5.rst @@ -1,7 +1,7 @@ .. _whats-new-1-5: -New in matplotlib 1.5 -===================== +What's new in Matplotlib 1.5 (Oct 29, 2015) +=========================================== .. contents:: Table of Contents :depth: 2 @@ -36,7 +36,7 @@ that the draw command is deferred and only called once. The upshot of this is that for interactive backends (including ``%matplotlib notebook``) in interactive mode (with ``plt.ion()``) -.. code-block :: python +.. code-block:: python import matplotlib.pyplot as plt fig, ax = plt.subplots() @@ -109,9 +109,6 @@ on two or more property cycles. :align: center :scale: 50 - Color Cycle - - New Colormaps ------------- @@ -316,9 +313,6 @@ specified, the default value is taken from rcParams. :align: center :scale: 50 - Contour Corner Mask - - Mostly unified linestyles for `.Line2D`, `.Patch` and `.Collection` ``````````````````````````````````````````````````````````````````` @@ -346,7 +340,7 @@ Added a :mod:`.legend_handler` for :class:`~matplotlib.collections.PolyCollectio Support for alternate pivots in mplot3d quiver plot ``````````````````````````````````````````````````` -Added a :code:`pivot` kwarg to :func:`~mpl_toolkits.mplot3d.Axes3D.quiver` +Added a :code:`pivot` kwarg to `~.Axes3D.quiver` that controls the pivot point around which the quiver line rotates. This also determines the placement of the arrow head along the quiver line. @@ -368,7 +362,7 @@ Add step kwargs to fill_between Added ``step`` kwarg to `.Axes.fill_between` to allow to fill between lines drawn using the 'step' draw style. The values of ``step`` match -those of the ``where`` kwarg of `.Axes.step`. The asymmetry of of the +those of the ``where`` kwarg of `.Axes.step`. The asymmetry of the kwargs names is not ideal, but `.Axes.fill_between` already has a ``where`` kwarg. @@ -379,9 +373,6 @@ This is particularly useful for plotting pre-binned histograms. :align: center :scale: 50 - Filled Step - - Square Plot ``````````` @@ -494,8 +485,7 @@ backends. DateFormatter strftime `````````````````````` -:class:`~matplotlib.dates.DateFormatter`\ 's -:meth:`~matplotlib.dates.DateFormatter.__call__` method will format +:class:`~matplotlib.dates.DateFormatter`\ 's ``__call__`` method will format a :class:`datetime.datetime` object with the format string passed to the formatter's constructor. This method accepts datetimes with years before 1900, unlike :meth:`datetime.datetime.strftime`. @@ -609,12 +599,12 @@ that comes as replacement for `.NavigationToolbar2` with the figures. Before we had the `.NavigationToolbar2` with its own tools like ``zoom/pan/home/save/...`` and also we had the shortcuts like ``yscale/grid/quit/....``. `.ToolManager` relocate all those actions as -`Tools` (located in `~matplotlib.backend_tools`), and defines a way to +Tools (located in `~matplotlib.backend_tools`), and defines a way to access/trigger/reconfigure them. -The `Toolbars` are replaced for `ToolContainers` that are just GUI -interfaces to `trigger` the tools. But don't worry the default -backends include a `ToolContainer` called `toolbar` +The Toolbars are replaced by `.ToolContainerBase`\s that are just GUI +interfaces to trigger the tools. But don't worry the default +backends include a `.ToolContainerBase` called ``toolbar`` .. note:: @@ -726,7 +716,7 @@ default, performing no display. ``html5`` converts the animation to an h264 encoded video, which is embedded directly in the notebook. Users not wishing to use the ``_repr_html_`` display hook can also manually -call the `to_html5_video` method to get the HTML and display using +call the `.to_html5_video` method to get the HTML and display using IPython's ``HTML`` display class:: from IPython.display import HTML diff --git a/doc/users/prev_whats_new/whats_new_2.0.0.rst b/doc/users/prev_whats_new/whats_new_2.0.0.rst index 7a6bac78e97a..0f5edb7c0e3f 100644 --- a/doc/users/prev_whats_new/whats_new_2.0.0.rst +++ b/doc/users/prev_whats_new/whats_new_2.0.0.rst @@ -1,11 +1,11 @@ .. _whats-new-2-0-0: -New in matplotlib 2.0 -===================== +What's new in Matplotlib 2.0 (Jan 17, 2017) +=========================================== .. note:: - matplotlib 2.0 supports Python 2.7, and 3.4+ + Matplotlib 2.0 supports Python 2.7, and 3.4+ @@ -17,7 +17,7 @@ The major changes in v2.0 are related to overhauling the default styles. .. toctree:: :maxdepth: 2 - ../dflt_style_changes + dflt_style_changes Improved color conversion API and RGBA support @@ -117,10 +117,11 @@ choropleths, labeled image features, etc. -Axis offset label now responds to `labelcolor` +Axis offset label now responds to *labelcolor* ---------------------------------------------- -Axis offset labels are now colored the same as axis tick markers when `labelcolor` is altered. +Axis offset labels are now colored the same as axis tick markers when +*labelcolor* is altered. Improved offset text choice --------------------------- diff --git a/doc/users/prev_whats_new/whats_new_2.1.0.rst b/doc/users/prev_whats_new/whats_new_2.1.0.rst index d426be2834cf..a66e2e10f3a2 100644 --- a/doc/users/prev_whats_new/whats_new_2.1.0.rst +++ b/doc/users/prev_whats_new/whats_new_2.1.0.rst @@ -1,7 +1,7 @@ .. _whats-new-2-1-0: -New in Matplotlib 2.1.0 -======================= +What's new in Matplotlib 2.1.0 (Oct 7, 2017) +============================================ Documentation +++++++++++++ @@ -391,11 +391,11 @@ time and can enhance the visibility of the flow pattern in some use cases. -`Axis.set_tick_params` now responds to ``rotation`` +``Axis.set_tick_params`` now responds to *rotation* --------------------------------------------------- Bulk setting of tick label rotation is now possible via -:func:`~matplotlib.axes.Axes.tick_params` using the ``rotation`` +:func:`~matplotlib.axes.Axes.tick_params` using the *rotation* keyword. :: @@ -406,7 +406,7 @@ keyword. Ticklabels are turned off instead of being invisible ---------------------------------------------------- -Internally, the `.Tick`'s :func:`~matplotlib.axis.Tick.label1On` attribute +Internally, the `.Tick`'s ``matplotlib.axis.Tick.label1On`` attribute is now used to hide tick labels instead of setting the visibility on the tick label objects. This improves overall performance and fixes some issues. diff --git a/doc/users/prev_whats_new/whats_new_2.2.rst b/doc/users/prev_whats_new/whats_new_2.2.rst index 05affccc7a6f..77b9056048f4 100644 --- a/doc/users/prev_whats_new/whats_new_2.2.rst +++ b/doc/users/prev_whats_new/whats_new_2.2.rst @@ -1,7 +1,7 @@ .. _whats-new-2-2-0: -New in Matplotlib 2.2 -===================== +What's new in Matplotlib 2.2 (Mar 06, 2018) +=========================================== Constrained Layout Manager -------------------------- @@ -60,7 +60,7 @@ New ``figure`` kwarg for ``GridSpec`` In order to facilitate ``constrained_layout``, ``GridSpec`` now accepts a ``figure`` keyword. This is backwards compatible, in that not supplying this will simply cause ``constrained_layout`` to not operate on the subplots -orgainzed by this ``GridSpec`` instance. Routines that use ``GridSpec`` (e.g. +organized by this ``GridSpec`` instance. Routines that use ``GridSpec`` (e.g. ``fig.subplots``) have been modified to pass the figure to ``GridSpec``. diff --git a/doc/users/prev_whats_new/whats_new_3.0.rst b/doc/users/prev_whats_new/whats_new_3.0.rst index 38247aee606b..c7da414a062a 100644 --- a/doc/users/prev_whats_new/whats_new_3.0.rst +++ b/doc/users/prev_whats_new/whats_new_3.0.rst @@ -1,7 +1,7 @@ .. _whats-new-3-0-0: -New in Matplotlib 3.0 -===================== +What's new in Matplotlib 3.0 (Sep 18, 2018) +=========================================== Improved default backend selection ---------------------------------- @@ -60,11 +60,11 @@ frame. Padding and separation parameters can be adjusted. Add ``minorticks_on()/off()`` methods for colorbar -------------------------------------------------- -A new method :meth:`.colorbar.Colobar.minorticks_on` has been added to +A new method ``ColorbarBase.minorticks_on`` has been added to correctly display minor ticks on a colorbar. This method doesn't allow the minor ticks to extend into the regions beyond vmin and vmax when the *extend* keyword argument (used while creating the colorbar) is set to 'both', 'max' or -'min'. A complementary method :meth:`.colorbar.Colobar.minorticks_off` has +'min'. A complementary method ``ColorbarBase.minorticks_off`` has also been added to remove the minor ticks on the colorbar. @@ -90,7 +90,7 @@ behaviour has been removed. Now if the file name exists on disk, the user is prompted whether or not to overwrite it. This eliminates guesswork, and allows intentional overwriting, especially when the figure name has been -manually set using `.figure.Figure.canvas.set_window_title()`. +manually set using ``figure.canvas.set_window_title()``. Legend now has a *title_fontsize* keyword argument (and rcParam) @@ -108,9 +108,8 @@ Support for axes.prop_cycle property *markevery* in rcParams ------------------------------------------------------------ The Matplotlib ``rcParams`` settings object now supports configuration -of the attribute :rc:`axes.prop_cycle` with cyclers using the `markevery` -Line2D object property. An example of this feature is provided at -:doc:`/gallery/lines_bars_and_markers/markevery_prop_cycle`. +of the attribute :rc:`axes.prop_cycle` with cyclers using the *markevery* +Line2D object property. Multi-page PDF support for pgf backend -------------------------------------- diff --git a/doc/users/prev_whats_new/whats_new_3.1.0.rst b/doc/users/prev_whats_new/whats_new_3.1.0.rst index 4501243fee8a..8821f8e59257 100644 --- a/doc/users/prev_whats_new/whats_new_3.1.0.rst +++ b/doc/users/prev_whats_new/whats_new_3.1.0.rst @@ -1,6 +1,7 @@ +.. _whats-new-3-1-0: -What's new in Matplotlib 3.1 -============================ +What's new in Matplotlib 3.1 (May 18, 2019) +=========================================== For a list of all of the issues and pull requests since the last revision, see the :ref:`github-stats`. @@ -168,7 +169,7 @@ Return type of ArtistInspector.get_aliases changed was used to simulate a set in earlier versions of Python. It has now been replaced by a set, i.e. ``{fullname: {alias1, alias2, ...}}``. -This value is also stored in `.ArtistInspector.aliasd`, which has likewise +This value is also stored in ``ArtistInspector.aliasd``, which has likewise changed. @@ -332,7 +333,7 @@ were previously displayed as ``1e+04``. MouseEvent button attribute is now an IntEnum ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The :attr:`button` attribute of `~.MouseEvent` instances can take the values +The ``button`` attribute of `~.MouseEvent` instances can take the values None, 1 (left button), 2 (middle button), 3 (right button), "up" (scroll), and "down" (scroll). For better legibility, the 1, 2, and 3 values are now represented using the `enum.IntEnum` class `matplotlib.backend_bases.MouseButton`, diff --git a/doc/users/prev_whats_new/whats_new_3.2.0.rst b/doc/users/prev_whats_new/whats_new_3.2.0.rst index efa099d01a23..12d7fab3af90 100644 --- a/doc/users/prev_whats_new/whats_new_3.2.0.rst +++ b/doc/users/prev_whats_new/whats_new_3.2.0.rst @@ -1,6 +1,7 @@ +.. _whats-new-3-2-0: -What's new in Matplotlib 3.2 -============================ +What's new in Matplotlib 3.2 (Mar 04, 2020) +=========================================== For a list of all of the issues and pull requests since the last revision, see the :ref:`github-stats`. diff --git a/doc/users/prev_whats_new/whats_new_3.3.0.rst b/doc/users/prev_whats_new/whats_new_3.3.0.rst index 04ca9d923f76..f9c9fdcd3713 100644 --- a/doc/users/prev_whats_new/whats_new_3.3.0.rst +++ b/doc/users/prev_whats_new/whats_new_3.3.0.rst @@ -1,6 +1,8 @@ -============================== -What's new in Matplotlib 3.3.0 -============================== +.. _whats-new-3-3-0: + +============================================= +What's new in Matplotlib 3.3.0 (Jul 16, 2020) +============================================= For a list of all of the issues and pull requests since the last revision, see the :ref:`github-stats`. @@ -285,7 +287,7 @@ Align labels to Axes edges -------------------------- `~.axes.Axes.set_xlabel`, `~.axes.Axes.set_ylabel` and -`.ColorbarBase.set_label` support a parameter ``loc`` for simplified +``ColorbarBase.set_label`` support a parameter ``loc`` for simplified positioning. For the xlabel, the supported values are 'left', 'center', or 'right'. For the ylabel, the supported values are 'bottom', 'center', or 'top'. diff --git a/doc/users/prev_whats_new/whats_new_3.4.0.rst b/doc/users/prev_whats_new/whats_new_3.4.0.rst index 15f0c76b19d7..1f4c139db816 100644 --- a/doc/users/prev_whats_new/whats_new_3.4.0.rst +++ b/doc/users/prev_whats_new/whats_new_3.4.0.rst @@ -1,6 +1,8 @@ -============================== -What's new in Matplotlib 3.4.0 -============================== +.. _whats-new-3-4-0: + +============================================= +What's new in Matplotlib 3.4.0 (Mar 26, 2021) +============================================= For a list of all of the issues and pull requests since the last revision, see the :ref:`github-stats`. @@ -164,7 +166,7 @@ New automatic labeling for bar charts A new `.Axes.bar_label` method has been added for auto-labeling bar charts. .. figure:: /gallery/lines_bars_and_markers/images/sphx_glr_bar_label_demo_001.png - :target: /gallery/lines_bars_and_markers/bar_label_demo.html + :target: ../../gallery/lines_bars_and_markers/bar_label_demo.html Example of the new automatic labeling. @@ -394,7 +396,7 @@ style. This can be used to, e.g., distinguish the valid and invalid sides of the constraint boundaries in the solution space of optimizations. .. figure:: /gallery/misc/images/sphx_glr_tickedstroke_demo_002.png - :target: /gallery/misc/tickedstroke_demo.html + :target: ../../gallery/misc/tickedstroke_demo.html Colors and colormaps @@ -700,7 +702,7 @@ The new `.Text` parameter ``transform_rotates_text`` now sets whether rotations of the transform affect the text direction. .. figure:: /gallery/text_labels_and_annotations/images/sphx_glr_text_rotation_relative_to_line_001.png - :target: /gallery/text_labels_and_annotations/text_rotation_relative_to_line.html + :target: ../../gallery/text_labels_and_annotations/text_rotation_relative_to_line.html Example of the new *transform_rotates_text* parameter @@ -724,7 +726,7 @@ for each individual text element in a plot. If no parameter is set, the global value :rc:`mathtext.fontset` will be used. .. figure:: /gallery/text_labels_and_annotations/images/sphx_glr_mathtext_fontfamily_example_001.png - :target: /gallery/text_labels_and_annotations/mathtext_fontfamily_example.html + :target: ../../gallery/text_labels_and_annotations/mathtext_fontfamily_example.html ``TextArea``/``AnchoredText`` support *horizontalalignment* ----------------------------------------------------------- @@ -858,7 +860,7 @@ its entirety, supporting features such as custom styling for error lines and cap marks, control over errorbar spacing, upper and lower limit marks. .. figure:: /gallery/mplot3d/images/sphx_glr_errorbar3d_001.png - :target: /gallery/mplot3d/errorbar3d.html + :target: ../../gallery/mplot3d/errorbar3d.html Stem plots in 3D Axes --------------------- @@ -1021,7 +1023,7 @@ Kerning added to strings in PDFs As with text produced in the Agg backend (see :ref:`the previous what's new entry ` for examples), PDFs now include kerning in -text strings. +text strings. Fully-fractional HiDPI in QtAgg ------------------------------- 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..69f4814123ba --- /dev/null +++ b/doc/users/prev_whats_new/whats_new_3.5.0.rst @@ -0,0 +1,681 @@ +============================================= +What's new in Matplotlib 3.5.0 (Nov 15, 2021) +============================================= + +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 +===================================== + +``subplot_mosaic`` supports simple Axes sharing +----------------------------------------------- + +`.Figure.subplot_mosaic`, `.pyplot.subplot_mosaic` support *simple* Axes +sharing (i.e., only `True`/`False` may be passed to *sharex*/*sharey*). When +`True`, tick label visibility and Axis units will be shared. + +.. plot:: + :include-source: + + mosaic = [ + ['A', [['B', 'C'], + ['D', 'E']]], + ['F', 'G'], + ] + fig = plt.figure(constrained_layout=True) + ax_dict = fig.subplot_mosaic(mosaic, sharex=True, sharey=True) + # All Axes use these scales after this call. + ax_dict['A'].set(xscale='log', yscale='logit') + +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. + +.. plot:: + + import matplotlib.patches as mpatches + + fig, ax = plt.subplots(figsize=(4, 4)) + + ax.plot([0.75, 0.75], [0.25, 0.75], 'ok') + ax.set(xlim=(0, 1), ylim=(0, 1), title='New ArrowStyle options') + + ax.annotate(']->', (0.75, 0.25), (0.25, 0.25), + arrowprops=dict( + arrowstyle=']->', connectionstyle="arc3,rad=-0.05", + shrinkA=5, shrinkB=5, + ), + bbox=dict(boxstyle='square', fc='w'), size='large') + + ax.annotate('<-[', (0.75, 0.75), (0.25, 0.75), + arrowprops=dict( + arrowstyle='<-[', connectionstyle="arc3,rad=-0.05", + shrinkA=5, shrinkB=5, + ), + bbox=dict(boxstyle='square', fc='w'), size='large') + +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 setting 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 modify 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. + +.. figure:: /gallery/images_contours_and_fields/images/sphx_glr_image_antialiasing_001.png + :target: ../../gallery/images_contours_and_fields/image_antialiasing.html + + Example of the interpolation stage options. + +For more details see the discussion of the new keyword argument in +:doc:`/gallery/images_contours_and_fields/image_antialiasing`. + +``imshow`` supports half-float arrays +------------------------------------- + +The `~.axes.Axes.imshow` method now supports half-float arrays, i.e., NumPy +arrays with dtype ``np.float16``. + +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`:: + + ax.set_xticks([1, 2, 3]) + ax.set_xticklabels(['a', 'b', 'c']) + +The combined functionality is now available in `~.Axis.set_ticks`:: + + ax.set_xticks([1, 2, 3], ['a', 'b', 'c']) + +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 +============== + +Triple and quadruple dot mathtext accents +----------------------------------------- + +In addition to single and double dot accents, mathtext now supports triple and +quadruple dot accents. + +.. plot:: + :include-source: + + fig = plt.figure(figsize=(3, 1)) + fig.text(0.5, 0.5, r'$\dot{a} \ddot{b} \dddot{c} \ddddot{d}$', fontsize=40, + horizontalalignment='center', verticalalignment='center') + +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`` 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. + +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 + + fig = plt.figure(figsize=(4, 3)) + for i, alignment in enumerate(['left', 'center', 'right']): + box_input = fig.add_axes([0.1, 0.7 - i*0.3, 0.8, 0.2]) + 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. + +.. plot:: + + import matplotlib.patches as mpatches + from mpl_toolkits.mplot3d import art3d + + fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(6.4, 3), + subplot_kw=dict(projection='3d')) + + ax1.set_title('computed_zorder = True (default)') + ax2.set_title('computed_zorder = False') + ax2.computed_zorder = False + + corners = ((0, 0, 0), (0, 5, 0), (5, 5, 0), (5, 0, 0)) + for ax in (ax1, ax2): + tri = art3d.Poly3DCollection([corners], + facecolors='white', + edgecolors='black', + zorder=1) + ax.add_collection3d(tri) + line, = ax.plot((2, 2), (2, 2), (0, 4), c='red', zorder=2, + label='zorder=2') + points = ax.scatter((3, 3), (1, 3), (1, 3), c='red', zorder=10, + label='zorder=10') + + ax.set_xlim((0, 5)) + ax.set_ylim((0, 5)) + ax.set_zlim((0, 2.5)) + + plane = mpatches.Patch(facecolor='white', edgecolor='black', + label='zorder=1') + fig.legend(handles=[plane, line, points], loc='lower center') + +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. + +.. plot:: + + Nphi, Nr = 18, 8 + phi = np.linspace(0, np.pi, Nphi) + r = np.arange(Nr) + phi = np.tile(phi, Nr).flatten() + r = np.repeat(r, Nphi).flatten() + + x = r * np.sin(phi) + y = r * np.cos(phi) + z = Nr - r + + fig, axs = plt.subplots(1, 3, figsize=(7, 3), + subplot_kw=dict(projection='3d'), + gridspec_kw=dict(wspace=0.4, left=0.08, right=0.98, + bottom=0, top=1)) + for vert_a, ax in zip(['z', 'y', 'x'], axs): + pc = ax.scatter(x, y, z, c=z) + ax.view_init(azim=30, elev=30, vertical_axis=vert_a) + ax.set(xlabel='x', ylabel='y', zlabel='z', + title=f'vertical_axis={vert_a!r}') + +``plot_surface`` supports masked arrays and NaNs +------------------------------------------------ + +`.axes3d.Axes3D.plot_surface` supports masked arrays and NaNs, and will now +hide quads that contain masked or NaN points. The behaviour is similar to +`.Axes.contour` with ``corner_mask=True``. + +.. plot:: + + import matplotlib + import matplotlib.pyplot as plt + import numpy as np + + fig, ax = plt.subplots(figsize=(6, 6), subplot_kw={'projection': '3d'}, + constrained_layout=True) + + x, y = np.mgrid[1:10:1, 1:10:1] + z = x ** 3 + y ** 3 - 500 + z = np.ma.masked_array(z, z < 0) + + ax.plot_surface(x, y, z, rstride=1, cstride=1, linewidth=0, cmap='inferno') + ax.view_init(35, -90) + +3D plotting methods support *data* keyword argument +--------------------------------------------------- + +To match all 2D plotting methods, the 3D Axes now support the *data* keyword +argument. This allows passing arguments indirectly from a DataFrame-like +structure. :: + + data = { # A labelled data set, or e.g., Pandas DataFrame. + 'x': ..., + 'y': ..., + 'z': ..., + 'width': ..., + 'depth': ..., + 'top': ..., + } + + fig, ax = plt.subplots(subplot_kw={'projection': '3d') + ax.bar3d('x', 'y', 'z', 'width', 'depth', 'top', data=data) + +Interactive tool improvements +============================= + +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. + +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() + +Directional sizing cursors +-------------------------- + +Canvases now support setting directional sizing cursors, i.e., horizontal and +vertical double arrows. These are used in e.g., selector widgets. Try the +:doc:`/gallery/widgets/mouse_cursor` example to see the cursor in your desired +backend. + +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 +============================= + +GTK backend +----------- + +A backend supporting GTK4_ has been added. Both Agg and Cairo renderers are +supported. The GTK4 backends may be selected as GTK4Agg or GTK4Cairo. + +.. _GTK4: https://www.gtk.org/ + +Qt backends +----------- + +Support for Qt6 (using either PyQt6_ or PySide6_) has been added, with either +the Agg or Cairo renderers. Simultaneously, support for Qt4 has been dropped. +Both Qt6 and Qt5 are supported by a combined backend (QtAgg or QtCairo), and +the loaded version is determined by modules already imported, the +:envvar:`QT_API` environment variable, and available packages. See +:ref:`QT_API-usage` for details. The versioned Qt5 backend names (Qt5Agg or +Qt5Cairo) remain supported for backwards compatibility. + +.. _PyQt6: https://www.riverbankcomputing.com/static/Docs/PyQt6/ +.. _PySide6: https://doc.qt.io/qtforpython/ + +HiDPI support in Cairo-based, GTK, and Tk backends +-------------------------------------------------- + +The GTK3 backends now support HiDPI fully, including mixed monitor cases (on +Wayland only). The newly added GTK4 backends also support HiDPI. + +The TkAgg backend now supports HiDPI **on Windows only**, including mixed +monitor cases. + +All Cairo-based backends correctly support HiDPI as well as their Agg +counterparts did (i.e., if the toolkit supports HiDPI, then the \*Cairo backend +will now support it, but not otherwise.) + +Qt figure options editor improvements +------------------------------------- + +The figure options editor in the Qt backend now also supports editing the left +and right titles (plus the existing centre title). Editing Axis limits is +better supported when using a date converter. The ``symlog`` option is now +available in Axis scaling options. All entries with the same label are now +shown in the Curves tab. + +WX backends support mouse navigation buttons +-------------------------------------------- + +The WX backends now support navigating through view states using the mouse +forward/backward buttons, as in other backends. + +WebAgg uses asyncio instead of Tornado +-------------------------------------- + +The WebAgg backend defaults to using `asyncio` over Tornado for timer support. +This allows using the WebAgg backend in JupyterLite. + +Version information +=================== + +We switched to the `release-branch-semver`_ version scheme of setuptools-scm. +This only affects the version information for development builds. Their version +number now describes the targeted release, i.e. 3.5.0.dev820+g6768ef8c4c 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/prev_whats_new/whats_new_3.5.2.rst b/doc/users/prev_whats_new/whats_new_3.5.2.rst new file mode 100644 index 000000000000..85b1c38862eb --- /dev/null +++ b/doc/users/prev_whats_new/whats_new_3.5.2.rst @@ -0,0 +1,20 @@ +============================================= +What's new in Matplotlib 3.5.2 (May 02, 2022) +============================================= + +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 + +Windows on ARM support +---------------------- + +Preliminary support for Windows on arm64 target has been added; this requires +FreeType 2.11 or above. + +No binary wheels are available yet but it may be built from source. diff --git a/doc/users/prev_whats_new/whats_new_3.6.0.rst b/doc/users/prev_whats_new/whats_new_3.6.0.rst new file mode 100644 index 000000000000..b7bb958d67ad --- /dev/null +++ b/doc/users/prev_whats_new/whats_new_3.6.0.rst @@ -0,0 +1,872 @@ +============================================= +What's new in Matplotlib 3.6.0 (Sep 15, 2022) +============================================= + +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 +===================================== + +``subplots``, ``subplot_mosaic`` accept *height_ratios* and *width_ratios* arguments +------------------------------------------------------------------------------------ + +The relative width and height of columns and rows in `~.Figure.subplots` and +`~.Figure.subplot_mosaic` can be controlled by passing *height_ratios* and +*width_ratios* keyword arguments to the methods: + +.. plot:: + :include-source: true + + fig = plt.figure() + axs = fig.subplots(3, 1, sharex=True, height_ratios=[3, 1, 1]) + +Previously, this required passing the ratios in *gridspec_kw* arguments:: + + fig = plt.figure() + axs = fig.subplots(3, 1, sharex=True, + gridspec_kw=dict(height_ratios=[3, 1, 1])) + +Constrained layout is no longer considered experimental +------------------------------------------------------- + +The constrained layout engine and API is no longer considered experimental. +Arbitrary changes to behaviour and API are no longer permitted without a +deprecation period. + +New ``layout_engine`` module +---------------------------- + +Matplotlib ships with ``tight_layout`` and ``constrained_layout`` layout +engines. A new `.layout_engine` module is provided to allow downstream +libraries to write their own layout engines and `~.figure.Figure` objects can +now take a `.LayoutEngine` subclass as an argument to the *layout* parameter. + +Compressed layout for fixed-aspect ratio Axes +--------------------------------------------- + +Simple arrangements of Axes with fixed aspect ratios can now be packed together +with ``fig, axs = plt.subplots(2, 3, layout='compressed')``. + +With ``layout='tight'`` or ``'constrained'``, Axes with a fixed aspect ratio +can leave large gaps between each other: + +.. plot:: + + fig, axs = plt.subplots(2, 2, figsize=(5, 3), + sharex=True, sharey=True, layout="constrained") + for ax in axs.flat: + ax.imshow([[0, 1], [2, 3]]) + fig.suptitle("fixed-aspect plots, layout='constrained'") + +Using the ``layout='compressed'`` layout reduces the space between the Axes, +and adds the extra space to the outer margins: + +.. plot:: + + fig, axs = plt.subplots(2, 2, figsize=(5, 3), + sharex=True, sharey=True, layout='compressed') + for ax in axs.flat: + ax.imshow([[0, 1], [2, 3]]) + fig.suptitle("fixed-aspect plots, layout='compressed'") + +See :ref:`compressed_layout` for further details. + +Layout engines may now be removed +--------------------------------- + +The layout engine on a Figure may now be removed by calling +`.Figure.set_layout_engine` with ``'none'``. This may be useful after computing +layout in order to reduce computations, e.g., for subsequent animation loops. + +A different layout engine may be set afterwards, so long as it is compatible +with the previous layout engine. + +``Axes.inset_axes`` flexibility +------------------------------- + +`matplotlib.axes.Axes.inset_axes` now accepts the *projection*, *polar* and +*axes_class* keyword arguments, so that subclasses of `matplotlib.axes.Axes` +may be returned. + +.. plot:: + :include-source: true + + fig, ax = plt.subplots() + + ax.plot([0, 2], [1, 2]) + + polar_ax = ax.inset_axes([0.75, 0.25, 0.2, 0.2], projection='polar') + polar_ax.plot([0, 2], [1, 2]) + +WebP is now a supported output format +------------------------------------- + +Figures may now be saved in WebP format by using the ``.webp`` file extension, +or passing ``format='webp'`` to `~.Figure.savefig`. This relies on `Pillow +`_ support for WebP. + +Garbage collection is no longer run on figure close +--------------------------------------------------- + +Matplotlib has a large number of circular references (between Figure and +Manager, between Axes and Figure, Axes and Artist, Figure and Canvas, etc.) so +when the user drops their last reference to a Figure (and clears it from +pyplot's state), the objects will not immediately be deleted. + +To account for this we have long (since before 2004) had a `gc.collect` (of the +lowest two generations only) in the closing code in order to promptly clean up +after ourselves. However this is both not doing what we want (as most of our +objects will actually survive) and due to clearing out the first generation +opened us up to having unbounded memory usage. + +In cases with a very tight loop between creating the figure and destroying it +(e.g. ``plt.figure(); plt.close()``) the first generation will never grow large +enough for Python to consider running the collection on the higher generations. +This will lead to unbounded memory usage as the long-lived objects are never +re-considered to look for reference cycles and hence are never deleted. + +We now no longer do any garbage collection when a figure is closed, and rely on +Python automatically deciding to run garbage collection periodically. If you +have strict memory requirements, you can call `gc.collect` yourself but this +may have performance impacts in a tight computation loop. + +Plotting methods +================ + +Striped lines (experimental) +---------------------------- + +The new *gapcolor* parameter to `~.Axes.plot` enables the creation of striped +lines. + +.. plot:: + :include-source: true + + x = np.linspace(1., 3., 10) + y = x**3 + + fig, ax = plt.subplots() + ax.plot(x, y, linestyle='--', color='orange', gapcolor='blue', + linewidth=3, label='a striped line') + ax.legend() + +Custom cap widths in box and whisker plots in ``bxp`` and ``boxplot`` +--------------------------------------------------------------------- + +The new *capwidths* parameter to `~.Axes.bxp` and `~.Axes.boxplot` allows +controlling the widths of the caps in box and whisker plots. + +.. plot:: + :include-source: true + + x = np.linspace(-7, 7, 140) + x = np.hstack([-25, x, 25]) + capwidths = [0.01, 0.2] + + fig, ax = plt.subplots() + ax.boxplot([x, x], notch=True, capwidths=capwidths) + ax.set_title(f'{capwidths=}') + +Easier labelling of bars in bar plot +------------------------------------ + +The *label* argument of `~.Axes.bar` and `~.Axes.barh` can now be passed a list +of labels for the bars. The list must be the same length as *x* and labels the +individual bars. Repeated labels are not de-duplicated and will cause repeated +label entries, so this is best used when bars also differ in style (e.g., by +passing a list to *color*, as below.) + +.. plot:: + :include-source: true + + x = ["a", "b", "c"] + y = [10, 20, 15] + color = ['C0', 'C1', 'C2'] + + fig, ax = plt.subplots() + ax.bar(x, y, color=color, label=x) + ax.legend() + +New style format string for colorbar ticks +------------------------------------------ + +The *format* argument of `~.Figure.colorbar` (and other colorbar methods) now +accepts ``{}``-style format strings. + +.. code-block:: python + + fig, ax = plt.subplots() + im = ax.imshow(z) + fig.colorbar(im, format='{x:.2e}') # Instead of '%.2e' + +Linestyles for negative contours may be set individually +-------------------------------------------------------- + +The line style of negative contours may be set by passing the +*negative_linestyles* argument to `.Axes.contour`. Previously, this style could +only be set globally via :rc:`contour.negative_linestyles`. + +.. plot:: + :include-source: true + + delta = 0.025 + x = np.arange(-3.0, 3.0, delta) + y = np.arange(-2.0, 2.0, delta) + X, Y = np.meshgrid(x, y) + Z1 = np.exp(-X**2 - Y**2) + Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2) + Z = (Z1 - Z2) * 2 + + fig, axs = plt.subplots(1, 2) + + CS = axs[0].contour(X, Y, Z, 6, colors='k') + axs[0].clabel(CS, fontsize=9, inline=True) + axs[0].set_title('Default negative contours') + + CS = axs[1].contour(X, Y, Z, 6, colors='k', negative_linestyles='dotted') + axs[1].clabel(CS, fontsize=9, inline=True) + axs[1].set_title('Dotted negative contours') + +Improved quad contour calculations via ContourPy +------------------------------------------------ + +The contouring functions `~.axes.Axes.contour` and `~.axes.Axes.contourf` have +a new keyword argument *algorithm* to control which algorithm is used to +calculate the contours. There is a choice of four algorithms to use, and the +default is to use ``algorithm='mpl2014'`` which is the same algorithm that +Matplotlib has been using since 2014. + +Previously Matplotlib shipped its own C++ code for calculating the contours of +quad grids. Now the external library `ContourPy +`_ is used instead. + +Other possible values of the *algorithm* keyword argument at this time are +``'mpl2005'``, ``'serial'`` and ``'threaded'``; see the `ContourPy +documentation `_ for further details. + +.. note:: + + Contour lines and polygons produced by ``algorithm='mpl2014'`` will be the + same as those produced before this change to within floating-point + tolerance. The exception is for duplicate points, i.e. contours containing + adjacent (x, y) points that are identical; previously the duplicate points + were removed, now they are kept. Contours affected by this will produce the + same visual output, but there will be a greater number of points in the + contours. + + The locations of contour labels obtained by using `~.axes.Axes.clabel` may + also be different. + +``errorbar`` supports *markerfacecoloralt* +------------------------------------------ + +The *markerfacecoloralt* parameter is now passed to the line plotter from +`.Axes.errorbar`. The documentation now accurately lists which properties are +passed to `.Line2D`, rather than claiming that all keyword arguments are passed +on. + +.. plot:: + :include-source: true + + x = np.arange(0.1, 4, 0.5) + y = np.exp(-x) + + fig, ax = plt.subplots() + ax.errorbar(x, y, xerr=0.2, yerr=0.4, + linestyle=':', color='darkgrey', + marker='o', markersize=20, fillstyle='left', + markerfacecolor='tab:blue', markerfacecoloralt='tab:orange', + markeredgecolor='tab:brown', markeredgewidth=2) + +``streamplot`` can disable streamline breaks +-------------------------------------------- + +It is now possible to specify that streamplots have continuous, unbroken +streamlines. Previously streamlines would end to limit the number of lines +within a single grid cell. See the difference between the plots below: + +.. plot:: + + w = 3 + Y, X = np.mgrid[-w:w:100j, -w:w:100j] + U = -1 - X**2 + Y + V = 1 + X - Y**2 + speed = np.sqrt(U**2 + V**2) + + fig, (ax0, ax1) = plt.subplots(1, 2, sharex=True) + + ax0.streamplot(X, Y, U, V, broken_streamlines=True) + ax0.set_title('broken_streamlines=True') + + ax1.streamplot(X, Y, U, V, broken_streamlines=False) + ax1.set_title('broken_streamlines=False') + +New axis scale ``asinh`` (experimental) +--------------------------------------- + +The new ``asinh`` axis scale offers an alternative to ``symlog`` that smoothly +transitions between the quasi-linear and asymptotically logarithmic regions of +the scale. This is based on an arcsinh transformation that allows plotting both +positive and negative values that span many orders of magnitude. + +.. plot:: + + fig, (ax0, ax1) = plt.subplots(1, 2, sharex=True) + x = np.linspace(-3, 6, 100) + + ax0.plot(x, x) + ax0.set_yscale('symlog') + ax0.grid() + ax0.set_title('symlog') + + ax1.plot(x, x) + ax1.set_yscale('asinh') + ax1.grid() + ax1.set_title(r'$sinh^{-1}$') + + for p in (-2, 2): + for ax in (ax0, ax1): + c = plt.Circle((p, p), radius=0.5, fill=False, + color='red', alpha=0.8, lw=3) + ax.add_patch(c) + +``stairs(..., fill=True)`` hides patch edge by setting linewidth +---------------------------------------------------------------- + +``stairs(..., fill=True)`` would previously hide Patch edges by setting +``edgecolor="none"``. Consequently, calling ``set_color()`` on the Patch later +would make the Patch appear larger. + +Now, by using ``linewidth=0``, this apparent size change is prevented. Likewise +calling ``stairs(..., fill=True, linewidth=3)`` will behave more transparently. + +Fix the dash offset of the Patch class +-------------------------------------- + +Formerly, when setting the line style on a `.Patch` object using a dash tuple, +the offset was ignored. Now the offset is applied to the Patch as expected and +it can be used as it is used with `.Line2D` objects. + +Rectangle patch rotation point +------------------------------ + +The rotation point of the `~matplotlib.patches.Rectangle` can now be set to +'xy', 'center' or a 2-tuple of numbers using the *rotation_point* argument. + +.. plot:: + + fig, ax = plt.subplots() + + rect = plt.Rectangle((0, 0), 1, 1, facecolor='none', edgecolor='C0') + ax.add_patch(rect) + ax.annotate('Unrotated', (1, 0), color='C0', + horizontalalignment='right', verticalalignment='top', + xytext=(0, -3), textcoords='offset points') + + for rotation_point, color in zip(['xy', 'center', (0.75, 0.25)], + ['C1', 'C2', 'C3']): + ax.add_patch( + plt.Rectangle((0, 0), 1, 1, facecolor='none', edgecolor=color, + angle=45, rotation_point=rotation_point)) + + if rotation_point == 'center': + point = 0.5, 0.5 + elif rotation_point == 'xy': + point = 0, 0 + else: + point = rotation_point + ax.plot(point[:1], point[1:], color=color, marker='o') + + label = f'{rotation_point}' + if label == 'xy': + label += ' (default)' + ax.annotate(label, point, color=color, + xytext=(3, 3), textcoords='offset points') + + ax.set_aspect(1) + ax.set_title('rotation_point options') + +Colors and colormaps +==================== + +Color sequence registry +----------------------- + +The color sequence registry, `.ColorSequenceRegistry`, contains sequences +(i.e., simple lists) of colors that are known to Matplotlib by name. This will +not normally be used directly, but through the universal instance at +`matplotlib.color_sequences`. + +Colormap method for creating a different lookup table size +---------------------------------------------------------- + +The new method `.Colormap.resampled` creates a new `.Colormap` instance +with the specified lookup table size. This is a replacement for manipulating +the lookup table size via ``get_cmap``. + +Use:: + + get_cmap(name).resampled(N) + +instead of:: + + get_cmap(name, lut=N) + +Setting norms with strings +-------------------------- + +Norms can now be set (e.g. on images) using the string name of the +corresponding scale, e.g. ``imshow(array, norm="log")``. Note that in that +case, it is permissible to also pass *vmin* and *vmax*, as a new Norm instance +will be created under the hood. + +Titles, ticks, and labels +========================= + +``plt.xticks`` and ``plt.yticks`` support *minor* keyword argument +------------------------------------------------------------------ + +It is now possible to set or get minor ticks using `.pyplot.xticks` and +`.pyplot.yticks` by setting ``minor=True``. + +.. plot:: + :include-source: true + + plt.figure() + plt.plot([1, 2, 3, 3.5], [2, 1, 0, -0.5]) + plt.xticks([1, 2, 3], ["One", "Zwei", "Trois"]) + plt.xticks([np.sqrt(2), 2.5, np.pi], + [r"$\sqrt{2}$", r"$\frac{5}{2}$", r"$\pi$"], minor=True) + +Legends +======= + +Legend can control alignment of title and handles +------------------------------------------------- + +`.Legend` now supports controlling the alignment of the title and handles via +the keyword argument *alignment*. You can also use `.Legend.set_alignment` to +control the alignment on existing Legends. + +.. plot:: + :include-source: true + + fig, axs = plt.subplots(3, 1) + for i, alignment in enumerate(['left', 'center', 'right']): + axs[i].plot(range(10), label='test') + axs[i].legend(title=f'{alignment=}', alignment=alignment) + +*ncol* keyword argument to ``legend`` renamed to *ncols* +-------------------------------------------------------- + +The *ncol* keyword argument to `~.Axes.legend` for controlling the number of +columns is renamed to *ncols* for consistency with the *ncols* and *nrows* +keywords of `~.Figure.subplots` and `~.GridSpec`. *ncol* remains supported for +backwards compatibility, but is discouraged. + +Markers +======= + +``marker`` can now be set to the string "none" +---------------------------------------------- + +The string "none" means *no-marker*, consistent with other APIs which support +the lowercase version. Using "none" is recommended over using "None", to avoid +confusion with the None object. + +Customization of ``MarkerStyle`` join and cap style +--------------------------------------------------- + +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 + + from matplotlib.markers import CapStyle, JoinStyle, MarkerStyle + from matplotlib.transforms import Affine2D + + fig, axs = plt.subplots(3, 1, layout='constrained') + for ax in axs: + ax.axis('off') + ax.set_xlim(-0.5, 2.5) + + axs[0].set_title('Cap styles', fontsize=14) + for col, cap in enumerate(CapStyle): + axs[0].plot(col, 0, markersize=32, markeredgewidth=8, + marker=MarkerStyle('1', capstyle=cap)) + # Show the marker edge for comparison with the cap. + axs[0].plot(col, 0, markersize=32, markeredgewidth=1, + markerfacecolor='none', markeredgecolor='lightgrey', + marker=MarkerStyle('1')) + axs[0].annotate(cap.name, (col, 0), + xytext=(20, -5), textcoords='offset points') + + axs[1].set_title('Join styles', fontsize=14) + for col, join in enumerate(JoinStyle): + axs[1].plot(col, 0, markersize=32, markeredgewidth=8, + marker=MarkerStyle('*', joinstyle=join)) + # Show the marker edge for comparison with the join. + axs[1].plot(col, 0, markersize=32, markeredgewidth=1, + markerfacecolor='none', markeredgecolor='lightgrey', + marker=MarkerStyle('*')) + axs[1].annotate(join.name, (col, 0), + xytext=(20, -5), textcoords='offset points') + + axs[2].set_title('Arbitrary transforms', fontsize=14) + for col, (size, rot) in enumerate(zip([2, 5, 7], [0, 45, 90])): + t = Affine2D().rotate_deg(rot).scale(size) + axs[2].plot(col, 0, marker=MarkerStyle('*', transform=t)) + +Fonts and Text +============== + +Font fallback +------------- + +It is now possible to specify a list of fonts families and Matplotlib will try +them in order to locate a required glyph. + +.. plot:: + :caption: Demonstration of mixed English and Chinese text with font fallback. + :alt: The phrase "There are 几个汉字 in between!" rendered in various fonts. + :include-source: True + + plt.rcParams["font.size"] = 20 + fig = plt.figure(figsize=(4.75, 1.85)) + + text = "There are 几个汉字 in between!" + fig.text(0.05, 0.85, text, family=["WenQuanYi Zen Hei"]) + fig.text(0.05, 0.65, text, family=["Noto Sans CJK JP"]) + fig.text(0.05, 0.45, text, family=["DejaVu Sans", "Noto Sans CJK JP"]) + fig.text(0.05, 0.25, text, family=["DejaVu Sans", "WenQuanYi Zen Hei"]) + +This currently works with the Agg (and all of the GUI embeddings), svg, pdf, +ps, and inline backends. + +List of available font names +---------------------------- + +The list of available fonts are now easily accessible. To get a list of the +available font names in Matplotlib use: + +.. code-block:: python + + from matplotlib import font_manager + font_manager.get_font_names() + +``math_to_image`` now has a *color* keyword argument +---------------------------------------------------- + +To easily support external libraries that rely on the MathText rendering of +Matplotlib to generate equation images, a *color* keyword argument was added to +`~matplotlib.mathtext.math_to_image`. + +.. code-block:: python + + from matplotlib import mathtext + mathtext.math_to_image('$x^2$', 'filename.png', color='Maroon') + +Active URL area rotates with link text +-------------------------------------- + +When link text is rotated in a figure, the active URL area will now include the +rotated link area. Previously, the active area remained in the original, +non-rotated, position. + +rcParams improvements +===================== + +Allow setting figure label size and weight globally and separately from title +----------------------------------------------------------------------------- + +For figure labels, ``Figure.supxlabel`` and ``Figure.supylabel``, the size and +weight can be set separately from the figure title using :rc:`figure.labelsize` +and :rc:`figure.labelweight`. + +.. plot:: + :include-source: true + + # Original (previously combined with below) rcParams: + plt.rcParams['figure.titlesize'] = 64 + plt.rcParams['figure.titleweight'] = 'bold' + + # New rcParams: + plt.rcParams['figure.labelsize'] = 32 + plt.rcParams['figure.labelweight'] = 'bold' + + fig, axs = plt.subplots(2, 2, layout='constrained') + for ax in axs.flat: + ax.set(xlabel='xlabel', ylabel='ylabel') + + fig.suptitle('suptitle') + fig.supxlabel('supxlabel') + fig.supylabel('supylabel') + +Note that if you have changed :rc:`figure.titlesize` or +:rc:`figure.titleweight`, you must now also change the introduced parameters +for a result consistent with past behaviour. + +Mathtext parsing can be disabled globally +----------------------------------------- + +The :rc:`text.parse_math` setting may be used to disable parsing of mathtext in +all `.Text` objects (most notably from the `.Axes.text` method). + +Double-quoted strings in matplotlibrc +------------------------------------- + +You can now use double-quotes around strings. This allows using the '#' +character in strings. Without quotes, '#' is interpreted as start of a comment. +In particular, you can now define hex-colors: + +.. code-block:: none + + grid.color: "#b0b0b0" + +3D Axes improvements +==================== + +Standardized views for primary plane viewing angles +--------------------------------------------------- + +When viewing a 3D plot in one of the primary view planes (i.e., perpendicular +to the XY, XZ, or YZ planes), the Axis will be displayed in a standard +location. For further information on 3D views, see +:ref:`toolkit_mplot3d-view-angles` and :doc:`/gallery/mplot3d/view_planes_3d`. + +Custom focal length for 3D camera +--------------------------------- + +The 3D Axes can now better mimic real-world cameras by specifying the focal +length of the virtual camera. The default focal length of 1 corresponds to a +Field of View (FOV) of 90°, and is backwards-compatible with existing 3D plots. +An increased focal length between 1 and infinity "flattens" the image, while a +decreased focal length between 1 and 0 exaggerates the perspective and gives +the image more apparent depth. + +The focal length can be calculated from a desired FOV via the equation: + +.. mathmpl:: + + focal\_length = 1/\tan(FOV/2) + +.. plot:: + :include-source: true + + from mpl_toolkits.mplot3d import axes3d + + X, Y, Z = axes3d.get_test_data(0.05) + + fig, axs = plt.subplots(1, 3, figsize=(7, 4), + subplot_kw={'projection': '3d'}) + + for ax, focal_length in zip(axs, [0.2, 1, np.inf]): + ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10) + ax.set_proj_type('persp', focal_length=focal_length) + ax.set_title(f"{focal_length=}") + +3D plots gained a 3rd "roll" viewing angle +------------------------------------------ + +3D plots can now be viewed from any orientation with the addition of a 3rd roll +angle, which rotates the plot about the viewing axis. Interactive rotation +using the mouse still only controls elevation and azimuth, meaning that this +feature is relevant to users who create more complex camera angles +programmatically. The default roll angle of 0 is backwards-compatible with +existing 3D plots. + +.. plot:: + :include-source: true + + from mpl_toolkits.mplot3d import axes3d + + X, Y, Z = axes3d.get_test_data(0.05) + + fig, ax = plt.subplots(subplot_kw={'projection': '3d'}) + + ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10) + ax.view_init(elev=0, azim=0, roll=30) + ax.set_title('elev=0, azim=0, roll=30') + +Equal aspect ratio for 3D plots +------------------------------- + +Users can set the aspect ratio for the X, Y, Z axes of a 3D plot to be 'equal', +'equalxy', 'equalxz', or 'equalyz' rather than the default of 'auto'. + +.. plot:: + :include-source: true + + from itertools import combinations, product + + aspects = [ + ['auto', 'equal', '.'], + ['equalxy', 'equalyz', 'equalxz'], + ] + fig, axs = plt.subplot_mosaic(aspects, figsize=(7, 6), + subplot_kw={'projection': '3d'}) + + # Draw rectangular cuboid with side lengths [1, 1, 5] + r = [0, 1] + scale = np.array([1, 1, 5]) + pts = combinations(np.array(list(product(r, r, r))), 2) + for start, end in pts: + if np.sum(np.abs(start - end)) == r[1] - r[0]: + for ax in axs.values(): + ax.plot3D(*zip(start*scale, end*scale), color='C0') + + # Set the aspect ratios + for aspect, ax in axs.items(): + ax.set_box_aspect((3, 4, 5)) + ax.set_aspect(aspect) + ax.set_title(f'set_aspect({aspect!r})') + +Interactive tool improvements +============================= + +Rotation, aspect ratio correction and add/remove state +------------------------------------------------------ + +The `.RectangleSelector` and `.EllipseSelector` can now be rotated +interactively between -45° and 45°. The range limits are currently dictated by +the implementation. The rotation is enabled or disabled by striking the *r* key +('r' is the default key mapped to 'rotate' in *state_modifier_keys*) or by +calling ``selector.add_state('rotate')``. + +The aspect ratio of the axes can now be taken into account when using the +"square" state. This is enabled by specifying ``use_data_coordinates='True'`` +when the selector is initialized. + +In addition to changing selector state interactively using the modifier keys +defined in *state_modifier_keys*, the selector state can now be changed +programmatically using the *add_state* and *remove_state* methods. + +.. code-block:: python + + from matplotlib.widgets import RectangleSelector + + values = np.arange(0, 100) + + fig = plt.figure() + ax = fig.add_subplot() + ax.plot(values, values) + + selector = RectangleSelector(ax, print, interactive=True, + drag_from_anywhere=True, + use_data_coordinates=True) + selector.add_state('rotate') # alternatively press 'r' key + # rotate the selector interactively + + selector.remove_state('rotate') # alternatively press 'r' key + + selector.add_state('square') + +``MultiCursor`` now supports Axes split over multiple figures +------------------------------------------------------------- + +Previously, `.MultiCursor` only worked if all target Axes belonged to the same +figure. + +As a consequence of this change, the first argument to the `.MultiCursor` +constructor has become unused (it was previously the joint canvas of all Axes, +but the canvases are now directly inferred from the list of Axes). + +``PolygonSelector`` bounding boxes +---------------------------------- + +`.PolygonSelector` now has a *draw_bounding_box* argument, which when set to +`True` will draw a bounding box around the polygon once it is complete. The +bounding box can be resized and moved, allowing the points of the polygon to be +easily resized. + +Setting ``PolygonSelector`` vertices +------------------------------------ + +The vertices of `.PolygonSelector` can now be set programmatically by using the +`.PolygonSelector.verts` property. Setting the vertices this way will reset the +selector, and create a new complete selector with the supplied vertices. + +``SpanSelector`` widget can now be snapped to specified values +-------------------------------------------------------------- + +The `.SpanSelector` widget can now be snapped to values specified by the +*snap_values* argument. + +More toolbar icons are styled for dark themes +--------------------------------------------- + +On the macOS and Tk backends, toolbar icons will now be inverted when using a +dark theme. + +Platform-specific changes +========================= + +Wx backend uses standard toolbar +-------------------------------- + +Instead of a custom sizer, the toolbar is set on Wx windows as a standard +toolbar. + +Improvements to macosx backend +------------------------------ + +Modifier keys handled more consistently +....................................... + +The macosx backend now handles modifier keys in a manner more consistent with +other backends. See the table in :ref:`event-connections` for further +information. + +``savefig.directory`` rcParam support +..................................... + +The macosx backend will now obey the :rc:`savefig.directory` setting. If set to +a non-empty string, then the save dialog will default to this directory, and +preserve subsequent save directories as they are changed. + +``figure.raise_window`` rcParam support +....................................... + +The macosx backend will now obey the :rc:`figure.raise_window` setting. If set +to False, figure windows will not be raised to the top on update. + +Full-screen toggle support +.......................... + +As supported on other backends, the macosx backend now supports toggling +fullscreen view. By default, this view can be toggled by pressing the :kbd:`f` +key. + +Improved animation and blitting support +....................................... + +The macosx backend has been improved to fix blitting, animation frames with new +artists, and to reduce unnecessary draw calls. + +macOS application icon applied on Qt backend +-------------------------------------------- + +When using the Qt-based backends on macOS, the application icon will now be +set, as is done on other backends/platforms. + +New minimum macOS version +------------------------- + +The macosx backend now requires macOS >= 10.12. + +Windows on ARM support +---------------------- + +Preliminary support for Windows on arm64 target has been added. This support +requires FreeType 2.11 or above. + +No binary wheels are available yet but it may be built from source. diff --git a/doc/users/project/citing.rst b/doc/users/project/citing.rst new file mode 100644 index 000000000000..5dd7488e50b6 --- /dev/null +++ b/doc/users/project/citing.rst @@ -0,0 +1,171 @@ +.. redirect-from:: /citing + +Citing Matplotlib +================= + +If Matplotlib contributes to a project that leads to a scientific publication, +please acknowledge this fact by citing `J. D. Hunter, "Matplotlib: A 2D +Graphics Environment", Computing in Science & Engineering, vol. 9, no. 3, +pp. 90-95, 2007 `_. + +.. literalinclude:: ../../../CITATION.bib + :language: bibtex + +.. container:: sphx-glr-download + + :download:`Download BibTeX bibliography file: CITATION.bib <../../../CITATION.bib>` + +DOIs +---- + +The following DOI represents *all* Matplotlib versions. Please select a more +specific DOI from the list below, referring to the version used for your publication. + + .. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.592536.svg + :target: https://doi.org/10.5281/zenodo.592536 + +By version +~~~~~~~~~~ +.. START OF AUTOGENERATED + + +v3.6.1 + .. image:: ../../_static/zenodo_cache/7162185.svg + :target: https://doi.org/10.5281/zenodo.7162185 +v3.6.0 + .. image:: ../../_static/zenodo_cache/7084615.svg + :target: https://doi.org/10.5281/zenodo.7084615 +v3.5.3 + .. image:: ../../_static/zenodo_cache/6982547.svg + :target: https://doi.org/10.5281/zenodo.6982547 +v3.5.2 + .. image:: ../../_static/zenodo_cache/6513224.svg + :target: https://doi.org/10.5281/zenodo.6513224 +v3.5.1 + .. image:: ../../_static/zenodo_cache/5773480.svg + :target: https://doi.org/10.5281/zenodo.5773480 +v3.5.0 + .. image:: ../../_static/zenodo_cache/5706396.svg + :target: https://doi.org/10.5281/zenodo.5706396 +v3.4.3 + .. image:: ../../_static/zenodo_cache/5194481.svg + :target: https://doi.org/10.5281/zenodo.5194481 +v3.4.2 + .. image:: ../../_static/zenodo_cache/4743323.svg + :target: https://doi.org/10.5281/zenodo.4743323 +v3.4.1 + .. image:: ../../_static/zenodo_cache/4649959.svg + :target: https://doi.org/10.5281/zenodo.4649959 +v3.4.0 + .. image:: ../../_static/zenodo_cache/4638398.svg + :target: https://doi.org/10.5281/zenodo.4638398 +v3.3.4 + .. image:: ../../_static/zenodo_cache/4475376.svg + :target: https://doi.org/10.5281/zenodo.4475376 +v3.3.3 + .. image:: ../../_static/zenodo_cache/4268928.svg + :target: https://doi.org/10.5281/zenodo.4268928 +v3.3.2 + .. image:: ../../_static/zenodo_cache/4030140.svg + :target: https://doi.org/10.5281/zenodo.4030140 +v3.3.1 + .. image:: ../../_static/zenodo_cache/3984190.svg + :target: https://doi.org/10.5281/zenodo.3984190 +v3.3.0 + .. image:: ../../_static/zenodo_cache/3948793.svg + :target: https://doi.org/10.5281/zenodo.3948793 +v3.2.2 + .. image:: ../../_static/zenodo_cache/3898017.svg + :target: https://doi.org/10.5281/zenodo.3898017 +v3.2.1 + .. image:: ../../_static/zenodo_cache/3714460.svg + :target: https://doi.org/10.5281/zenodo.3714460 +v3.2.0 + .. image:: ../../_static/zenodo_cache/3695547.svg + :target: https://doi.org/10.5281/zenodo.3695547 +v3.1.3 + .. image:: ../../_static/zenodo_cache/3633844.svg + :target: https://doi.org/10.5281/zenodo.3633844 +v3.1.2 + .. image:: ../../_static/zenodo_cache/3563226.svg + :target: https://doi.org/10.5281/zenodo.3563226 +v3.1.1 + .. image:: ../../_static/zenodo_cache/3264781.svg + :target: https://doi.org/10.5281/zenodo.3264781 +v3.1.0 + .. image:: ../../_static/zenodo_cache/2893252.svg + :target: https://doi.org/10.5281/zenodo.2893252 +v3.0.3 + .. image:: ../../_static/zenodo_cache/2577644.svg + :target: https://doi.org/10.5281/zenodo.2577644 +v3.0.2 + .. image:: ../../_static/zenodo_cache/1482099.svg + :target: https://doi.org/10.5281/zenodo.1482099 +v3.0.1 + .. image:: ../../_static/zenodo_cache/1482098.svg + :target: https://doi.org/10.5281/zenodo.1482098 +v2.2.5 + .. image:: ../../_static/zenodo_cache/3633833.svg + :target: https://doi.org/10.5281/zenodo.3633833 +v3.0.0 + .. image:: ../../_static/zenodo_cache/1420605.svg + :target: https://doi.org/10.5281/zenodo.1420605 +v2.2.4 + .. image:: ../../_static/zenodo_cache/2669103.svg + :target: https://doi.org/10.5281/zenodo.2669103 +v2.2.3 + .. image:: ../../_static/zenodo_cache/1343133.svg + :target: https://doi.org/10.5281/zenodo.1343133 +v2.2.2 + .. image:: ../../_static/zenodo_cache/1202077.svg + :target: https://doi.org/10.5281/zenodo.1202077 +v2.2.1 + .. image:: ../../_static/zenodo_cache/1202050.svg + :target: https://doi.org/10.5281/zenodo.1202050 +v2.2.0 + .. image:: ../../_static/zenodo_cache/1189358.svg + :target: https://doi.org/10.5281/zenodo.1189358 +v2.1.2 + .. image:: ../../_static/zenodo_cache/1154287.svg + :target: https://doi.org/10.5281/zenodo.1154287 +v2.1.1 + .. image:: ../../_static/zenodo_cache/1098480.svg + :target: https://doi.org/10.5281/zenodo.1098480 +v2.1.0 + .. image:: ../../_static/zenodo_cache/1004650.svg + :target: https://doi.org/10.5281/zenodo.1004650 +v2.0.2 + .. image:: ../../_static/zenodo_cache/573577.svg + :target: https://doi.org/10.5281/zenodo.573577 +v2.0.1 + .. image:: ../../_static/zenodo_cache/570311.svg + :target: https://doi.org/10.5281/zenodo.570311 +v2.0.0 + .. image:: ../../_static/zenodo_cache/248351.svg + :target: https://doi.org/10.5281/zenodo.248351 +v1.5.3 + .. image:: ../../_static/zenodo_cache/61948.svg + :target: https://doi.org/10.5281/zenodo.61948 +v1.5.2 + .. image:: ../../_static/zenodo_cache/56926.svg + :target: https://doi.org/10.5281/zenodo.56926 +v1.5.1 + .. image:: ../../_static/zenodo_cache/44579.svg + :target: https://doi.org/10.5281/zenodo.44579 +v1.5.0 + .. image:: ../../_static/zenodo_cache/32914.svg + :target: https://doi.org/10.5281/zenodo.32914 +v1.4.3 + .. image:: ../../_static/zenodo_cache/15423.svg + :target: https://doi.org/10.5281/zenodo.15423 +v1.4.2 + .. image:: ../../_static/zenodo_cache/12400.svg + :target: https://doi.org/10.5281/zenodo.12400 +v1.4.1 + .. image:: ../../_static/zenodo_cache/12287.svg + :target: https://doi.org/10.5281/zenodo.12287 +v1.4.0 + .. image:: ../../_static/zenodo_cache/11451.svg + :target: https://doi.org/10.5281/zenodo.11451 + +.. END OF AUTOGENERATED diff --git a/doc/users/credits.rst b/doc/users/project/credits.rst similarity index 99% rename from doc/users/credits.rst rename to doc/users/project/credits.rst index 0c0feed44218..c23d6ac11298 100644 --- a/doc/users/credits.rst +++ b/doc/users/project/credits.rst @@ -1,5 +1,7 @@ .. Note: This file is auto-generated using generate_credits.py +.. redirect-from:: /users/credits + .. _credits: ******* @@ -11,7 +13,7 @@ Matplotlib was written by John D. Hunter, with contributions from an ever-increasing number of users and developers. The current lead developer is Thomas A. Caswell, who is assisted by many `active developers `_. -Please also see our instructions on :doc:`/citing`. +Please also see our instructions on :doc:`/users/project/citing`. The following is a list of contributors extracted from the git revision control history of the project: diff --git a/doc/users/history.rst b/doc/users/project/history.rst similarity index 95% rename from doc/users/history.rst rename to doc/users/project/history.rst index 5294382b951e..3291d7e43f8b 100644 --- a/doc/users/history.rst +++ b/doc/users/project/history.rst @@ -1,3 +1,7 @@ +.. redirect-from:: /users/history + +.. _project_history: + History ======= @@ -9,7 +13,7 @@ History Matplotlib is a library for making 2D plots of arrays in `Python `_. Although it has its origins in emulating the MATLAB graphics commands, it is -independent of MATLAB, and can be used in a Pythonic, object oriented +independent of MATLAB, and can be used in a Pythonic, object-oriented way. Although Matplotlib is written primarily in pure Python, it makes heavy use of `NumPy `_ and other extension code to provide good performance even for large arrays. @@ -72,17 +76,17 @@ nothing about output. The *backends* are device-dependent drawing devices, aka renderers, that transform the frontend representation to hardcopy or a display device (:ref:`what-is-a-backend`). Example backends: PS creates `PostScript® -`_ hardcopy, SVG +`_ 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 and embed it in a Gtk+ application, and so on for `PDF -`_, `WxWidgets +`_, `WxWidgets `_, `Tkinter -`_, etc. +`_, etc. Matplotlib is used by many people in many different contexts. Some people want to automatically generate PostScript files to send diff --git a/doc/users/project/index.rst b/doc/users/project/index.rst new file mode 100644 index 000000000000..27f60d166abb --- /dev/null +++ b/doc/users/project/index.rst @@ -0,0 +1,13 @@ +.. redirect-from:: /users/backmatter + +Project information +------------------- + +.. toctree:: + :maxdepth: 2 + + mission.rst + history.rst + citing.rst + license.rst + credits.rst diff --git a/doc/users/project/license.rst b/doc/users/project/license.rst new file mode 100644 index 000000000000..a55927d9f2c5 --- /dev/null +++ b/doc/users/project/license.rst @@ -0,0 +1,50 @@ +.. _license: + +.. redirect-from:: /users/license + +******* +License +******* + +Matplotlib only uses BSD compatible code, and its license is based on +the `PSF `_ license. See the Open +Source Initiative `licenses page +`_ for details on individual +licenses. Non-BSD compatible licenses (e.g., LGPL) are acceptable in +matplotlib toolkits. For a discussion of the motivations behind the +licencing choice, see :ref:`license-discussion`. + +Copyright policy +================ + +John Hunter began Matplotlib around 2003. Since shortly before his +passing in 2012, Michael Droettboom has been the lead maintainer of +Matplotlib, but, as has always been the case, Matplotlib is the work +of many. + +Prior to July of 2013, and the 1.3.0 release, the copyright of the +source code was held by John Hunter. As of July 2013, and the 1.3.0 +release, matplotlib has moved to a shared copyright model. + +Matplotlib uses a shared copyright model. Each contributor maintains +copyright over their contributions to Matplotlib. But, it is important to +note that these contributions are typically only changes to the +repositories. Thus, the Matplotlib source code, in its entirety, is not +the copyright of any single person or institution. Instead, it is the +collective copyright of the entire Matplotlib Development Team. If +individual contributors want to maintain a record of what +changes/contributions they have specific copyright on, they should +indicate their copyright in the commit message of the change, when +they commit the change to one of the matplotlib repositories. + +The Matplotlib Development Team is the set of all contributors to the +matplotlib project. A full list can be obtained from the git version +control logs. + +.. _license-agreement: + +License agreement +================= + +.. literalinclude:: ../../../LICENSE/LICENSE + :language: none diff --git a/doc/users/project/mission.rst b/doc/users/project/mission.rst new file mode 100644 index 000000000000..cf5eae92906f --- /dev/null +++ b/doc/users/project/mission.rst @@ -0,0 +1,21 @@ +Matplotlib Mission Statement +============================ + +The Matplotlib developer community develops, maintains, and supports Matplotlib +and its extensions to provide data visualization tools for the Scientific +Python Ecosystem. + +Adapting the requirements :ref:`laid out by John Hunter ` +Matplotlib should: + +* Support users of the Scientific Python ecosystem; +* Facilitate interactive data exploration; +* Produce high-quality raster and vector format outputs suitable for publication; +* Provide a simple graphical user interface and support embedding in applications; +* Be understandable and extensible by people familiar with data processing in Python; +* Make common plots easy, and novel or complex visualizations possible. + +We believe that a diverse developer community creates the best software, and we +welcome anyone who shares our mission, and our values described in the `code of +conduct +`__. diff --git a/doc/users/release_notes.rst b/doc/users/release_notes.rst new file mode 100644 index 000000000000..5ec5d471142f --- /dev/null +++ b/doc/users/release_notes.rst @@ -0,0 +1,222 @@ +.. redirect-from:: /api/api_changes_old +.. redirect-from:: /users/whats_new_old + + +============= +Release notes +============= + +.. include from another document so that it's easy to exclude this for releases +.. include: release_notes_next.rst + +Version 3.6 +=========== +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_3.6.0.rst + ../api/prev_api_changes/api_changes_3.6.1.rst + ../api/prev_api_changes/api_changes_3.6.0.rst + github_stats.rst + prev_whats_new/github_stats_3.6.0.rst + +Version 3.5 +=========== +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_3.5.2.rst + prev_whats_new/whats_new_3.5.0.rst + ../api/prev_api_changes/api_changes_3.5.3.rst + ../api/prev_api_changes/api_changes_3.5.2.rst + ../api/prev_api_changes/api_changes_3.5.0.rst + prev_whats_new/github_stats_3.5.3.rst + prev_whats_new/github_stats_3.5.2.rst + prev_whats_new/github_stats_3.5.1.rst + prev_whats_new/github_stats_3.5.0.rst + +Version 3.4 +=========== +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_3.4.0.rst + ../api/prev_api_changes/api_changes_3.4.2.rst + ../api/prev_api_changes/api_changes_3.4.0.rst + prev_whats_new/github_stats_3.4.1.rst + prev_whats_new/github_stats_3.4.0.rst + +Past versions +============= + +Version 3.3 +~~~~~~~~~~~ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_3.3.0.rst + ../api/prev_api_changes/api_changes_3.3.1.rst + ../api/prev_api_changes/api_changes_3.3.0.rst + prev_whats_new/github_stats_3.3.4.rst + prev_whats_new/github_stats_3.3.3.rst + prev_whats_new/github_stats_3.3.2.rst + prev_whats_new/github_stats_3.3.1.rst + prev_whats_new/github_stats_3.3.0.rst + +Version 3.2 +~~~~~~~~~~~ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_3.2.0.rst + ../api/prev_api_changes/api_changes_3.2.0.rst + prev_whats_new/github_stats_3.2.2.rst + prev_whats_new/github_stats_3.2.1.rst + prev_whats_new/github_stats_3.2.0.rst + +Version 3.1 +~~~~~~~~~~~ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_3.1.0.rst + ../api/prev_api_changes/api_changes_3.1.1.rst + ../api/prev_api_changes/api_changes_3.1.0.rst + prev_whats_new/github_stats_3.1.3.rst + prev_whats_new/github_stats_3.1.2.rst + prev_whats_new/github_stats_3.1.1.rst + prev_whats_new/github_stats_3.1.0.rst + +Version 3.0 +~~~~~~~~~~~ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_3.0.rst + ../api/prev_api_changes/api_changes_3.0.1.rst + ../api/prev_api_changes/api_changes_3.0.0.rst + prev_whats_new/github_stats_3.0.3.rst + prev_whats_new/github_stats_3.0.2.rst + prev_whats_new/github_stats_3.0.1.rst + prev_whats_new/github_stats_3.0.0.rst + +Version 2.2 +~~~~~~~~~~~ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_2.2.rst + ../api/prev_api_changes/api_changes_2.2.0.rst + +Version 2.1 +~~~~~~~~~~~ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_2.1.0.rst + ../api/prev_api_changes/api_changes_2.1.2.rst + ../api/prev_api_changes/api_changes_2.1.1.rst + ../api/prev_api_changes/api_changes_2.1.0.rst + +Version 2.0 +~~~~~~~~~~~ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_2.0.0.rst + ../api/prev_api_changes/api_changes_2.0.1.rst + ../api/prev_api_changes/api_changes_2.0.0.rst + +Version 1.5 +~~~~~~~~~~~ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_1.5.rst + ../api/prev_api_changes/api_changes_1.5.3.rst + ../api/prev_api_changes/api_changes_1.5.2.rst + ../api/prev_api_changes/api_changes_1.5.0.rst + +Version 1.4 +~~~~~~~~~~~ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_1.4.rst + ../api/prev_api_changes/api_changes_1.4.x.rst + +Version 1.3 +~~~~~~~~~~~ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_1.3.rst + ../api/prev_api_changes/api_changes_1.3.x.rst + +Version 1.2 +~~~~~~~~~~~ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_1.2.2.rst + prev_whats_new/whats_new_1.2.rst + ../api/prev_api_changes/api_changes_1.2.x.rst + +Version 1.1 +~~~~~~~~~~~ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_1.1.rst + ../api/prev_api_changes/api_changes_1.1.x.rst + +Version 1.0 +~~~~~~~~~~~ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_1.0.rst + +Version 0.x +~~~~~~~~~~~ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/changelog.rst + prev_whats_new/whats_new_0.99.rst + ../api/prev_api_changes/api_changes_0.99.x.rst + ../api/prev_api_changes/api_changes_0.99.rst + prev_whats_new/whats_new_0.98.4.rst + ../api/prev_api_changes/api_changes_0.98.x.rst + ../api/prev_api_changes/api_changes_0.98.1.rst + ../api/prev_api_changes/api_changes_0.98.0.rst + ../api/prev_api_changes/api_changes_0.91.2.rst + ../api/prev_api_changes/api_changes_0.91.0.rst + ../api/prev_api_changes/api_changes_0.90.1.rst + ../api/prev_api_changes/api_changes_0.90.0.rst + + ../api/prev_api_changes/api_changes_0.87.7.rst + ../api/prev_api_changes/api_changes_0.86.rst + ../api/prev_api_changes/api_changes_0.85.rst + ../api/prev_api_changes/api_changes_0.84.rst + ../api/prev_api_changes/api_changes_0.83.rst + ../api/prev_api_changes/api_changes_0.82.rst + ../api/prev_api_changes/api_changes_0.81.rst + ../api/prev_api_changes/api_changes_0.80.rst + + ../api/prev_api_changes/api_changes_0.73.rst + ../api/prev_api_changes/api_changes_0.72.rst + ../api/prev_api_changes/api_changes_0.71.rst + ../api/prev_api_changes/api_changes_0.70.rst + + ../api/prev_api_changes/api_changes_0.65.1.rst + ../api/prev_api_changes/api_changes_0.65.rst + ../api/prev_api_changes/api_changes_0.63.rst + ../api/prev_api_changes/api_changes_0.61.rst + ../api/prev_api_changes/api_changes_0.60.rst + + ../api/prev_api_changes/api_changes_0.54.3.rst + ../api/prev_api_changes/api_changes_0.54.rst + ../api/prev_api_changes/api_changes_0.50.rst + ../api/prev_api_changes/api_changes_0.42.rst + ../api/prev_api_changes/api_changes_0.40.rst diff --git a/doc/users/release_notes_next.rst b/doc/users/release_notes_next.rst new file mode 100644 index 000000000000..6813f61c5f90 --- /dev/null +++ b/doc/users/release_notes_next.rst @@ -0,0 +1,10 @@ +:orphan: + +Next version +============ +.. toctree:: + :maxdepth: 1 + + next_whats_new + ../api/next_api_changes + github_stats diff --git a/doc/users/resources/index.rst b/doc/users/resources/index.rst new file mode 100644 index 000000000000..a2c4ccd4a7fc --- /dev/null +++ b/doc/users/resources/index.rst @@ -0,0 +1,87 @@ +.. _resources-index: + +.. redirect-from:: /resources/index + +****************** +External resources +****************** + + +============================ +Books, chapters and articles +============================ + +* `Scientific Visualization: Python + Matplotlib (2021) + `_ + by Nicolas P. Rougier + +* `Mastering matplotlib + `_ + by Duncan M. McGreggor + +* `Interactive Applications Using Matplotlib + `_ + by Benjamin Root + +* `Matplotlib for Python Developers + `_ + by Sandro Tosi + +* `Matplotlib chapter `_ + by John Hunter and Michael Droettboom in The Architecture of Open Source + Applications + +* `Ten Simple Rules for Better Figures + `_ + by Nicolas P. Rougier, Michael Droettboom and Philip E. Bourne + +* `Learning Scientific Programming with Python chapter 7 + `_ + by Christian Hill + +* `Hands-On Data Analysis with Pandas, chapters 5 and 6 + `_ + by Stefanie Molin + +====== +Videos +====== + +* `Plotting with matplotlib `_ + by Mike Müller + +* `Introduction to NumPy and Matplotlib + `_ by Eric Jones + +* `Anatomy of Matplotlib + `_ + by Benjamin Root + +* `Data Visualization Basics with Python (O'Reilly) + `_ + by Randal S. Olson +* `Matplotlib Introduction + `_ + by codebasics +* `Matplotlib + `_ + by Derek Banas + +========= +Tutorials +========= + + +* `The Python Graph Gallery `_ + by Yan Holtz + +* `Matplotlib tutorial `_ + by Nicolas P. Rougier + +* `Anatomy of Matplotlib - IPython Notebooks + `_ + by Benjamin Root + +* `Beyond the Basics: Data Visualization in Python + `_ + by Stefanie Molin diff --git a/doc/users/whats_new.rst b/doc/users/whats_new.rst deleted file mode 100644 index 776f477ce4d4..000000000000 --- a/doc/users/whats_new.rst +++ /dev/null @@ -1,29 +0,0 @@ -.. _whats-new: - -=========== -What's new? -=========== - -.. ifconfig:: releaselevel == 'dev' - - .. note:: - - The list below is a table of contents of individual files from the - 'next_whats_new' folder. - - When a release is made - - - All the files in 'next_whats_new/' should be moved to a single file in - 'prev_whats_new/'. - - The include directive below should be changed to point to the new file - created in the previous step. - - - .. toctree:: - :glob: - :maxdepth: 1 - - next_whats_new/* - -.. Be sure to update the version in `exclude_patterns` in conf.py. -.. include:: prev_whats_new/whats_new_3.4.0.rst diff --git a/doc/users/whats_new_old.rst b/doc/users/whats_new_old.rst deleted file mode 100644 index d24992a1b242..000000000000 --- a/doc/users/whats_new_old.rst +++ /dev/null @@ -1,12 +0,0 @@ - -=================== -Previous What's New -=================== - -.. toctree:: - :glob: - :maxdepth: 1 - :reversed: - - prev_whats_new/changelog - prev_whats_new/whats_new_* diff --git a/environment.yml b/environment.yml new file mode 100644 index 000000000000..28ff3a1b2c34 --- /dev/null +++ b/environment.yml @@ -0,0 +1,60 @@ +# To set up a development environment using conda run: +# +# conda env create -f environment.yml +# conda activate mpl-dev +# pip install -e . +# +name: mpl-dev +channels: + - conda-forge +dependencies: + - cairocffi + - contourpy>=1.0.1 + - cycler>=0.10.0 + - fonttools>=4.22.0 + - kiwisolver>=1.0.1 + - numpy>=1.19 + - pillow>=6.2 + - pygobject + - pyparsing + - pyqt + - python-dateutil>=2.1 + - setuptools + - setuptools_scm + - wxpython + # building documentation + - colorspacious + - graphviz + - ipython + - ipywidgets + - numpydoc>=0.8 + - packaging + - pydata-sphinx-theme + - sphinx>=1.8.1,!=2.0.0 + - sphinx-copybutton + - sphinx-gallery>=0.10 + - sphinx-design + - pip + - pip: + - mpl-sphinx-theme + - sphinxcontrib-svg2pdfconverter + - pikepdf + # testing + - coverage + - flake8>=3.8 + - flake8-docstrings>=1.4.0 + - gtk3 + - ipykernel + - nbconvert[execute]!=6.0.0,!=6.0.1 + - nbformat!=5.0.0,!=5.0.1 + - pandas!=0.25.0 + - psutil + - pre-commit + - pydocstyle>=5.1.0 + - pytest!=4.6.0,!=5.4.0 + - pytest-cov + - pytest-rerunfailures + - pytest-timeout + - pytest-xdist + - tornado + - pytz diff --git a/examples/README b/examples/README deleted file mode 100644 index 746f2e334664..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 - http://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/README.txt b/examples/README.txt index f2331df7246a..87098fb881ce 100644 --- a/examples/README.txt +++ b/examples/README.txt @@ -2,13 +2,13 @@ .. _gallery: -======= -Gallery -======= +======== +Examples +======== -This gallery contains examples of the many things you can do with -Matplotlib. Click on any image to see the full image and source code. +This page contains example plots. Click on any image to see the full image +and source code. For longer tutorials, see our `tutorials page <../tutorials/index.html>`_. -You can also find `external resources <../resources/index.html>`_ and -a `FAQ <../faq/index.html>`_ in our `user guide <../contents.html>`_. +You can also find `external resources <../users/resources/index.html>`_ and +a `FAQ <../users/faq/index.html>`_ in our `user guide <../users/index.html>`_. diff --git a/examples/animation/animate_decay.py b/examples/animation/animate_decay.py index 88f25e2d6aaa..0c056bc2f8a8 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. """ @@ -23,7 +24,7 @@ def data_gen(): def init(): ax.set_ylim(-1.1, 1.1) - ax.set_xlim(0, 10) + ax.set_xlim(0, 1) del xdata[:] del ydata[:] line.set_data(xdata, ydata) @@ -49,5 +50,5 @@ def run(data): return line, -ani = animation.FuncAnimation(fig, run, data_gen, interval=10, init_func=init) +ani = animation.FuncAnimation(fig, run, data_gen, interval=100, init_func=init) plt.show() diff --git a/examples/animation/animation_demo.py b/examples/animation/animation_demo.py index beab2801d731..ed88e2d9a418 100644 --- a/examples/animation/animation_demo.py +++ b/examples/animation/animation_demo.py @@ -20,9 +20,9 @@ fig, ax = plt.subplots() -for i in range(len(data)): - ax.cla() - ax.imshow(data[i]) - ax.set_title("frame {}".format(i)) +for i, img in enumerate(data): + ax.clear() + ax.imshow(img) + ax.set_title(f"frame {i}") # Note that using time.sleep does *not* work here! plt.pause(0.1) diff --git a/examples/animation/double_pendulum.py b/examples/animation/double_pendulum.py index d497c1e24d83..fba7ea4ec9e2 100644 --- a/examples/animation/double_pendulum.py +++ b/examples/animation/double_pendulum.py @@ -12,7 +12,6 @@ from numpy import sin, cos import numpy as np import matplotlib.pyplot as plt -import scipy.integrate as integrate import matplotlib.animation as animation from collections import deque @@ -22,13 +21,13 @@ L = L1 + L2 # maximal length of the combined pendulum M1 = 1.0 # mass of pendulum 1 in kg M2 = 1.0 # mass of pendulum 2 in kg -t_stop = 5 # how many seconds to simulate +t_stop = 2.5 # how many seconds to simulate history_len = 500 # how many trajectory points to display -def derivs(state, t): - +def derivs(t, state): dydx = np.zeros_like(state) + dydx[0] = state[1] delta = state[2] - state[0] @@ -51,7 +50,7 @@ def derivs(state, t): return dydx # create a time array from 0..t_stop sampled at 0.02 second steps -dt = 0.02 +dt = 0.01 t = np.arange(0, t_stop, dt) # th1 and th2 are the initial angles (degrees) @@ -64,8 +63,15 @@ def derivs(state, t): # initial state state = np.radians([th1, w1, th2, w2]) -# integrate your ODE using scipy.integrate. -y = integrate.odeint(derivs, state, t) +# integrate the ODE using Euler's method +y = np.empty((len(t), 4)) +y[0] = state +for i in range(1, len(t)): + y[i] = y[i - 1] + derivs(t[i - 1], y[i - 1]) * dt + +# A more accurate estimate could be obtained e.g. using scipy: +# +# y = scipy.integrate.solve_ivp(derivs, t[[0, -1]], state, t_eval=t).y.T x1 = L1*sin(y[:, 0]) y1 = -L1*cos(y[:, 0]) @@ -79,7 +85,7 @@ def derivs(state, t): ax.grid() line, = ax.plot([], [], 'o-', lw=2) -trace, = ax.plot([], [], ',-', lw=1) +trace, = ax.plot([], [], '.-', lw=1, ms=2) time_template = 'time = %.1fs' time_text = ax.text(0.05, 0.9, '', transform=ax.transAxes) history_x, history_y = deque(maxlen=history_len), deque(maxlen=history_len) diff --git a/examples/animation/dynamic_image.py b/examples/animation/dynamic_image.py index 5543da039639..cbec62607b2c 100644 --- a/examples/animation/dynamic_image.py +++ b/examples/animation/dynamic_image.py @@ -23,8 +23,8 @@ def f(x, y): # each frame ims = [] for i in range(60): - x += np.pi / 15. - y += np.pi / 20. + x += np.pi / 15 + y += np.pi / 30 im = ax.imshow(f(x, y), animated=True) if i == 0: ax.imshow(f(x, y)) # show an initial one first diff --git a/examples/animation/multiple_axes.py b/examples/animation/multiple_axes.py new file mode 100644 index 000000000000..1fdc545d480d --- /dev/null +++ b/examples/animation/multiple_axes.py @@ -0,0 +1,79 @@ +""" +======================= +Multiple axes animation +======================= + +This example showcases: + +- how animation across multiple subplots works, +- using a figure artist in the animation. +""" + +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.animation as animation +from matplotlib.patches import ConnectionPatch + +fig, (axl, axr) = plt.subplots( + ncols=2, + sharey=True, + figsize=(6, 2), + gridspec_kw=dict(width_ratios=[1, 3], wspace=0), +) +axl.set_aspect(1) +axr.set_box_aspect(1 / 3) +axr.yaxis.set_visible(False) +axr.xaxis.set_ticks([0, np.pi, 2 * np.pi], ["0", r"$\pi$", r"$2\pi$"]) + +# draw circle with initial point in left Axes +x = np.linspace(0, 2 * np.pi, 50) +axl.plot(np.cos(x), np.sin(x), "k", lw=0.3) +point, = axl.plot(0, 0, "o") + +# draw full curve to set view limits in right Axes +sine, = axr.plot(x, np.sin(x)) + +# draw connecting line between both graphs +con = ConnectionPatch( + (1, 0), + (0, 0), + "data", + "data", + axesA=axl, + axesB=axr, + color="C0", + ls="dotted", +) +fig.add_artist(con) + + +def animate(i): + pos = np.cos(i), np.sin(i) + point.set_data(*pos) + x = np.linspace(0, i, int(i * 25 / np.pi)) + sine.set_data(x, np.sin(x)) + con.xy1 = pos + con.xy2 = i, pos[1] + return point, sine, con + + +ani = animation.FuncAnimation( + fig, + animate, + interval=50, + blit=False, # blitting can't be used with Figure artists + frames=x, + repeat_delay=100, +) + +plt.show() + +############################################################################# +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.patches.ConnectionPatch` +# - `matplotlib.animation.FuncAnimation` diff --git a/examples/animation/pause_resume.py b/examples/animation/pause_resume.py index ed20197f6167..7bed65140263 100644 --- a/examples/animation/pause_resume.py +++ b/examples/animation/pause_resume.py @@ -4,8 +4,17 @@ ================================= This example showcases: + - using the Animation.pause() method to pause an animation. - using the Animation.resume() method to resume an animation. + +.. note:: + This example exercises the interactive capabilities of Matplotlib, and this + will not appear in the static documentation. Please run this code on your + machine to see the interactivity. + + You can copy and paste individual parts, or download the entire example + using the link at the bottom of the page. """ import matplotlib.pyplot as plt diff --git a/examples/animation/random_walk.py b/examples/animation/random_walk.py index 2488f98b144c..f49c099a5c2d 100644 --- a/examples/animation/random_walk.py +++ b/examples/animation/random_walk.py @@ -13,62 +13,40 @@ np.random.seed(19680801) -def gen_rand_line(length, dims=2): - """ - Create a line using a random walk algorithm. +def random_walk(num_steps, max_step=0.05): + """Return a 3D random walk as (num_steps, 3) array.""" + start_pos = np.random.random(3) + steps = np.random.uniform(-max_step, max_step, size=(num_steps, 3)) + walk = start_pos + np.cumsum(steps, axis=0) + return walk - Parameters - ---------- - length : int - The number of points of the line. - dims : int - The number of dimensions of the line. - """ - line_data = np.empty((dims, length)) - line_data[:, 0] = np.random.rand(dims) - for index in range(1, length): - # scaling the random numbers by 0.1 so - # movement is small compared to position. - # subtraction by 0.5 is to change the range to [-0.5, 0.5] - # to allow a line to move backwards. - step = (np.random.rand(dims) - 0.5) * 0.1 - line_data[:, index] = line_data[:, index - 1] + step - return line_data - -def update_lines(num, data_lines, lines): - for line, data in zip(lines, data_lines): +def update_lines(num, walks, lines): + for line, walk in zip(lines, walks): # NOTE: there is no .set_data() for 3 dim data... - line.set_data(data[0:2, :num]) - line.set_3d_properties(data[2, :num]) + line.set_data(walk[:num, :2].T) + line.set_3d_properties(walk[:num, 2]) return lines +# Data: 40 random walks as (num_steps, 3) arrays +num_steps = 30 +walks = [random_walk(num_steps) for index in range(40)] + # Attaching 3D axis to the figure fig = plt.figure() ax = fig.add_subplot(projection="3d") -# Fifty lines of random 3-D lines -data = [gen_rand_line(25, 3) for index in range(50)] - -# Creating fifty line objects. -# NOTE: Can't pass empty arrays into 3d version of plot() -lines = [ax.plot(dat[0, 0:1], dat[1, 0:1], dat[2, 0:1])[0] for dat in data] +# Create lines initially without data +lines = [ax.plot([], [], [])[0] for _ in walks] # Setting the axes properties -ax.set_xlim3d([0.0, 1.0]) -ax.set_xlabel('X') - -ax.set_ylim3d([0.0, 1.0]) -ax.set_ylabel('Y') - -ax.set_zlim3d([0.0, 1.0]) -ax.set_zlabel('Z') - -ax.set_title('3D Test') +ax.set(xlim3d=(0, 1), xlabel='X') +ax.set(ylim3d=(0, 1), ylabel='Y') +ax.set(zlim3d=(0, 1), zlabel='Z') # Creating the Animation object -line_ani = animation.FuncAnimation( - fig, update_lines, 50, fargs=(data, lines), interval=50) +ani = animation.FuncAnimation( + fig, update_lines, num_steps, fargs=(walks, lines), interval=100) plt.show() diff --git a/examples/animation/simple_scatter.py b/examples/animation/simple_scatter.py new file mode 100644 index 000000000000..1d18039dcf11 --- /dev/null +++ b/examples/animation/simple_scatter.py @@ -0,0 +1,31 @@ +""" +============================= +Animated scatter saved as GIF +============================= + +""" +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.animation as animation + +fig, ax = plt.subplots() +ax.set_xlim([0, 10]) + +scat = ax.scatter(1, 0) +x = np.linspace(0, 10) + + +def animate(i): + scat.set_offsets((x[i], 0)) + return scat, + +ani = animation.FuncAnimation(fig, animate, repeat=True, + frames=len(x) - 1, interval=50) + +# To save the animation using Pillow as a gif +# writer = animation.PillowWriter(fps=15, +# metadata=dict(artist='Me'), +# bitrate=1800) +# ani.save('scatter.gif', writer=writer) + +plt.show() diff --git a/examples/axes_grid1/README.txt b/examples/axes_grid1/README.txt index 5c3c5d14ec96..e9afa00b9a72 100644 --- a/examples/axes_grid1/README.txt +++ b/examples/axes_grid1/README.txt @@ -2,5 +2,5 @@ .. _axes_grid1-examples-index: -Axes Grid -========= +axes_grid1 +========== diff --git a/examples/axes_grid1/demo_anchored_direction_arrows.py b/examples/axes_grid1/demo_anchored_direction_arrows.py index cdf16dc05754..24d3ddfcc4ad 100644 --- a/examples/axes_grid1/demo_anchored_direction_arrows.py +++ b/examples/axes_grid1/demo_anchored_direction_arrows.py @@ -42,7 +42,7 @@ # Rotated arrow fontprops = fm.FontProperties(family='serif') -roatated_arrow = AnchoredDirectionArrows( +rotated_arrow = AnchoredDirectionArrows( ax.transAxes, '30', '120', loc='center', @@ -50,7 +50,7 @@ angle=30, fontproperties=fontprops ) -ax.add_artist(roatated_arrow) +ax.add_artist(rotated_arrow) # Altering arrow directions a1 = AnchoredDirectionArrows( diff --git a/examples/axes_grid1/demo_axes_divider.py b/examples/axes_grid1/demo_axes_divider.py index dddc2ed4760c..c800c263a453 100644 --- a/examples/axes_grid1/demo_axes_divider.py +++ b/examples/axes_grid1/demo_axes_divider.py @@ -22,7 +22,7 @@ def demo_simple_image(ax): im = ax.imshow(Z, extent=extent) cb = plt.colorbar(im) - plt.setp(cb.ax.get_yticklabels(), visible=False) + cb.ax.yaxis.set_tick_params(labelright=False) def demo_locatable_axes_hard(fig): @@ -60,7 +60,7 @@ def demo_locatable_axes_hard(fig): im = ax.imshow(Z, extent=extent) plt.colorbar(im, cax=ax_cb) - plt.setp(ax_cb.get_yticklabels(), visible=False) + ax_cb.yaxis.set_tick_params(labelright=False) def demo_locatable_axes_easy(ax): @@ -68,7 +68,7 @@ def demo_locatable_axes_easy(ax): divider = make_axes_locatable(ax) - ax_cb = divider.new_horizontal(size="5%", pad=0.05) + ax_cb = divider.append_axes("right", size="5%", pad=0.05) fig = ax.get_figure() fig.add_axes(ax_cb) @@ -86,7 +86,7 @@ def demo_images_side_by_side(ax): divider = make_axes_locatable(ax) Z, extent = get_demo_image() - ax2 = divider.new_horizontal(size="100%", pad=0.05) + ax2 = divider.append_axes("right", size="100%", pad=0.05) fig1 = ax.get_figure() fig1.add_axes(ax2) diff --git a/examples/axes_grid1/demo_axes_grid2.py b/examples/axes_grid1/demo_axes_grid2.py index b18648186c5d..488460921471 100644 --- a/examples/axes_grid1/demo_axes_grid2.py +++ b/examples/axes_grid1/demo_axes_grid2.py @@ -58,10 +58,6 @@ def add_inner_title(ax, title, loc, **kwargs): for ax, z in zip(grid, ZS): ax.cax.toggle_label(True) - #axis = ax.cax.axis[ax.cax.orientation] - #axis.label.set_text("counts s$^{-1}$") - #axis.label.set_size(10) - #axis.major_ticklabels.set_size(6) grid[0].set_xticks([-2, 0]) grid[0].set_yticks([-2, 0, 2]) diff --git a/examples/axes_grid1/demo_axes_rgb.py b/examples/axes_grid1/demo_axes_rgb.py index 9b1ccb431d77..fa6ffdb5df3e 100644 --- a/examples/axes_grid1/demo_axes_rgb.py +++ b/examples/axes_grid1/demo_axes_rgb.py @@ -59,11 +59,8 @@ def demo_rgb2(): ax_b.imshow(im_b) for ax in fig.axes: - ax.tick_params(axis='both', direction='in') + ax.tick_params(direction='in', color='w') ax.spines[:].set_color("w") - for tick in ax.xaxis.get_major_ticks() + ax.yaxis.get_major_ticks(): - tick.tick1line.set_markeredgecolor("w") - tick.tick2line.set_markeredgecolor("w") demo_rgb1() diff --git a/examples/axes_grid1/demo_colorbar_of_inset_axes.py b/examples/axes_grid1/demo_colorbar_of_inset_axes.py index e0f31221949e..bb0023adac3f 100644 --- a/examples/axes_grid1/demo_colorbar_of_inset_axes.py +++ b/examples/axes_grid1/demo_colorbar_of_inset_axes.py @@ -1,8 +1,7 @@ """ -=========================== -Demo Colorbar of Inset Axes -=========================== - +=============================== +Adding a colorbar to inset axes +=============================== """ from matplotlib import cbook @@ -11,18 +10,15 @@ fig, ax = plt.subplots(figsize=[5, 4]) +ax.set(aspect=1, xlim=(-15, 15), ylim=(-20, 5)) Z = cbook.get_sample_data("axes_grid/bivariate_normal.npy", np_load=True) extent = (-3, 4, -4, 3) -ax.set(aspect=1, xlim=(-15, 15), ylim=(-20, 5)) - axins = zoomed_inset_axes(ax, zoom=2, loc='upper left') +axins.set(xticks=[], yticks=[]) im = axins.imshow(Z, extent=extent, origin="lower") -plt.xticks(visible=False) -plt.yticks(visible=False) - # colorbar cax = inset_axes(axins, width="5%", # width = 10% of parent_bbox width diff --git a/examples/axes_grid1/demo_colorbar_with_axes_divider.py b/examples/axes_grid1/demo_colorbar_with_axes_divider.py index 9ab4640ee8b0..920db5703b98 100644 --- a/examples/axes_grid1/demo_colorbar_with_axes_divider.py +++ b/examples/axes_grid1/demo_colorbar_with_axes_divider.py @@ -18,13 +18,13 @@ im1 = ax1.imshow([[1, 2], [3, 4]]) ax1_divider = make_axes_locatable(ax1) -# Add an axes to the right of the main axes. +# Add an Axes to the right of the main Axes. cax1 = ax1_divider.append_axes("right", size="7%", pad="2%") cb1 = fig.colorbar(im1, cax=cax1) im2 = ax2.imshow([[1, 2], [3, 4]]) ax2_divider = make_axes_locatable(ax2) -# Add an axes above the main axes. +# Add an Axes above the main Axes. cax2 = ax2_divider.append_axes("top", size="7%", pad="2%") cb2 = fig.colorbar(im2, cax=cax2, orientation="horizontal") # Change tick position to top (with the default tick position "bottom", ticks diff --git a/examples/axes_grid1/demo_colorbar_with_inset_locator.py b/examples/axes_grid1/demo_colorbar_with_inset_locator.py index 94d435d6b288..87939800021d 100644 --- a/examples/axes_grid1/demo_colorbar_with_inset_locator.py +++ b/examples/axes_grid1/demo_colorbar_with_inset_locator.py @@ -6,40 +6,37 @@ This example shows how to control the position, height, and width of colorbars using `~mpl_toolkits.axes_grid1.inset_locator.inset_axes`. -Controlling the placement of the inset axes is done similarly as that of the -legend: either by providing a location option ("upper right", "best", ...), or -by providing a locator with respect to the parent bbox. - +Inset axes placement is controlled as for legends: either by providing a *loc* +option ("upper right", "best", ...), or by providing a locator with respect to +the parent bbox. Parameters such as *bbox_to_anchor* and *borderpad* likewise +work in the same way, and are also demonstrated here. """ -import matplotlib.pyplot as plt +import matplotlib.pyplot as plt from mpl_toolkits.axes_grid1.inset_locator import inset_axes fig, (ax1, ax2) = plt.subplots(1, 2, figsize=[6, 3]) -axins1 = inset_axes(ax1, - width="50%", # width = 50% of parent_bbox width - height="5%", # height : 5% - loc='upper right') - im1 = ax1.imshow([[1, 2], [2, 3]]) -fig.colorbar(im1, cax=axins1, orientation="horizontal", ticks=[1, 2, 3]) +axins1 = inset_axes( + ax1, + width="50%", # width: 50% of parent_bbox width + height="5%", # height: 5% + loc="upper right", +) axins1.xaxis.set_ticks_position("bottom") - -axins = inset_axes(ax2, - width="5%", # width = 5% of parent_bbox width - height="50%", # height : 50% - loc='lower left', - bbox_to_anchor=(1.05, 0., 1, 1), - bbox_transform=ax2.transAxes, - borderpad=0, - ) - -# Controlling the placement of the inset axes is basically same as that -# of the legend. you may want to play with the borderpad value and -# the bbox_to_anchor coordinate. +fig.colorbar(im1, cax=axins1, orientation="horizontal", ticks=[1, 2, 3]) im = ax2.imshow([[1, 2], [2, 3]]) +axins = inset_axes( + ax2, + width="5%", # width: 5% of parent_bbox width + height="50%", # height: 50% + loc="lower left", + bbox_to_anchor=(1.05, 0., 1, 1), + bbox_transform=ax2.transAxes, + borderpad=0, +) fig.colorbar(im, cax=axins, ticks=[1, 2, 3]) plt.show() diff --git a/examples/axes_grid1/demo_edge_colorbar.py b/examples/axes_grid1/demo_edge_colorbar.py index 74dc74c56965..17dfd952992c 100644 --- a/examples/axes_grid1/demo_edge_colorbar.py +++ b/examples/axes_grid1/demo_edge_colorbar.py @@ -1,7 +1,7 @@ """ -================== -Demo Edge Colorbar -================== +=============================== +Per-row or per-column colorbars +=============================== This example shows how to use one common colorbar for each row or column of an image grid. @@ -35,7 +35,7 @@ def demo_bottom_cbar(fig): ) Z, extent = get_demo_image() - cmaps = [plt.get_cmap("autumn"), plt.get_cmap("summer")] + cmaps = ["autumn", "summer"] for i in range(4): im = grid[i].imshow(Z, extent=extent, cmap=cmaps[i//2]) if i % 2: @@ -65,7 +65,7 @@ def demo_right_cbar(fig): cbar_pad="2%", ) Z, extent = get_demo_image() - cmaps = [plt.get_cmap("spring"), plt.get_cmap("winter")] + cmaps = ["spring", "winter"] for i in range(4): im = grid[i].imshow(Z, extent=extent, cmap=cmaps[i//2]) if i % 2: diff --git a/examples/axes_grid1/demo_fixed_size_axes.py b/examples/axes_grid1/demo_fixed_size_axes.py index 41d138b1f453..85ab092982e4 100644 --- a/examples/axes_grid1/demo_fixed_size_axes.py +++ b/examples/axes_grid1/demo_fixed_size_axes.py @@ -1,7 +1,7 @@ """ -==================== -Demo Fixed Size Axes -==================== +=============================== +Axes with a fixed physical size +=============================== """ import matplotlib.pyplot as plt diff --git a/examples/axes_grid1/demo_imagegrid_aspect.py b/examples/axes_grid1/demo_imagegrid_aspect.py index 3369777460a5..5c29b802f1a0 100644 --- a/examples/axes_grid1/demo_imagegrid_aspect.py +++ b/examples/axes_grid1/demo_imagegrid_aspect.py @@ -1,25 +1,21 @@ """ -===================== -Demo Imagegrid Aspect -===================== - +========================================= +Setting a fixed aspect on ImageGrid cells +========================================= """ -import matplotlib.pyplot as plt +import matplotlib.pyplot as plt from mpl_toolkits.axes_grid1 import ImageGrid + fig = plt.figure() grid1 = ImageGrid(fig, 121, (2, 2), axes_pad=0.1, aspect=True, share_all=True) - for i in [0, 1]: grid1[i].set_aspect(2) - grid2 = ImageGrid(fig, 122, (2, 2), axes_pad=0.1, aspect=True, share_all=True) - - for i in [1, 3]: grid2[i].set_aspect(2) diff --git a/examples/axes_grid1/inset_locator_demo.py b/examples/axes_grid1/inset_locator_demo.py index 56b5a8357e10..974783c04309 100644 --- a/examples/axes_grid1/inset_locator_demo.py +++ b/examples/axes_grid1/inset_locator_demo.py @@ -69,7 +69,7 @@ bbox_transform=ax.transAxes, loc=3) # For visualization purposes we mark the bounding box by a rectangle -ax.add_patch(plt.Rectangle((.2, .4), .6, .5, ls="--", ec="c", fc="None", +ax.add_patch(plt.Rectangle((.2, .4), .6, .5, ls="--", ec="c", fc="none", transform=ax.transAxes)) # We set the axis limits to something other than the default, in order to not @@ -89,9 +89,9 @@ bbox_transform=ax3.transAxes) # For visualization purposes we mark the bounding box by a rectangle -ax2.add_patch(plt.Rectangle((0, 0), 1, 1, ls="--", lw=2, ec="c", fc="None")) +ax2.add_patch(plt.Rectangle((0, 0), 1, 1, ls="--", lw=2, ec="c", fc="none")) ax3.add_patch(plt.Rectangle((.7, .5), .3, .5, ls="--", lw=2, - ec="c", fc="None")) + ec="c", fc="none")) # Turn ticklabels off for axi in [axins2, axins3, ax2, ax3]: @@ -135,7 +135,7 @@ # Create an inset horizontally centered in figure coordinates and vertically # bound to line up with the axes. -from matplotlib.transforms import blended_transform_factory +from matplotlib.transforms import blended_transform_factory # noqa transform = blended_transform_factory(fig.transFigure, ax2.transAxes) axins4 = inset_axes(ax2, width="16%", height="34%", bbox_to_anchor=(0, 0, 1, 1), diff --git a/examples/axes_grid1/inset_locator_demo2.py b/examples/axes_grid1/inset_locator_demo2.py index 509f65510438..795cb9471bdb 100644 --- a/examples/axes_grid1/inset_locator_demo2.py +++ b/examples/axes_grid1/inset_locator_demo2.py @@ -3,10 +3,10 @@ Inset Locator Demo2 =================== -This Demo shows how to create a zoomed inset via `~.zoomed_inset_axes`. -In the first subplot an `~.AnchoredSizeBar` shows the zoom effect. +This Demo shows how to create a zoomed inset via `.zoomed_inset_axes`. +In the first subplot an `.AnchoredSizeBar` shows the zoom effect. In the second subplot a connection to the region of interest is -created via `~.mark_inset`. +created via `.mark_inset`. """ from matplotlib import cbook @@ -32,9 +32,7 @@ def get_demo_image(): # fix the number of ticks on the inset axes axins.yaxis.get_major_locator().set_params(nbins=7) axins.xaxis.get_major_locator().set_params(nbins=7) - -plt.setp(axins.get_xticklabels(), visible=False) -plt.setp(axins.get_yticklabels(), visible=False) +axins.tick_params(labelleft=False, labelbottom=False) def add_sizebar(ax, size): @@ -69,9 +67,7 @@ def add_sizebar(ax, size): # fix the number of ticks on the inset axes axins2.yaxis.get_major_locator().set_params(nbins=7) axins2.xaxis.get_major_locator().set_params(nbins=7) - -plt.setp(axins2.get_xticklabels(), visible=False) -plt.setp(axins2.get_yticklabels(), visible=False) +axins2.tick_params(labelleft=False, labelbottom=False) # draw a bbox of the region of the inset axes in the parent axes and # connecting lines between the bbox and the inset axes area diff --git a/examples/axes_grid1/make_room_for_ylabel_using_axesgrid.py b/examples/axes_grid1/make_room_for_ylabel_using_axesgrid.py index af5946535cfb..802af8739b97 100644 --- a/examples/axes_grid1/make_room_for_ylabel_using_axesgrid.py +++ b/examples/axes_grid1/make_room_for_ylabel_using_axesgrid.py @@ -1,8 +1,7 @@ """ -=================================== -Make Room For Ylabel Using Axesgrid -=================================== - +==================================== +Make room for ylabel using axes_grid +==================================== """ import matplotlib.pyplot as plt @@ -11,23 +10,20 @@ from mpl_toolkits.axes_grid1.axes_divider import make_axes_area_auto_adjustable -plt.figure() -ax = plt.axes([0, 0, 1, 1]) +fig = plt.figure() +ax = fig.add_axes([0, 0, 1, 1]) -ax.set_yticks([0.5]) -ax.set_yticklabels(["very long label"]) +ax.set_yticks([0.5], labels=["very long label"]) make_axes_area_auto_adjustable(ax) ############################################################################### +fig = plt.figure() +ax1 = fig.add_axes([0, 0, 1, 0.5]) +ax2 = fig.add_axes([0, 0.5, 1, 0.5]) -plt.figure() -ax1 = plt.axes([0, 0, 1, 0.5]) -ax2 = plt.axes([0, 0.5, 1, 0.5]) - -ax1.set_yticks([0.5]) -ax1.set_yticklabels(["very long label"]) +ax1.set_yticks([0.5], labels=["very long label"]) ax1.set_ylabel("Y label") ax2.set_title("Title") @@ -37,12 +33,11 @@ ############################################################################### - fig = plt.figure() -ax1 = plt.axes([0, 0, 1, 1]) +ax1 = fig.add_axes([0, 0, 1, 1]) divider = make_axes_locatable(ax1) -ax2 = divider.new_horizontal("100%", pad=0.3, sharey=ax1) +ax2 = divider.append_axes("right", "100%", pad=0.3, sharey=ax1) ax2.tick_params(labelleft=False) fig.add_axes(ax2) @@ -53,8 +48,7 @@ divider.add_auto_adjustable_area(use_axes=[ax1, ax2], pad=0.1, adjust_dirs=["top", "bottom"]) -ax1.set_yticks([0.5]) -ax1.set_yticklabels(["very long label"]) +ax1.set_yticks([0.5], labels=["very long label"]) ax2.set_title("Title") ax2.set_xlabel("X - Label") diff --git a/examples/axes_grid1/parasite_simple.py b/examples/axes_grid1/parasite_simple.py index c76f70ee9d74..36f36f9c997e 100644 --- a/examples/axes_grid1/parasite_simple.py +++ b/examples/axes_grid1/parasite_simple.py @@ -2,13 +2,12 @@ =============== Parasite Simple =============== - """ -from mpl_toolkits.axes_grid1 import host_subplot + import matplotlib.pyplot as plt +from mpl_toolkits.axes_grid1 import host_subplot host = host_subplot(111) - par = host.twinx() host.set_xlabel("Distance") @@ -18,12 +17,9 @@ p1, = host.plot([0, 1, 2], [0, 1, 2], label="Density") p2, = par.plot([0, 1, 2], [0, 3, 2], label="Temperature") -leg = plt.legend() +host.legend(labelcolor="linecolor") host.yaxis.get_label().set_color(p1.get_color()) -leg.texts[0].set_color(p1.get_color()) - par.yaxis.get_label().set_color(p2.get_color()) -leg.texts[1].set_color(p2.get_color()) plt.show() diff --git a/examples/axes_grid1/scatter_hist_locatable_axes.py b/examples/axes_grid1/scatter_hist_locatable_axes.py index 60dec95d71b9..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,17 +65,12 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown -# in this example: - -import matplotlib -import mpl_toolkits -mpl_toolkits.axes_grid1.axes_divider.make_axes_locatable -matplotlib.axes.Axes.set_aspect -matplotlib.axes.Axes.scatter -matplotlib.axes.Axes.hist +# - `mpl_toolkits.axes_grid1.axes_divider.make_axes_locatable` +# - `matplotlib.axes.Axes.set_aspect` +# - `matplotlib.axes.Axes.scatter` +# - `matplotlib.axes.Axes.hist` diff --git a/examples/axes_grid1/simple_anchored_artists.py b/examples/axes_grid1/simple_anchored_artists.py index 343d8d3aaa13..212f9797a7f7 100644 --- a/examples/axes_grid1/simple_anchored_artists.py +++ b/examples/axes_grid1/simple_anchored_artists.py @@ -73,7 +73,7 @@ def draw_sizebar(ax): ax.add_artist(asb) -ax = plt.gca() +fig, ax = plt.subplots() ax.set_aspect(1.) draw_text(ax) diff --git a/examples/axes_grid1/simple_axes_divider1.py b/examples/axes_grid1/simple_axes_divider1.py index 1ca9fd463d81..0ead9c27ae9f 100644 --- a/examples/axes_grid1/simple_axes_divider1.py +++ b/examples/axes_grid1/simple_axes_divider1.py @@ -3,16 +3,26 @@ Simple Axes Divider 1 ===================== +See also :doc:`/tutorials/toolkits/axes_grid`. """ from mpl_toolkits.axes_grid1 import Size, Divider import matplotlib.pyplot as plt +def label_axes(ax, text): + """Place a label at the center of an Axes, and remove the axis ticks.""" + ax.text(.5, .5, text, transform=ax.transAxes, + horizontalalignment="center", verticalalignment="center") + ax.tick_params(bottom=False, labelbottom=False, + left=False, labelleft=False) + + ############################################################################## # Fixed axes sizes; fixed paddings. fig = plt.figure(figsize=(6, 6)) +fig.suptitle("Fixed axes sizes, fixed paddings") # Sizes are in inches. horiz = [Size.Fixed(1.), Size.Fixed(.5), Size.Fixed(1.5), Size.Fixed(.5)] @@ -20,36 +30,39 @@ rect = (0.1, 0.1, 0.8, 0.8) # Divide the axes rectangle into a grid with sizes specified by horiz * vert. -divider = Divider(fig, rect, horiz, vert, aspect=False) +div = Divider(fig, rect, horiz, vert, aspect=False) # The rect parameter will actually be ignored and overridden by axes_locator. -ax1 = fig.add_axes(rect, axes_locator=divider.new_locator(nx=0, ny=0)) -ax2 = fig.add_axes(rect, axes_locator=divider.new_locator(nx=0, ny=2)) -ax3 = fig.add_axes(rect, axes_locator=divider.new_locator(nx=2, ny=2)) -ax4 = fig.add_axes(rect, axes_locator=divider.new_locator(nx=2, nx1=4, ny=0)) - -for ax in fig.axes: - ax.tick_params(labelbottom=False, labelleft=False) +ax1 = fig.add_axes(rect, axes_locator=div.new_locator(nx=0, ny=0)) +label_axes(ax1, "nx=0, ny=0") +ax2 = fig.add_axes(rect, axes_locator=div.new_locator(nx=0, ny=2)) +label_axes(ax2, "nx=0, ny=2") +ax3 = fig.add_axes(rect, axes_locator=div.new_locator(nx=2, ny=2)) +label_axes(ax3, "nx=2, ny=2") +ax4 = fig.add_axes(rect, axes_locator=div.new_locator(nx=2, nx1=4, ny=0)) +label_axes(ax4, "nx=2, nx1=4, ny=0") ############################################################################## # Axes sizes that scale with the figure size; fixed paddings. fig = plt.figure(figsize=(6, 6)) +fig.suptitle("Scalable axes sizes, fixed paddings") horiz = [Size.Scaled(1.5), Size.Fixed(.5), Size.Scaled(1.), Size.Scaled(.5)] vert = [Size.Scaled(1.), Size.Fixed(.5), Size.Scaled(1.5)] rect = (0.1, 0.1, 0.8, 0.8) # Divide the axes rectangle into a grid with sizes specified by horiz * vert. -divider = Divider(fig, rect, horiz, vert, aspect=False) +div = Divider(fig, rect, horiz, vert, aspect=False) # The rect parameter will actually be ignored and overridden by axes_locator. -ax1 = fig.add_axes(rect, axes_locator=divider.new_locator(nx=0, ny=0)) -ax2 = fig.add_axes(rect, axes_locator=divider.new_locator(nx=0, ny=2)) -ax3 = fig.add_axes(rect, axes_locator=divider.new_locator(nx=2, ny=2)) -ax4 = fig.add_axes(rect, axes_locator=divider.new_locator(nx=2, nx1=4, ny=0)) - -for ax in fig.axes: - ax.tick_params(labelbottom=False, labelleft=False) +ax1 = fig.add_axes(rect, axes_locator=div.new_locator(nx=0, ny=0)) +label_axes(ax1, "nx=0, ny=0") +ax2 = fig.add_axes(rect, axes_locator=div.new_locator(nx=0, ny=2)) +label_axes(ax2, "nx=0, ny=2") +ax3 = fig.add_axes(rect, axes_locator=div.new_locator(nx=2, ny=2)) +label_axes(ax3, "nx=2, ny=2") +ax4 = fig.add_axes(rect, axes_locator=div.new_locator(nx=2, nx1=4, ny=0)) +label_axes(ax4, "nx=2, nx1=4, ny=0") plt.show() diff --git a/examples/axes_grid1/simple_axes_divider3.py b/examples/axes_grid1/simple_axes_divider3.py index 6b7d9b4dd096..624a3be359f8 100644 --- a/examples/axes_grid1/simple_axes_divider3.py +++ b/examples/axes_grid1/simple_axes_divider3.py @@ -3,7 +3,9 @@ Simple Axes Divider 3 ===================== +See also :doc:`/tutorials/toolkits/axes_grid`. """ + import mpl_toolkits.axes_grid1.axes_size as Size from mpl_toolkits.axes_grid1 import Divider import matplotlib.pyplot as plt diff --git a/examples/axes_grid1/simple_axisline4.py b/examples/axes_grid1/simple_axisline4.py index 91b76cf3e956..4d93beb2b0d0 100644 --- a/examples/axes_grid1/simple_axisline4.py +++ b/examples/axes_grid1/simple_axisline4.py @@ -13,9 +13,9 @@ ax.plot(xx, np.sin(xx)) ax2 = ax.twin() # ax2 is responsible for "top" axis and "right" axis -ax2.set_xticks([0., .5*np.pi, np.pi, 1.5*np.pi, 2*np.pi]) -ax2.set_xticklabels(["$0$", r"$\frac{1}{2}\pi$", - r"$\pi$", r"$\frac{3}{2}\pi$", r"$2\pi$"]) +ax2.set_xticks([0., .5*np.pi, np.pi, 1.5*np.pi, 2*np.pi], + labels=["$0$", r"$\frac{1}{2}\pi$", + r"$\pi$", r"$\frac{3}{2}\pi$", r"$2\pi$"]) ax2.axis["right"].major_ticklabels.set_visible(False) ax2.axis["top"].major_ticklabels.set_visible(True) diff --git a/examples/axes_grid1/simple_colorbar.py b/examples/axes_grid1/simple_colorbar.py index d7ca58046a53..41a34eefcab1 100644 --- a/examples/axes_grid1/simple_colorbar.py +++ b/examples/axes_grid1/simple_colorbar.py @@ -11,7 +11,7 @@ ax = plt.subplot() im = ax.imshow(np.arange(100).reshape((10, 10))) -# create an axes on the right side of ax. The width of cax will be 5% +# create an Axes on the right side of ax. The width of cax will be 5% # of ax and the padding between cax and ax will be fixed at 0.05 inch. divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="5%", pad=0.05) diff --git a/examples/axisartist/README.txt b/examples/axisartist/README.txt index d6b190aedaf0..dc7c87d3a608 100644 --- a/examples/axisartist/README.txt +++ b/examples/axisartist/README.txt @@ -2,5 +2,5 @@ .. _axisartist-examples-index: -Axis Artist -=========== +axisartist +========== diff --git a/examples/axisartist/demo_axis_direction.py b/examples/axisartist/demo_axis_direction.py index 81fd7a6126bf..f8c03205e732 100644 --- a/examples/axisartist/demo_axis_direction.py +++ b/examples/axisartist/demo_axis_direction.py @@ -1,8 +1,7 @@ """ =================== -Demo Axis Direction +axis_direction demo =================== - """ import numpy as np diff --git a/examples/axisartist/demo_axisline_style.py b/examples/axisartist/demo_axisline_style.py index 1427a90952a1..c7270941dadf 100644 --- a/examples/axisartist/demo_axisline_style.py +++ b/examples/axisartist/demo_axisline_style.py @@ -7,8 +7,8 @@ Note: The `mpl_toolkits.axisartist` axes classes may be confusing for new users. If the only aim is to obtain arrow heads at the ends of the axes, -rather check out the -:doc:`/gallery/ticks_and_spines/centered_spines_with_arrows` example. +rather check out the :doc:`/gallery/spines/centered_spines_with_arrows` +example. """ from mpl_toolkits.axisartist.axislines import AxesZero diff --git a/examples/axisartist/demo_curvelinear_grid.py b/examples/axisartist/demo_curvelinear_grid.py index 91045ee786ab..31f6f514a623 100644 --- a/examples/axisartist/demo_curvelinear_grid.py +++ b/examples/axisartist/demo_curvelinear_grid.py @@ -27,24 +27,17 @@ def curvelinear_test1(fig): Grid for custom transform. """ - def tr(x, y): - x, y = np.asarray(x), np.asarray(y) - return x, y - x - - def inv_tr(x, y): - x, y = np.asarray(x), np.asarray(y) - return x, y + x + def tr(x, y): return x, y - x + def inv_tr(x, y): return x, y + x grid_helper = GridHelperCurveLinear((tr, inv_tr)) ax1 = fig.add_subplot(1, 2, 1, axes_class=Axes, grid_helper=grid_helper) - # ax1 will have a ticks and gridlines defined by the given - # transform (+ transData of the Axes). Note that the transform of - # the Axes itself (i.e., transData) is not affected by the given - # transform. - - xx, yy = tr([3, 6], [5, 10]) - ax1.plot(xx, yy, linewidth=2.0) + # ax1 will have ticks and gridlines defined by the given transform (+ + # transData of the Axes). Note that the transform of the Axes itself + # (i.e., transData) is not affected by the given transform. + xx, yy = tr(np.array([3, 6]), np.array([5, 10])) + ax1.plot(xx, yy) ax1.set_aspect(1) ax1.set_xlim(0, 10) diff --git a/examples/axisartist/demo_curvelinear_grid2.py b/examples/axisartist/demo_curvelinear_grid2.py index cc07a32d1805..82944ef92575 100644 --- a/examples/axisartist/demo_curvelinear_grid2.py +++ b/examples/axisartist/demo_curvelinear_grid2.py @@ -24,14 +24,10 @@ def curvelinear_test1(fig): """Grid for custom transform.""" def tr(x, y): - sgn = np.sign(x) - x, y = np.abs(np.asarray(x)), np.asarray(y) - return sgn*x**.5, y + return np.sign(x)*abs(x)**.5, y def inv_tr(x, y): - sgn = np.sign(x) - x, y = np.asarray(x), np.asarray(y) - return sgn*x**2, y + return np.sign(x)*x**2, y grid_helper = GridHelperCurveLinear( (tr, inv_tr), diff --git a/examples/axisartist/demo_floating_axes.py b/examples/axisartist/demo_floating_axes.py index 6920ddca5233..06ec6934366c 100644 --- a/examples/axisartist/demo_floating_axes.py +++ b/examples/axisartist/demo_floating_axes.py @@ -41,6 +41,7 @@ def setup_axes1(fig, rect): ax1 = fig.add_subplot( rect, axes_class=floating_axes.FloatingAxes, grid_helper=grid_helper) + ax1.grid() aux_ax = ax1.get_aux_axes(tr) @@ -72,6 +73,7 @@ def setup_axes2(fig, rect): ax1 = fig.add_subplot( rect, axes_class=floating_axes.FloatingAxes, grid_helper=grid_helper) + ax1.grid() # create a parasite axes whose transData in RA, cz aux_ax = ax1.get_aux_axes(tr) @@ -129,6 +131,7 @@ def setup_axes3(fig, rect): ax1.axis["left"].label.set_text(r"cz [km$^{-1}$]") ax1.axis["top"].label.set_text(r"$\alpha_{1950}$") + ax1.grid() # create a parasite axes whose transData in RA, cz aux_ax = ax1.get_aux_axes(tr) diff --git a/examples/axisartist/demo_floating_axis.py b/examples/axisartist/demo_floating_axis.py index 36de8ce87dae..f607907c2654 100644 --- a/examples/axisartist/demo_floating_axis.py +++ b/examples/axisartist/demo_floating_axis.py @@ -1,14 +1,15 @@ """ ================== -Demo Floating Axis +floating_axis demo ================== -Axis within rectangular frame +Axis within rectangular frame. The following code demonstrates how to put a floating polar curve within a rectangular box. In order to get a better sense of polar curves, please look at :doc:`/gallery/axisartist/demo_curvelinear_grid`. """ + import numpy as np import matplotlib.pyplot as plt import mpl_toolkits.axisartist.angle_helper as angle_helper @@ -28,8 +29,7 @@ def curvelinear_test2(fig): lon_cycle=360, lat_cycle=None, lon_minmax=None, - lat_minmax=(0, - np.inf), + lat_minmax=(0, np.inf), ) grid_locator1 = angle_helper.LocatorDMS(12) diff --git a/examples/axisartist/demo_parasite_axes.py b/examples/axisartist/demo_parasite_axes.py index ef7d5ca5268d..0b7858f645f3 100644 --- a/examples/axisartist/demo_parasite_axes.py +++ b/examples/axisartist/demo_parasite_axes.py @@ -10,7 +10,7 @@ `mpl_toolkits.axes_grid1.parasite_axes.ParasiteAxes`. An alternative approach using standard Matplotlib subplots is shown in the -:doc:`/gallery/ticks_and_spines/multiple_yaxis_with_spines` example. +:doc:`/gallery/spines/multiple_yaxis_with_spines` example. An alternative approach using :mod:`mpl_toolkits.axes_grid1` and :mod:`mpl_toolkits.axisartist` is found in the diff --git a/examples/axisartist/demo_parasite_axes2.py b/examples/axisartist/demo_parasite_axes2.py index 3c23e37b7ae7..651cdd032ae5 100644 --- a/examples/axisartist/demo_parasite_axes2.py +++ b/examples/axisartist/demo_parasite_axes2.py @@ -19,7 +19,7 @@ `~.mpl_toolkits.axes_grid1.parasite_axes.ParasiteAxes` is the :doc:`/gallery/axisartist/demo_parasite_axes` example. An alternative approach using the usual Matplotlib subplots is shown in -the :doc:`/gallery/ticks_and_spines/multiple_yaxis_with_spines` example. +the :doc:`/gallery/spines/multiple_yaxis_with_spines` example. """ from mpl_toolkits.axes_grid1 import host_subplot diff --git a/examples/axisartist/demo_ticklabel_alignment.py b/examples/axisartist/demo_ticklabel_alignment.py index 928b3c71a64d..e6d2d0c6ae59 100644 --- a/examples/axisartist/demo_ticklabel_alignment.py +++ b/examples/axisartist/demo_ticklabel_alignment.py @@ -12,10 +12,8 @@ def setup_axes(fig, pos): ax = fig.add_subplot(pos, axes_class=axisartist.Axes) - ax.set_yticks([0.2, 0.8]) - ax.set_yticklabels(["short", "loooong"]) - ax.set_xticks([0.2, 0.8]) - ax.set_xticklabels([r"$\frac{1}{2}\pi$", r"$\pi$"]) + ax.set_yticks([0.2, 0.8], labels=["short", "loooong"]) + ax.set_xticks([0.2, 0.8], labels=[r"$\frac{1}{2}\pi$", r"$\pi$"]) return ax diff --git a/examples/axisartist/simple_axisartist1.py b/examples/axisartist/simple_axisartist1.py index 9e49a3b12a71..95d710cbe0b1 100644 --- a/examples/axisartist/simple_axisartist1.py +++ b/examples/axisartist/simple_axisartist1.py @@ -1,26 +1,52 @@ """ -================== -Simple Axisartist1 -================== +============================= +Custom spines with axisartist +============================= +This example showcases the use of :mod:`.axisartist` to draw spines at custom +positions (here, at ``y = 0``). + +Note, however, that it is simpler to achieve this effect using standard +`.Spine` methods, as demonstrated in +:doc:`/gallery/spines/centered_spines_with_arrows`. + +.. redirect-from:: /gallery/axisartist/simple_axisline2 """ + import matplotlib.pyplot as plt from mpl_toolkits import axisartist +import numpy as np + + +fig = plt.figure(figsize=(6, 3), constrained_layout=True) +# To construct axes of two different classes, we need to use gridspec (or +# MATLAB-style add_subplot calls). +gs = fig.add_gridspec(1, 2) + + +ax0 = fig.add_subplot(gs[0, 0], axes_class=axisartist.Axes) +# Make a new axis along the first (x) axis which passes through y=0. +ax0.axis["y=0"] = ax0.new_floating_axis(nth_coord=0, value=0, + axis_direction="bottom") +ax0.axis["y=0"].toggle(all=True) +ax0.axis["y=0"].label.set_text("y = 0") +# Make other axis invisible. +ax0.axis["bottom", "top", "right"].set_visible(False) -fig = plt.figure() -fig.subplots_adjust(right=0.85) -ax = fig.add_subplot(axes_class=axisartist.Axes) -# make some axis invisible -ax.axis["bottom", "top", "right"].set_visible(False) +# Alternatively, one can use AxesZero, which automatically sets up two +# additional axis, named "xzero" (the y=0 axis) and "yzero" (the x=0 axis). +ax1 = fig.add_subplot(gs[0, 1], axes_class=axisartist.axislines.AxesZero) +# "xzero" and "yzero" default to invisible; make xzero axis visible. +ax1.axis["xzero"].set_visible(True) +ax1.axis["xzero"].label.set_text("Axis Zero") +# Make other axis invisible. +ax1.axis["bottom", "top", "right"].set_visible(False) -# make an new axis along the first axis axis (x-axis) which pass -# through y=0. -ax.axis["y=0"] = ax.new_floating_axis(nth_coord=0, value=0, - axis_direction="bottom") -ax.axis["y=0"].toggle(all=True) -ax.axis["y=0"].label.set_text("y = 0") -ax.set_ylim(-2, 4) +# Draw some sample data. +x = np.arange(0, 2*np.pi, 0.01) +ax0.plot(x, np.sin(x)) +ax1.plot(x, np.sin(x)) plt.show() diff --git a/examples/axisartist/simple_axisline.py b/examples/axisartist/simple_axisline.py index 7623763f1ea7..da0aad34a28a 100644 --- a/examples/axisartist/simple_axisline.py +++ b/examples/axisartist/simple_axisline.py @@ -24,9 +24,9 @@ ax.set_ylim(-2, 4) ax.set_xlabel("Label X") ax.set_ylabel("Label Y") -# or -#ax.axis["bottom"].label.set_text("Label X") -#ax.axis["left"].label.set_text("Label Y") +# Or: +# ax.axis["bottom"].label.set_text("Label X") +# ax.axis["left"].label.set_text("Label Y") # make new (right-side) yaxis, but with some offset ax.axis["right2"] = ax.new_fixed_axis(loc="right", offset=(20, 0)) diff --git a/examples/axisartist/simple_axisline2.py b/examples/axisartist/simple_axisline2.py deleted file mode 100644 index ecd49a16de77..000000000000 --- a/examples/axisartist/simple_axisline2.py +++ /dev/null @@ -1,28 +0,0 @@ -""" -================ -Simple Axisline2 -================ - -""" -import matplotlib.pyplot as plt -from mpl_toolkits.axisartist.axislines import AxesZero -import numpy as np - -fig = plt.figure(figsize=(4, 3)) - -# a subplot with two additional axis, "xzero" and "yzero". "xzero" is -# y=0 line, and "yzero" is x=0 line. -ax = fig.add_subplot(axes_class=AxesZero) - -# make xzero axis (horizontal axis line through y=0) visible. -ax.axis["xzero"].set_visible(True) -ax.axis["xzero"].label.set_text("Axis Zero") - -# make other axis (bottom, top, right) invisible. -for n in ["bottom", "top", "right"]: - ax.axis[n].set_visible(False) - -xx = np.arange(0, 2*np.pi, 0.01) -ax.plot(xx, np.sin(xx)) - -plt.show() diff --git a/examples/color/color_by_yvalue.py b/examples/color/color_by_yvalue.py index 79d18ab0919b..585fe4dc86f8 100644 --- a/examples/color/color_by_yvalue.py +++ b/examples/color/color_by_yvalue.py @@ -24,14 +24,9 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.plot -matplotlib.pyplot.plot +# - `matplotlib.axes.Axes.plot` / `matplotlib.pyplot.plot` diff --git a/examples/color/color_cycle_default.py b/examples/color/color_cycle_default.py index 8de0048b54a9..d098821d3306 100644 --- a/examples/color/color_cycle_default.py +++ b/examples/color/color_cycle_default.py @@ -42,18 +42,12 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.axhline -matplotlib.axes.Axes.axvline -matplotlib.pyplot.axhline -matplotlib.pyplot.axvline -matplotlib.axes.Axes.set_facecolor -matplotlib.figure.Figure.suptitle +# - `matplotlib.axes.Axes.axhline` / `matplotlib.pyplot.axhline` +# - `matplotlib.axes.Axes.axvline` / `matplotlib.pyplot.axvline` +# - `matplotlib.axes.Axes.set_facecolor` +# - `matplotlib.figure.Figure.suptitle` diff --git a/examples/color/color_demo.py b/examples/color/color_demo.py index 0ad1c11edfa9..8a161442184b 100644 --- a/examples/color/color_demo.py +++ b/examples/color/color_demo.py @@ -48,9 +48,9 @@ # 3) gray level string: ax.set_title('Voltage vs. time chart', color='0.7') # 4) single letter color string -ax.set_xlabel('time (s)', color='c') +ax.set_xlabel('Time [s]', color='c') # 5) a named color: -ax.set_ylabel('voltage (mV)', color='peachpuff') +ax.set_ylabel('Voltage [mV]', color='peachpuff') # 6) a named xkcd color: ax.plot(t, s, 'xkcd:crimson') # 7) Cn notation: @@ -63,19 +63,15 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.colors -matplotlib.axes.Axes.plot -matplotlib.axes.Axes.set_facecolor -matplotlib.axes.Axes.set_title -matplotlib.axes.Axes.set_xlabel -matplotlib.axes.Axes.set_ylabel -matplotlib.axes.Axes.tick_params +# - `matplotlib.colors` +# - `matplotlib.axes.Axes.plot` +# - `matplotlib.axes.Axes.set_facecolor` +# - `matplotlib.axes.Axes.set_title` +# - `matplotlib.axes.Axes.set_xlabel` +# - `matplotlib.axes.Axes.set_ylabel` +# - `matplotlib.axes.Axes.tick_params` diff --git a/examples/color/colorbar_basics.py b/examples/color/colorbar_basics.py index e1815b1da6f3..f31ded0100c7 100644 --- a/examples/color/colorbar_basics.py +++ b/examples/color/colorbar_basics.py @@ -3,8 +3,8 @@ Colorbar ======== -Use `~.figure.Figure.colorbar` by specifying the mappable object (here -the `~.matplotlib.image.AxesImage` returned by `~.axes.Axes.imshow`) +Use `~.Figure.colorbar` by specifying the mappable object (here +the `.AxesImage` returned by `~.axes.Axes.imshow`) and the axes to attach the colorbar to. """ @@ -47,19 +47,12 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -import matplotlib.colorbar -matplotlib.axes.Axes.imshow -matplotlib.pyplot.imshow -matplotlib.figure.Figure.colorbar -matplotlib.pyplot.colorbar -matplotlib.colorbar.Colorbar.minorticks_on -matplotlib.colorbar.Colorbar.minorticks_off +# - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow` +# - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar` +# - `matplotlib.colorbar.Colorbar.minorticks_on` +# - `matplotlib.colorbar.Colorbar.minorticks_off` diff --git a/examples/color/colormap_reference.py b/examples/color/colormap_reference.py index d294e653fabb..549345edffab 100644 --- a/examples/color/colormap_reference.py +++ b/examples/color/colormap_reference.py @@ -6,16 +6,17 @@ Reference for colormaps included with Matplotlib. A reversed version of each of these colormaps is available by appending -``_r`` to the name, e.g., ``viridis_r``. +``_r`` to the name, as shown in :ref:`reverse-cmap`. See :doc:`/tutorials/colors/colormaps` for an in-depth discussion about -colormaps, including colorblind-friendliness. +colormaps, including colorblind-friendliness, and +:doc:`/tutorials/colors/colormap-manipulation` for a guide to creating +colormaps. """ import numpy as np import matplotlib.pyplot as plt - cmaps = [('Perceptually Uniform Sequential', [ 'viridis', 'plasma', 'inferno', 'magma', 'cividis']), ('Sequential', [ @@ -40,7 +41,6 @@ 'gist_rainbow', 'rainbow', 'jet', 'turbo', 'nipy_spectral', 'gist_ncar'])] - gradient = np.linspace(0, 1, 256) gradient = np.vstack((gradient, gradient)) @@ -52,11 +52,11 @@ def plot_color_gradients(cmap_category, cmap_list): fig, axs = plt.subplots(nrows=nrows, figsize=(6.4, figh)) fig.subplots_adjust(top=1-.35/figh, bottom=.15/figh, left=0.2, right=0.99) - axs[0].set_title(cmap_category + ' colormaps', fontsize=14) + axs[0].set_title(f"{cmap_category} colormaps", fontsize=14) - for ax, name in zip(axs, cmap_list): - ax.imshow(gradient, aspect='auto', cmap=plt.get_cmap(name)) - ax.text(-.01, .5, name, va='center', ha='right', fontsize=10, + for ax, cmap_name in zip(axs, cmap_list): + ax.imshow(gradient, aspect='auto', cmap=cmap_name) + ax.text(-.01, .5, cmap_name, va='center', ha='right', fontsize=10, transform=ax.transAxes) # Turn off *all* ticks & spines, not just the ones with colormaps. @@ -67,20 +67,30 @@ def plot_color_gradients(cmap_category, cmap_list): for cmap_category, cmap_list in cmaps: plot_color_gradients(cmap_category, cmap_list) -plt.show() + +############################################################################### +# .. _reverse-cmap: +# +# Reversed colormaps +# ------------------ +# +# Append ``_r`` to the name of any built-in colormap to get the reversed +# version: + +plot_color_gradients("Original and reversed ", ['viridis', 'viridis_r']) + +# %% +# The built-in reversed colormaps are generated using `.Colormap.reversed`. +# For an example, see :ref:`reversing-colormap` ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.colors -matplotlib.axes.Axes.imshow -matplotlib.figure.Figure.text -matplotlib.axes.Axes.set_axis_off +# - `matplotlib.colors` +# - `matplotlib.axes.Axes.imshow` +# - `matplotlib.figure.Figure.text` +# - `matplotlib.axes.Axes.set_axis_off` diff --git a/examples/color/custom_cmap.py b/examples/color/custom_cmap.py index 441cce6484f9..a99127d972e6 100644 --- a/examples/color/custom_cmap.py +++ b/examples/color/custom_cmap.py @@ -12,7 +12,7 @@ Creating custom colormaps -------------------------- +========================= It is also possible to create a custom mapping for a colormap. This is accomplished by creating dictionary that specifies how the RGB channels change from one end of the cmap to the other. @@ -21,54 +21,82 @@ half, green to do the same over the middle half, and blue over the top half. Then you would use:: - cdict = {'red': ((0.0, 0.0, 0.0), - (0.5, 1.0, 1.0), - (1.0, 1.0, 1.0)), - - 'green': ((0.0, 0.0, 0.0), - (0.25, 0.0, 0.0), - (0.75, 1.0, 1.0), - (1.0, 1.0, 1.0)), - - 'blue': ((0.0, 0.0, 0.0), - (0.5, 0.0, 0.0), - (1.0, 1.0, 1.0))} + cdict = { + 'red': ( + (0.0, 0.0, 0.0), + (0.5, 1.0, 1.0), + (1.0, 1.0, 1.0), + ), + 'green': ( + (0.0, 0.0, 0.0), + (0.25, 0.0, 0.0), + (0.75, 1.0, 1.0), + (1.0, 1.0, 1.0), + ), + 'blue': ( + (0.0, 0.0, 0.0), + (0.5, 0.0, 0.0), + (1.0, 1.0, 1.0), + ) + } If, as in this example, there are no discontinuities in the r, g, and b components, then it is quite simple: the second and third element of -each tuple, above, is the same--call it "y". The first element ("x") +each tuple, above, is the same -- call it "``y``". The first element ("``x``") defines interpolation intervals over the full range of 0 to 1, and it -must span that whole range. In other words, the values of x divide the -0-to-1 range into a set of segments, and y gives the end-point color +must span that whole range. In other words, the values of ``x`` divide the +0-to-1 range into a set of segments, and ``y`` gives the end-point color values for each segment. -Now consider the green. cdict['green'] is saying that for -0 <= x <= 0.25, y is zero; no green. -0.25 < x <= 0.75, y varies linearly from 0 to 1. -x > 0.75, y remains at 1, full green. - -If there are discontinuities, then it is a little more complicated. -Label the 3 elements in each row in the cdict entry for a given color as -(x, y0, y1). Then for values of x between x[i] and x[i+1] the color -value is interpolated between y1[i] and y0[i+1]. - -Going back to the cookbook example, look at cdict['red']; because y0 != -y1, it is saying that for x from 0 to 0.5, red increases from 0 to 1, -but then it jumps down, so that for x from 0.5 to 1, red increases from -0.7 to 1. Green ramps from 0 to 1 as x goes from 0 to 0.5, then jumps -back to 0, and ramps back to 1 as x goes from 0.5 to 1.:: +Now consider the green, ``cdict['green']`` is saying that for: + +- 0 <= ``x`` <= 0.25, ``y`` is zero; no green. +- 0.25 < ``x`` <= 0.75, ``y`` varies linearly from 0 to 1. +- 0.75 < ``x`` <= 1, ``y`` remains at 1, full green. + +If there are discontinuities, then it is a little more complicated. Label the 3 +elements in each row in the ``cdict`` entry for a given color as ``(x, y0, +y1)``. Then for values of ``x`` between ``x[i]`` and ``x[i+1]`` the color value +is interpolated between ``y1[i]`` and ``y0[i+1]``. + +Going back to a cookbook example:: + + cdict = { + 'red': ( + (0.0, 0.0, 0.0), + (0.5, 1.0, 0.7), + (1.0, 1.0, 1.0), + ), + 'green': ( + (0.0, 0.0, 0.0), + (0.5, 1.0, 0.0), + (1.0, 1.0, 1.0), + ), + 'blue': ( + (0.0, 0.0, 0.0), + (0.5, 0.0, 0.0), + (1.0, 1.0, 1.0), + ) + } + +and look at ``cdict['red'][1]``; because ``y0 != y1``, it is saying that for +``x`` from 0 to 0.5, red increases from 0 to 1, but then it jumps down, so that +for ``x`` from 0.5 to 1, red increases from 0.7 to 1. Green ramps from 0 to 1 +as ``x`` goes from 0 to 0.5, then jumps back to 0, and ramps back to 1 as ``x`` +goes from 0.5 to 1. :: row i: x y0 y1 - / / + / row i+1: x y0 y1 -Above is an attempt to show that for x in the range x[i] to x[i+1], the -interpolation is between y1[i] and y0[i+1]. So, y0[0] and y1[-1] are -never used. +Above is an attempt to show that for ``x`` in the range ``x[i]`` to ``x[i+1]``, +the interpolation is between ``y1[i]`` and ``y0[i+1]``. So, ``y0[0]`` and +``y1[-1]`` are never used. """ import numpy as np +import matplotlib as mpl import matplotlib.pyplot as plt from matplotlib.colors import LinearSegmentedColormap @@ -81,14 +109,15 @@ ############################################################################### -# --- Colormaps from a list --- +# Colormaps from a list +# --------------------- colors = [(1, 0, 0), (0, 1, 0), (0, 0, 1)] # R -> G -> B n_bins = [3, 6, 10, 100] # Discretizes the interpolation into bins cmap_name = 'my_list' fig, axs = plt.subplots(2, 2, figsize=(6, 9)) fig.subplots_adjust(left=0.02, bottom=0.06, right=0.95, top=0.94, wspace=0.05) -for n_bin, ax in zip(n_bins, axs.ravel()): +for n_bin, ax in zip(n_bins, axs.flat): # Create the colormap cmap = LinearSegmentedColormap.from_list(cmap_name, colors, N=n_bin) # Fewer bins will result in "coarser" colomap interpolation @@ -98,60 +127,79 @@ ############################################################################### -# --- Custom colormaps --- - -cdict1 = {'red': ((0.0, 0.0, 0.0), - (0.5, 0.0, 0.1), - (1.0, 1.0, 1.0)), - - 'green': ((0.0, 0.0, 0.0), - (1.0, 0.0, 0.0)), - - 'blue': ((0.0, 0.0, 1.0), - (0.5, 0.1, 0.0), - (1.0, 0.0, 0.0)) - } - -cdict2 = {'red': ((0.0, 0.0, 0.0), - (0.5, 0.0, 1.0), - (1.0, 0.1, 1.0)), - - 'green': ((0.0, 0.0, 0.0), - (1.0, 0.0, 0.0)), - - 'blue': ((0.0, 0.0, 0.1), - (0.5, 1.0, 0.0), - (1.0, 0.0, 0.0)) - } - -cdict3 = {'red': ((0.0, 0.0, 0.0), - (0.25, 0.0, 0.0), - (0.5, 0.8, 1.0), - (0.75, 1.0, 1.0), - (1.0, 0.4, 1.0)), - - 'green': ((0.0, 0.0, 0.0), - (0.25, 0.0, 0.0), - (0.5, 0.9, 0.9), - (0.75, 0.0, 0.0), - (1.0, 0.0, 0.0)), - - 'blue': ((0.0, 0.0, 0.4), - (0.25, 1.0, 1.0), - (0.5, 1.0, 0.8), - (0.75, 0.0, 0.0), - (1.0, 0.0, 0.0)) - } +# Custom colormaps +# ---------------- + +cdict1 = { + 'red': ( + (0.0, 0.0, 0.0), + (0.5, 0.0, 0.1), + (1.0, 1.0, 1.0), + ), + 'green': ( + (0.0, 0.0, 0.0), + (1.0, 0.0, 0.0), + ), + 'blue': ( + (0.0, 0.0, 1.0), + (0.5, 0.1, 0.0), + (1.0, 0.0, 0.0), + ) +} + +cdict2 = { + 'red': ( + (0.0, 0.0, 0.0), + (0.5, 0.0, 1.0), + (1.0, 0.1, 1.0), + ), + 'green': ( + (0.0, 0.0, 0.0), + (1.0, 0.0, 0.0), + ), + 'blue': ( + (0.0, 0.0, 0.1), + (0.5, 1.0, 0.0), + (1.0, 0.0, 0.0), + ) +} + +cdict3 = { + 'red': ( + (0.0, 0.0, 0.0), + (0.25, 0.0, 0.0), + (0.5, 0.8, 1.0), + (0.75, 1.0, 1.0), + (1.0, 0.4, 1.0), + ), + 'green': ( + (0.0, 0.0, 0.0), + (0.25, 0.0, 0.0), + (0.5, 0.9, 0.9), + (0.75, 0.0, 0.0), + (1.0, 0.0, 0.0), + ), + 'blue': ( + (0.0, 0.0, 0.4), + (0.25, 1.0, 1.0), + (0.5, 1.0, 0.8), + (0.75, 0.0, 0.0), + (1.0, 0.0, 0.0), + ) +} # Make a modified version of cdict3 with some transparency # in the middle of the range. -cdict4 = {**cdict3, - 'alpha': ((0.0, 1.0, 1.0), - # (0.25, 1.0, 1.0), - (0.5, 0.3, 0.3), - # (0.75, 1.0, 1.0), - (1.0, 1.0, 1.0)), - } +cdict4 = { + **cdict3, + 'alpha': ( + (0.0, 1.0, 1.0), + # (0.25, 1.0, 1.0), + (0.5, 0.3, 0.3), + # (0.75, 1.0, 1.0), + (1.0, 1.0, 1.0), + ), +} ############################################################################### @@ -167,25 +215,20 @@ # of Colormap, not just # a LinearSegmentedColormap: -blue_red2 = LinearSegmentedColormap('BlueRed2', cdict2) -plt.register_cmap(cmap=blue_red2) - -plt.register_cmap(cmap=LinearSegmentedColormap('BlueRed3', cdict3)) -plt.register_cmap(cmap=LinearSegmentedColormap('BlueRedAlpha', cdict4)) +mpl.colormaps.register(LinearSegmentedColormap('BlueRed2', cdict2)) +mpl.colormaps.register(LinearSegmentedColormap('BlueRed3', cdict3)) +mpl.colormaps.register(LinearSegmentedColormap('BlueRedAlpha', cdict4)) ############################################################################### -# Make the figure: +# Make the figure, with 4 subplots: fig, axs = plt.subplots(2, 2, figsize=(6, 9)) fig.subplots_adjust(left=0.02, bottom=0.06, right=0.95, top=0.94, wspace=0.05) -# Make 4 subplots: - im1 = axs[0, 0].imshow(Z, cmap=blue_red1) fig.colorbar(im1, ax=axs[0, 0]) -cmap = plt.get_cmap('BlueRed2') -im2 = axs[1, 0].imshow(Z, cmap=cmap) +im2 = axs[1, 0].imshow(Z, cmap='BlueRed2') fig.colorbar(im2, ax=axs[1, 0]) # Now we will set the third cmap as the default. One would @@ -215,7 +258,6 @@ # colorbar after they have been plotted. im4.set_cmap('BlueRedAlpha') axs[1, 1].set_title("Varying alpha") -# fig.suptitle('Custom Blue-Red colormaps', fontsize=16) fig.subplots_adjust(top=0.9) @@ -224,23 +266,16 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.imshow -matplotlib.pyplot.imshow -matplotlib.figure.Figure.colorbar -matplotlib.pyplot.colorbar -matplotlib.colors -matplotlib.colors.LinearSegmentedColormap -matplotlib.colors.LinearSegmentedColormap.from_list -matplotlib.cm -matplotlib.cm.ScalarMappable.set_cmap -matplotlib.pyplot.register_cmap -matplotlib.cm.register_cmap +# - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow` +# - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar` +# - `matplotlib.colors` +# - `matplotlib.colors.LinearSegmentedColormap` +# - `matplotlib.colors.LinearSegmentedColormap.from_list` +# - `matplotlib.cm` +# - `matplotlib.cm.ScalarMappable.set_cmap` +# - `matplotlib.cm.register_cmap` diff --git a/examples/color/named_colors.py b/examples/color/named_colors.py index a1cbf1a92eca..59f15e307bd5 100644 --- a/examples/color/named_colors.py +++ b/examples/color/named_colors.py @@ -3,15 +3,18 @@ List of named colors ==================== -This plots a list of the named colors supported in matplotlib. Note that -:ref:`xkcd colors ` are supported as well, but are not listed here -for brevity. - +This plots a list of the named colors supported in matplotlib. For more information on colors in matplotlib see * the :doc:`/tutorials/colors/colors` tutorial; * the `matplotlib.colors` API; * the :doc:`/gallery/color/color_demo`. + +---------------------------- +Helper Function for Plotting +---------------------------- +First we define a helper function for making a table of colors, then we use it +on some common color categories. """ from matplotlib.patches import Rectangle @@ -19,13 +22,12 @@ import matplotlib.colors as mcolors -def plot_colortable(colors, title, sort_colors=True, emptycols=0): +def plot_colortable(colors, sort_colors=True, emptycols=0): cell_width = 212 cell_height = 22 swatch_width = 48 margin = 12 - topmargin = 40 # Sort colors by hue, saturation, value and name. if sort_colors is True: @@ -41,18 +43,17 @@ def plot_colortable(colors, title, sort_colors=True, emptycols=0): nrows = n // ncols + int(n % ncols > 0) width = cell_width * 4 + 2 * margin - height = cell_height * nrows + margin + topmargin + height = cell_height * nrows + 2 * margin dpi = 72 fig, ax = plt.subplots(figsize=(width / dpi, height / dpi), dpi=dpi) fig.subplots_adjust(margin/width, margin/height, - (width-margin)/width, (height-topmargin)/height) + (width-margin)/width, (height-margin)/height) ax.set_xlim(0, cell_width * 4) ax.set_ylim(cell_height * (nrows-0.5), -cell_height/2.) ax.yaxis.set_visible(False) ax.xaxis.set_visible(False) ax.set_axis_off() - ax.set_title(title, fontsize=24, loc="left", pad=10) for i, name in enumerate(names): row = i % nrows @@ -73,36 +74,48 @@ def plot_colortable(colors, title, sort_colors=True, emptycols=0): return fig -plot_colortable(mcolors.BASE_COLORS, "Base Colors", - sort_colors=False, emptycols=1) -plot_colortable(mcolors.TABLEAU_COLORS, "Tableau Palette", - sort_colors=False, emptycols=2) +############################################################################# +# ----------- +# Base colors +# ----------- -#sphinx_gallery_thumbnail_number = 3 -plot_colortable(mcolors.CSS4_COLORS, "CSS Colors") +plot_colortable(mcolors.BASE_COLORS, sort_colors=False, emptycols=1) -# Optionally plot the XKCD colors (Caution: will produce large figure) -#xkcd_fig = plot_colortable(mcolors.XKCD_COLORS, "XKCD Colors") -#xkcd_fig.savefig("XKCD_Colors.png") +############################################################################# +# --------------- +# Tableau Palette +# --------------- -plt.show() +plot_colortable(mcolors.TABLEAU_COLORS, sort_colors=False, emptycols=2) +############################################################################# +# ---------- +# CSS Colors +# ---------- + +# sphinx_gallery_thumbnail_number = 3 +plot_colortable(mcolors.CSS4_COLORS) +plt.show() ############################################################################# +# ----------- +# XKCD Colors +# ----------- +# XKCD colors are supported, but they produce a large figure, so we skip them +# for now. You can use the following code if desired:: +# +# xkcd_fig = plot_colortable(mcolors.XKCD_COLORS, "XKCD Colors") +# xkcd_fig.savefig("XKCD_Colors.png") # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.colors -matplotlib.colors.rgb_to_hsv -matplotlib.colors.to_rgba -matplotlib.figure.Figure.get_size_inches -matplotlib.figure.Figure.subplots_adjust -matplotlib.axes.Axes.text -matplotlib.patches.Rectangle +# - `matplotlib.colors` +# - `matplotlib.colors.rgb_to_hsv` +# - `matplotlib.colors.to_rgba` +# - `matplotlib.figure.Figure.get_size_inches` +# - `matplotlib.figure.Figure.subplots_adjust` +# - `matplotlib.axes.Axes.text` +# - `matplotlib.patches.Rectangle` diff --git a/examples/event_handling/README.txt b/examples/event_handling/README.txt index 165cb66cb15a..46aed29b56bc 100644 --- a/examples/event_handling/README.txt +++ b/examples/event_handling/README.txt @@ -3,10 +3,11 @@ Event handling ============== -Matplotlib supports :doc:`event handling` with a GUI -neutral event model, so you can connect to Matplotlib events without knowledge -of what user interface Matplotlib will ultimately be plugged in to. This has -two advantages: the code you write will be more portable, and Matplotlib events -are aware of things like data coordinate space and which axes the event occurs -in so you don't have to mess with low level transformation details to go from -canvas space to data space. Object picking examples are also included. +Matplotlib supports :doc:`event handling` with +a GUI neutral event model, so you can connect to Matplotlib events without +knowledge of what user interface Matplotlib will ultimately be plugged in to. +This has two advantages: the code you write will be more portable, and +Matplotlib events are aware of things like data coordinate space and which +axes the event occurs in so you don't have to mess with low level +transformation details to go from canvas space to data space. Object picking +examples are also included. diff --git a/examples/event_handling/close_event.py b/examples/event_handling/close_event.py index 9566167bdc6c..24b45b74ea48 100644 --- a/examples/event_handling/close_event.py +++ b/examples/event_handling/close_event.py @@ -4,6 +4,14 @@ =========== Example to show connecting events that occur when the figure closes. + +.. note:: + This example exercises the interactive capabilities of Matplotlib, and this + will not appear in the static documentation. Please run this code on your + machine to see the interactivity. + + You can copy and paste individual parts, or download the entire example + using the link at the bottom of the page. """ import matplotlib.pyplot as plt diff --git a/examples/event_handling/coords_demo.py b/examples/event_handling/coords_demo.py index 4bfb49518ef5..38a5b77f68e4 100644 --- a/examples/event_handling/coords_demo.py +++ b/examples/event_handling/coords_demo.py @@ -5,6 +5,14 @@ An example of how to interact with the plotting canvas by connecting to move and click events. + +.. note:: + This example exercises the interactive capabilities of Matplotlib, and this + will not appear in the static documentation. Please run this code on your + machine to see the interactivity. + + You can copy and paste individual parts, or download the entire example + using the link at the bottom of the page. """ from matplotlib.backend_bases import MouseButton @@ -18,11 +26,9 @@ def on_move(event): - # get the x and y pixel coords - x, y = event.x, event.y if event.inaxes: - ax = event.inaxes # the axes instance - print('data coords %f %f' % (event.xdata, event.ydata)) + print(f'data coords {event.xdata} {event.ydata},', + f'pixel coords {event.x} {event.y}') def on_click(event): diff --git a/examples/misc/cursor_demo.py b/examples/event_handling/cursor_demo.py similarity index 99% rename from examples/misc/cursor_demo.py rename to examples/event_handling/cursor_demo.py index 5cbdecda82cc..ac09c08f1059 100644 --- a/examples/misc/cursor_demo.py +++ b/examples/event_handling/cursor_demo.py @@ -21,6 +21,8 @@ __ https://github.com/joferkington/mpldatacursor __ https://github.com/anntzer/mplcursors + +.. redirect-from:: /gallery/misc/cursor_demo """ import matplotlib.pyplot as plt diff --git a/examples/event_handling/data_browser.py b/examples/event_handling/data_browser.py index 6d2c68a3741d..42fee3b483c5 100644 --- a/examples/event_handling/data_browser.py +++ b/examples/event_handling/data_browser.py @@ -8,6 +8,14 @@ This example covers how to interact data with multiple canvases. This let's you select and highlight a point on one axis, and generating the data of that point on the other axis. + +.. note:: + This example exercises the interactive capabilities of Matplotlib, and this + will not appear in the static documentation. Please run this code on your + machine to see the interactivity. + + You can copy and paste individual parts, or download the entire example + using the link at the bottom of the page. """ import numpy as np @@ -67,7 +75,7 @@ def update(self): dataind = self.lastind - ax2.cla() + ax2.clear() ax2.plot(X[dataind]) ax2.text(0.05, 0.9, f'mu={xs[dataind]:1.3f}\nsigma={ys[dataind]:1.3f}', diff --git a/examples/event_handling/figure_axes_enter_leave.py b/examples/event_handling/figure_axes_enter_leave.py index 8526346610f8..d55fb6e69d6a 100644 --- a/examples/event_handling/figure_axes_enter_leave.py +++ b/examples/event_handling/figure_axes_enter_leave.py @@ -5,6 +5,14 @@ Illustrate the figure and Axes enter and leave events by changing the frame colors on enter and leave. + +.. note:: + This example exercises the interactive capabilities of Matplotlib, and this + will not appear in the static documentation. Please run this code on your + machine to see the interactivity. + + You can copy and paste individual parts, or download the entire example + using the link at the bottom of the page. """ import matplotlib.pyplot as plt diff --git a/examples/event_handling/ginput_manual_clabel_sgskip.py b/examples/event_handling/ginput_manual_clabel_sgskip.py index 0dc41b05230f..00e548bfccb4 100644 --- a/examples/event_handling/ginput_manual_clabel_sgskip.py +++ b/examples/event_handling/ginput_manual_clabel_sgskip.py @@ -6,10 +6,13 @@ This provides examples of uses of interactive functions, such as ginput, waitforbuttonpress and manual clabel placement. -This script must be run interactively using a backend that has a -graphical user interface (for example, using GTK3Agg backend, but not -PS backend). +.. note:: + This example exercises the interactive capabilities of Matplotlib, and this + will not appear in the static documentation. Please run this code on your + machine to see the interactivity. + You can copy and paste individual parts, or download the entire example + using the link at the bottom of the page. """ import time @@ -27,8 +30,9 @@ def tellme(s): # Define a triangle by clicking three points -plt.clf() -plt.setp(plt.gca(), autoscale_on=False) +plt.figure() +plt.xlim(0, 1) +plt.ylim(0, 1) tellme('You will define a triangle, click to begin') diff --git a/examples/event_handling/image_slices_viewer.py b/examples/event_handling/image_slices_viewer.py index 96cf8f64a3a0..6431d38ac541 100644 --- a/examples/event_handling/image_slices_viewer.py +++ b/examples/event_handling/image_slices_viewer.py @@ -4,6 +4,14 @@ =================== Scroll through 2D image slices of a 3D array. + +.. note:: + This example exercises the interactive capabilities of Matplotlib, and this + will not appear in the static documentation. Please run this code on your + machine to see the interactivity. + + You can copy and paste individual parts, or download the entire example + using the link at the bottom of the page. """ import numpy as np diff --git a/examples/event_handling/keypress_demo.py b/examples/event_handling/keypress_demo.py index 6aff3b617ca3..e71e7ad3dfc4 100644 --- a/examples/event_handling/keypress_demo.py +++ b/examples/event_handling/keypress_demo.py @@ -4,6 +4,14 @@ ============== Show how to connect to keypress events. + +.. note:: + This example exercises the interactive capabilities of Matplotlib, and this + will not appear in the static documentation. Please run this code on your + machine to see the interactivity. + + You can copy and paste individual parts, or download the entire example + using the link at the bottom of the page. """ import sys import numpy as np diff --git a/examples/event_handling/lasso_demo.py b/examples/event_handling/lasso_demo.py index b5f29b2fe3a6..cb9412079330 100644 --- a/examples/event_handling/lasso_demo.py +++ b/examples/event_handling/lasso_demo.py @@ -9,6 +9,14 @@ This is currently a proof-of-concept implementation (though it is usable as is). There will be some refinement of the API. + +.. note:: + This example exercises the interactive capabilities of Matplotlib, and this + will not appear in the static documentation. Please run this code on your + machine to see the interactivity. + + You can copy and paste individual parts, or download the entire example + using the link at the bottom of the page. """ from matplotlib import colors as mcolors, path @@ -45,7 +53,7 @@ def __init__(self, ax, data): 6, sizes=(100,), facecolors=facecolors, offsets=self.xys, - transOffset=ax.transData) + offset_transform=ax.transData) ax.add_collection(self.collection) diff --git a/examples/event_handling/legend_picking.py b/examples/event_handling/legend_picking.py index 5f8a3d1bb779..8c050d472d7a 100644 --- a/examples/event_handling/legend_picking.py +++ b/examples/event_handling/legend_picking.py @@ -4,6 +4,14 @@ ============== Enable picking on the legend to toggle the original line on and off + +.. note:: + This example exercises the interactive capabilities of Matplotlib, and this + will not appear in the static documentation. Please run this code on your + machine to see the interactivity. + + You can copy and paste individual parts, or download the entire example + using the link at the bottom of the page. """ import numpy as np diff --git a/examples/event_handling/looking_glass.py b/examples/event_handling/looking_glass.py index 139afab106f3..70fe4651f79d 100644 --- a/examples/event_handling/looking_glass.py +++ b/examples/event_handling/looking_glass.py @@ -4,6 +4,14 @@ ============= Example using mouse events to simulate a looking glass for inspecting data. + +.. note:: + This example exercises the interactive capabilities of Matplotlib, and this + will not appear in the static documentation. Please run this code on your + machine to see the interactivity. + + You can copy and paste individual parts, or download the entire example + using the link at the bottom of the page. """ import numpy as np import matplotlib.pyplot as plt diff --git a/examples/event_handling/path_editor.py b/examples/event_handling/path_editor.py index 9fe1a1470e64..be8d79935db9 100644 --- a/examples/event_handling/path_editor.py +++ b/examples/event_handling/path_editor.py @@ -7,6 +7,14 @@ This example demonstrates a cross-GUI application using Matplotlib event handling to interact with and modify objects on the canvas. + +.. note:: + This example exercises the interactive capabilities of Matplotlib, and this + will not appear in the static documentation. Please run this code on your + machine to see the interactivity. + + You can copy and paste individual parts, or download the entire example + using the link at the bottom of the page. """ import numpy as np diff --git a/examples/event_handling/pick_event_demo.py b/examples/event_handling/pick_event_demo.py index 84c635c27ca8..b61b18c0fe31 100644 --- a/examples/event_handling/pick_event_demo.py +++ b/examples/event_handling/pick_event_demo.py @@ -3,10 +3,9 @@ Pick Event Demo =============== - You can enable picking by setting the "picker" property of an artist -(for example, a matplotlib Line2D, Text, Patch, Polygon, AxesImage, -etc...) +(for example, a Matplotlib Line2D, Text, Patch, Polygon, AxesImage, +etc.) There are a variety of meanings of the picker property: @@ -22,7 +21,7 @@ of the data within epsilon of the pick event * function - if picker is callable, it is a user supplied function which - determines whether the artist is hit by the mouse event. + determines whether the artist is hit by the mouse event. :: hit, props = picker(artist, mouseevent) @@ -32,7 +31,7 @@ After you have enabled an artist for picking by setting the "picker" property, you need to connect to the figure canvas pick_event to get -pick callbacks on mouse press events. For example, +pick callbacks on mouse press events. For example, :: def pick_handler(event): mouseevent = event.mouseevent @@ -43,15 +42,18 @@ def pick_handler(event): The pick event (matplotlib.backend_bases.PickEvent) which is passed to your callback is always fired with two attributes: - mouseevent - the mouse event that generate the pick event. The - mouse event in turn has attributes like x and y (the coordinates in - display space, such as pixels from left, bottom) and xdata, ydata (the - coords in data space). Additionally, you can get information about - which buttons were pressed, which keys were pressed, which Axes - the mouse is over, etc. See matplotlib.backend_bases.MouseEvent - for details. +mouseevent + the mouse event that generate the pick event. + + The mouse event in turn has attributes like x and y (the coordinates in + display space, such as pixels from left, bottom) and xdata, ydata (the + coords in data space). Additionally, you can get information about + which buttons were pressed, which keys were pressed, which Axes + the mouse is over, etc. See matplotlib.backend_bases.MouseEvent + for details. - artist - the matplotlib.artist that generated the pick event. +artist + the matplotlib.artist that generated the pick event. Additionally, certain artists like Line2D and PatchCollection may attach additional meta data like the indices into the data that meet @@ -59,6 +61,14 @@ def pick_handler(event): the specified epsilon tolerance) The examples below illustrate each of these methods. + +.. note:: + These examples exercises the interactive capabilities of Matplotlib, and + this will not appear in the static documentation. Please run this code on + your machine to see the interactivity. + + You can copy and paste individual parts, or download the entire example + using the link at the bottom of the page. """ import matplotlib.pyplot as plt @@ -74,114 +84,125 @@ def pick_handler(event): np.random.seed(19680801) -def pick_simple(): - # simple picking, lines, rectangles and text - fig, (ax1, ax2) = plt.subplots(2, 1) - ax1.set_title('click on points, rectangles or text', picker=True) - ax1.set_ylabel('ylabel', picker=True, bbox=dict(facecolor='red')) - line, = ax1.plot(rand(100), 'o', picker=True, pickradius=5) - - # pick the rectangle - ax2.bar(range(10), rand(10), picker=True) - for label in ax2.get_xticklabels(): # make the xtick labels pickable - label.set_picker(True) - - def onpick1(event): - if isinstance(event.artist, Line2D): - thisline = event.artist - xdata = thisline.get_xdata() - ydata = thisline.get_ydata() - ind = event.ind - print('onpick1 line:', np.column_stack([xdata[ind], ydata[ind]])) - elif isinstance(event.artist, Rectangle): - patch = event.artist - print('onpick1 patch:', patch.get_path()) - elif isinstance(event.artist, Text): - text = event.artist - print('onpick1 text:', text.get_text()) - - fig.canvas.mpl_connect('pick_event', onpick1) - - -def pick_custom_hit(): - # picking with a custom hit test function - # you can define custom pickers by setting picker to a callable - # function. The function has the signature - # - # hit, props = func(artist, mouseevent) - # - # to determine the hit test. if the mouse event is over the artist, - # return hit=True and props is a dictionary of - # properties you want added to the PickEvent attributes - - def line_picker(line, mouseevent): - """ - Find the points within a certain distance from the mouseclick in - data coords and attach some extra attributes, pickx and picky - which are the data points that were picked. - """ - if mouseevent.xdata is None: - return False, dict() - xdata = line.get_xdata() - ydata = line.get_ydata() - maxd = 0.05 - d = np.sqrt( - (xdata - mouseevent.xdata)**2 + (ydata - mouseevent.ydata)**2) - - ind, = np.nonzero(d <= maxd) - if len(ind): - pickx = xdata[ind] - picky = ydata[ind] - props = dict(ind=ind, pickx=pickx, picky=picky) - return True, props - else: - return False, dict() - - def onpick2(event): - print('onpick2 line:', event.pickx, event.picky) - - fig, ax = plt.subplots() - ax.set_title('custom picker for line data') - line, = ax.plot(rand(100), rand(100), 'o', picker=line_picker) - fig.canvas.mpl_connect('pick_event', onpick2) - - -def pick_scatter_plot(): - # picking on a scatter plot (matplotlib.collections.RegularPolyCollection) - - x, y, c, s = rand(4, 100) - - def onpick3(event): +############################################################################# +# Simple picking, lines, rectangles and text +# ------------------------------------------ + +fig, (ax1, ax2) = plt.subplots(2, 1) +ax1.set_title('click on points, rectangles or text', picker=True) +ax1.set_ylabel('ylabel', picker=True, bbox=dict(facecolor='red')) +line, = ax1.plot(rand(100), 'o', picker=True, pickradius=5) + +# Pick the rectangle. +ax2.bar(range(10), rand(10), picker=True) +for label in ax2.get_xticklabels(): # Make the xtick labels pickable. + label.set_picker(True) + + +def onpick1(event): + if isinstance(event.artist, Line2D): + thisline = event.artist + xdata = thisline.get_xdata() + ydata = thisline.get_ydata() ind = event.ind - print('onpick3 scatter:', ind, x[ind], y[ind]) - - fig, ax = plt.subplots() - ax.scatter(x, y, 100*s, c, picker=True) - fig.canvas.mpl_connect('pick_event', onpick3) - - -def pick_image(): - # picking images (matplotlib.image.AxesImage) - fig, ax = plt.subplots() - ax.imshow(rand(10, 5), extent=(1, 2, 1, 2), picker=True) - ax.imshow(rand(5, 10), extent=(3, 4, 1, 2), picker=True) - ax.imshow(rand(20, 25), extent=(1, 2, 3, 4), picker=True) - ax.imshow(rand(30, 12), extent=(3, 4, 3, 4), picker=True) - ax.set(xlim=(0, 5), ylim=(0, 5)) - - def onpick4(event): - artist = event.artist - if isinstance(artist, AxesImage): - im = artist - A = im.get_array() - print('onpick4 image', A.shape) - - fig.canvas.mpl_connect('pick_event', onpick4) - - -if __name__ == '__main__': - pick_simple() - pick_custom_hit() - pick_scatter_plot() - pick_image() - plt.show() + print('onpick1 line:', np.column_stack([xdata[ind], ydata[ind]])) + elif isinstance(event.artist, Rectangle): + patch = event.artist + print('onpick1 patch:', patch.get_path()) + elif isinstance(event.artist, Text): + text = event.artist + print('onpick1 text:', text.get_text()) + + +fig.canvas.mpl_connect('pick_event', onpick1) + + +############################################################################# +# Picking with a custom hit test function +# --------------------------------------- +# You can define custom pickers by setting picker to a callable function. The +# function has the signature:: +# +# hit, props = func(artist, mouseevent) +# +# to determine the hit test. If the mouse event is over the artist, return +# ``hit=True`` and ``props`` is a dictionary of properties you want added to +# the `.PickEvent` attributes. + +def line_picker(line, mouseevent): + """ + Find the points within a certain distance from the mouseclick in + data coords and attach some extra attributes, pickx and picky + which are the data points that were picked. + """ + if mouseevent.xdata is None: + return False, dict() + xdata = line.get_xdata() + ydata = line.get_ydata() + maxd = 0.05 + d = np.sqrt( + (xdata - mouseevent.xdata)**2 + (ydata - mouseevent.ydata)**2) + + ind, = np.nonzero(d <= maxd) + if len(ind): + pickx = xdata[ind] + picky = ydata[ind] + props = dict(ind=ind, pickx=pickx, picky=picky) + return True, props + else: + return False, dict() + + +def onpick2(event): + print('onpick2 line:', event.pickx, event.picky) + + +fig, ax = plt.subplots() +ax.set_title('custom picker for line data') +line, = ax.plot(rand(100), rand(100), 'o', picker=line_picker) +fig.canvas.mpl_connect('pick_event', onpick2) + + +############################################################################# +# Picking on a scatter plot +# ------------------------- +# A scatter plot is backed by a `~matplotlib.collections.PathCollection`. + +x, y, c, s = rand(4, 100) + + +def onpick3(event): + ind = event.ind + print('onpick3 scatter:', ind, x[ind], y[ind]) + + +fig, ax = plt.subplots() +ax.scatter(x, y, 100*s, c, picker=True) +fig.canvas.mpl_connect('pick_event', onpick3) + + +############################################################################# +# Picking images +# -------------- +# Images plotted using `.Axes.imshow` are `~matplotlib.image.AxesImage` +# objects. + +fig, ax = plt.subplots() +ax.imshow(rand(10, 5), extent=(1, 2, 1, 2), picker=True) +ax.imshow(rand(5, 10), extent=(3, 4, 1, 2), picker=True) +ax.imshow(rand(20, 25), extent=(1, 2, 3, 4), picker=True) +ax.imshow(rand(30, 12), extent=(3, 4, 3, 4), picker=True) +ax.set(xlim=(0, 5), ylim=(0, 5)) + + +def onpick4(event): + artist = event.artist + if isinstance(artist, AxesImage): + im = artist + A = im.get_array() + print('onpick4 image', A.shape) + + +fig.canvas.mpl_connect('pick_event', onpick4) + +plt.show() diff --git a/examples/event_handling/pick_event_demo2.py b/examples/event_handling/pick_event_demo2.py index 162fbdf65ade..8ffb5361a9e3 100644 --- a/examples/event_handling/pick_event_demo2.py +++ b/examples/event_handling/pick_event_demo2.py @@ -6,6 +6,14 @@ Compute the mean (mu) and standard deviation (sigma) of 100 data sets and plot mu vs. sigma. When you click on one of the (mu, sigma) points, plot the raw data from the dataset that generated this point. + +.. note:: + This example exercises the interactive capabilities of Matplotlib, and this + will not appear in the static documentation. Please run this code on your + machine to see the interactivity. + + You can copy and paste individual parts, or download the entire example + using the link at the bottom of the page. """ import numpy as np import matplotlib.pyplot as plt @@ -26,11 +34,11 @@ def onpick(event): if event.artist != line: - return True + return N = len(event.ind) if not N: - return True + return figi, axs = plt.subplots(N, squeeze=False) for ax, dataind in zip(axs.flat, event.ind): @@ -39,7 +47,7 @@ def onpick(event): transform=ax.transAxes, va='top') ax.set_ylim(-0.5, 1.5) figi.show() - return True + fig.canvas.mpl_connect('pick_event', onpick) diff --git a/examples/event_handling/pipong.py b/examples/event_handling/pipong.py deleted file mode 100644 index 7d0acab617fa..000000000000 --- a/examples/event_handling/pipong.py +++ /dev/null @@ -1,290 +0,0 @@ -""" -====== -Pipong -====== - -A Matplotlib based game of Pong illustrating one way to write interactive -animation which are easily ported to multiple backends -pipong.py was written by Paul Ivanov -""" - - -import numpy as np -import matplotlib.pyplot as plt -from numpy.random import randn, randint -from matplotlib.font_manager import FontProperties - -instructions = """ -Player A: Player B: - 'e' up 'i' - 'd' down 'k' - -press 't' -- close these instructions - (animation will be much faster) -press 'a' -- add a puck -press 'A' -- remove a puck -press '1' -- slow down all pucks -press '2' -- speed up all pucks -press '3' -- slow down distractors -press '4' -- speed up distractors -press ' ' -- reset the first puck -press 'n' -- toggle distractors on/off -press 'g' -- toggle the game on/off - - """ - - -class Pad: - def __init__(self, disp, x, y, type='l'): - self.disp = disp - self.x = x - self.y = y - self.w = .3 - self.score = 0 - self.xoffset = 0.3 - self.yoffset = 0.1 - if type == 'r': - self.xoffset *= -1.0 - - if type == 'l' or type == 'r': - self.signx = -1.0 - self.signy = 1.0 - else: - self.signx = 1.0 - self.signy = -1.0 - - def contains(self, loc): - return self.disp.get_bbox().contains(loc.x, loc.y) - - -class Puck: - def __init__(self, disp, pad, field): - self.vmax = .2 - self.disp = disp - self.field = field - self._reset(pad) - - def _reset(self, pad): - self.x = pad.x + pad.xoffset - if pad.y < 0: - self.y = pad.y + pad.yoffset - else: - self.y = pad.y - pad.yoffset - self.vx = pad.x - self.x - self.vy = pad.y + pad.w/2 - self.y - self._speedlimit() - self._slower() - self._slower() - - def update(self, pads): - self.x += self.vx - self.y += self.vy - for pad in pads: - if pad.contains(self): - self.vx *= 1.2 * pad.signx - self.vy *= 1.2 * pad.signy - fudge = .001 - # probably cleaner with something like... - if self.x < fudge: - pads[1].score += 1 - self._reset(pads[0]) - return True - if self.x > 7 - fudge: - pads[0].score += 1 - self._reset(pads[1]) - return True - if self.y < -1 + fudge or self.y > 1 - fudge: - self.vy *= -1.0 - # add some randomness, just to make it interesting - self.vy -= (randn()/300.0 + 1/300.0) * np.sign(self.vy) - self._speedlimit() - return False - - def _slower(self): - self.vx /= 5.0 - self.vy /= 5.0 - - def _faster(self): - self.vx *= 5.0 - self.vy *= 5.0 - - def _speedlimit(self): - if self.vx > self.vmax: - self.vx = self.vmax - if self.vx < -self.vmax: - self.vx = -self.vmax - - if self.vy > self.vmax: - self.vy = self.vmax - if self.vy < -self.vmax: - self.vy = -self.vmax - - -class Game: - def __init__(self, ax): - # create the initial line - self.ax = ax - ax.set_ylim([-1, 1]) - ax.set_xlim([0, 7]) - pad_a_x = 0 - pad_b_x = .50 - pad_a_y = pad_b_y = .30 - pad_b_x += 6.3 - - # pads - pA, = self.ax.barh(pad_a_y, .2, - height=.3, color='k', alpha=.5, edgecolor='b', - lw=2, label="Player B", - animated=True) - pB, = self.ax.barh(pad_b_y, .2, - height=.3, left=pad_b_x, color='k', alpha=.5, - edgecolor='r', lw=2, label="Player A", - animated=True) - - # distractors - self.x = np.arange(0, 2.22*np.pi, 0.01) - self.line, = self.ax.plot(self.x, np.sin(self.x), "r", - animated=True, lw=4) - self.line2, = self.ax.plot(self.x, np.cos(self.x), "g", - animated=True, lw=4) - self.line3, = self.ax.plot(self.x, np.cos(self.x), "g", - animated=True, lw=4) - self.line4, = self.ax.plot(self.x, np.cos(self.x), "r", - animated=True, lw=4) - - # center line - self.centerline, = self.ax.plot([3.5, 3.5], [1, -1], 'k', - alpha=.5, animated=True, lw=8) - - # puck (s) - self.puckdisp = self.ax.scatter([1], [1], label='_nolegend_', - s=200, c='g', - alpha=.9, animated=True) - - self.canvas = self.ax.figure.canvas - self.background = None - self.cnt = 0 - self.distract = True - self.res = 100.0 - self.on = False - self.inst = True # show instructions from the beginning - self.background = None - self.pads = [Pad(pA, pad_a_x, pad_a_y), - Pad(pB, pad_b_x, pad_b_y, 'r')] - self.pucks = [] - self.i = self.ax.annotate(instructions, (.5, 0.5), - name='monospace', - verticalalignment='center', - horizontalalignment='center', - multialignment='left', - textcoords='axes fraction', - animated=False) - self.canvas.mpl_connect('key_press_event', self.on_key_press) - - def draw(self, event): - draw_artist = self.ax.draw_artist - if self.background is None: - self.background = self.canvas.copy_from_bbox(self.ax.bbox) - - # restore the clean slate background - self.canvas.restore_region(self.background) - - # show the distractors - if self.distract: - self.line.set_ydata(np.sin(self.x + self.cnt/self.res)) - self.line2.set_ydata(np.cos(self.x - self.cnt/self.res)) - self.line3.set_ydata(np.tan(self.x + self.cnt/self.res)) - self.line4.set_ydata(np.tan(self.x - self.cnt/self.res)) - draw_artist(self.line) - draw_artist(self.line2) - draw_artist(self.line3) - draw_artist(self.line4) - - # pucks and pads - if self.on: - self.ax.draw_artist(self.centerline) - for pad in self.pads: - pad.disp.set_y(pad.y) - pad.disp.set_x(pad.x) - self.ax.draw_artist(pad.disp) - - for puck in self.pucks: - if puck.update(self.pads): - # we only get here if someone scored - self.pads[0].disp.set_label( - " " + str(self.pads[0].score)) - self.pads[1].disp.set_label( - " " + str(self.pads[1].score)) - self.ax.legend(loc='center', framealpha=.2, - facecolor='0.5', - prop=FontProperties(size='xx-large', - weight='bold')) - - self.background = None - self.ax.figure.canvas.draw_idle() - return True - puck.disp.set_offsets([[puck.x, puck.y]]) - self.ax.draw_artist(puck.disp) - - # just redraw the axes rectangle - self.canvas.blit(self.ax.bbox) - self.canvas.flush_events() - if self.cnt == 50000: - # just so we don't get carried away - print("...and you've been playing for too long!!!") - plt.close() - - self.cnt += 1 - return True - - def on_key_press(self, event): - if event.key == '3': - self.res *= 5.0 - if event.key == '4': - self.res /= 5.0 - - if event.key == 'e': - self.pads[0].y += .1 - if self.pads[0].y > 1 - .3: - self.pads[0].y = 1 - .3 - if event.key == 'd': - self.pads[0].y -= .1 - if self.pads[0].y < -1: - self.pads[0].y = -1 - - if event.key == 'i': - self.pads[1].y += .1 - if self.pads[1].y > 1 - .3: - self.pads[1].y = 1 - .3 - if event.key == 'k': - self.pads[1].y -= .1 - if self.pads[1].y < -1: - self.pads[1].y = -1 - - if event.key == 'a': - self.pucks.append(Puck(self.puckdisp, - self.pads[randint(2)], - self.ax.bbox)) - if event.key == 'A' and len(self.pucks): - self.pucks.pop() - if event.key == ' ' and len(self.pucks): - self.pucks[0]._reset(self.pads[randint(2)]) - if event.key == '1': - for p in self.pucks: - p._slower() - if event.key == '2': - for p in self.pucks: - p._faster() - - if event.key == 'n': - self.distract = not self.distract - - if event.key == 'g': - self.on = not self.on - if event.key == 't': - self.inst = not self.inst - self.i.set_visible(not self.i.get_visible()) - self.background = None - self.canvas.draw_idle() - if event.key == 'q': - plt.close() diff --git a/examples/event_handling/poly_editor.py b/examples/event_handling/poly_editor.py index 938c7b8b7b28..b156b9c662bd 100644 --- a/examples/event_handling/poly_editor.py +++ b/examples/event_handling/poly_editor.py @@ -5,6 +5,14 @@ This is an example to show how to build cross-GUI applications using Matplotlib event handling to interact with objects on the canvas. + +.. note:: + This example exercises the interactive capabilities of Matplotlib, and this + will not appear in the static documentation. Please run this code on your + machine to see the interactivity. + + You can copy and paste individual parts, or download the entire example + using the link at the bottom of the page. """ import numpy as np from matplotlib.lines import Line2D @@ -24,7 +32,7 @@ def dist_point_to_segment(p, s0, s1): Get the distance of a point to a segment. *p*, *s0*, *s1* are *xy* sequences This algorithm from - http://geomalgorithms.com/a02-_lines.html + http://www.geomalgorithms.com/algorithms.html """ v = s1 - s0 w = p - s0 diff --git a/examples/event_handling/pong_sgskip.py b/examples/event_handling/pong_sgskip.py index 416bdf048dbe..e629d888cd77 100644 --- a/examples/event_handling/pong_sgskip.py +++ b/examples/event_handling/pong_sgskip.py @@ -3,23 +3,304 @@ Pong ==== -A small game demo using Matplotlib. +A Matplotlib based game of Pong illustrating one way to write interactive +animations that are easily ported to multiple backends. -.. only:: builder_html - - This example requires :download:`pipong.py ` +.. note:: + This example exercises the interactive capabilities of Matplotlib, and this + will not appear in the static documentation. Please run this code on your + machine to see the interactivity. + You can copy and paste individual parts, or download the entire example + using the link at the bottom of the page. """ -import time +import time +import numpy as np import matplotlib.pyplot as plt -import pipong +from numpy.random import randn, randint +from matplotlib.font_manager import FontProperties + +instructions = """ +Player A: Player B: + 'e' up 'i' + 'd' down 'k' + +press 't' -- close these instructions + (animation will be much faster) +press 'a' -- add a puck +press 'A' -- remove a puck +press '1' -- slow down all pucks +press '2' -- speed up all pucks +press '3' -- slow down distractors +press '4' -- speed up distractors +press ' ' -- reset the first puck +press 'n' -- toggle distractors on/off +press 'g' -- toggle the game on/off + + """ + + +class Pad: + def __init__(self, disp, x, y, type='l'): + self.disp = disp + self.x = x + self.y = y + self.w = .3 + self.score = 0 + self.xoffset = 0.3 + self.yoffset = 0.1 + if type == 'r': + self.xoffset *= -1.0 + + if type == 'l' or type == 'r': + self.signx = -1.0 + self.signy = 1.0 + else: + self.signx = 1.0 + self.signy = -1.0 + + def contains(self, loc): + return self.disp.get_bbox().contains(loc.x, loc.y) + + +class Puck: + def __init__(self, disp, pad, field): + self.vmax = .2 + self.disp = disp + self.field = field + self._reset(pad) + + def _reset(self, pad): + self.x = pad.x + pad.xoffset + if pad.y < 0: + self.y = pad.y + pad.yoffset + else: + self.y = pad.y - pad.yoffset + self.vx = pad.x - self.x + self.vy = pad.y + pad.w/2 - self.y + self._speedlimit() + self._slower() + self._slower() + + def update(self, pads): + self.x += self.vx + self.y += self.vy + for pad in pads: + if pad.contains(self): + self.vx *= 1.2 * pad.signx + self.vy *= 1.2 * pad.signy + fudge = .001 + # probably cleaner with something like... + if self.x < fudge: + pads[1].score += 1 + self._reset(pads[0]) + return True + if self.x > 7 - fudge: + pads[0].score += 1 + self._reset(pads[1]) + return True + if self.y < -1 + fudge or self.y > 1 - fudge: + self.vy *= -1.0 + # add some randomness, just to make it interesting + self.vy -= (randn()/300.0 + 1/300.0) * np.sign(self.vy) + self._speedlimit() + return False + + def _slower(self): + self.vx /= 5.0 + self.vy /= 5.0 + + def _faster(self): + self.vx *= 5.0 + self.vy *= 5.0 + + def _speedlimit(self): + if self.vx > self.vmax: + self.vx = self.vmax + if self.vx < -self.vmax: + self.vx = -self.vmax + + if self.vy > self.vmax: + self.vy = self.vmax + if self.vy < -self.vmax: + self.vy = -self.vmax + + +class Game: + def __init__(self, ax): + # create the initial line + self.ax = ax + ax.xaxis.set_visible(False) + ax.set_xlim([0, 7]) + ax.yaxis.set_visible(False) + ax.set_ylim([-1, 1]) + pad_a_x = 0 + pad_b_x = .50 + pad_a_y = pad_b_y = .30 + pad_b_x += 6.3 + + # pads + pA, = self.ax.barh(pad_a_y, .2, + height=.3, color='k', alpha=.5, edgecolor='b', + lw=2, label="Player B", + animated=True) + pB, = self.ax.barh(pad_b_y, .2, + height=.3, left=pad_b_x, color='k', alpha=.5, + edgecolor='r', lw=2, label="Player A", + animated=True) + + # distractors + self.x = np.arange(0, 2.22*np.pi, 0.01) + self.line, = self.ax.plot(self.x, np.sin(self.x), "r", + animated=True, lw=4) + self.line2, = self.ax.plot(self.x, np.cos(self.x), "g", + animated=True, lw=4) + self.line3, = self.ax.plot(self.x, np.cos(self.x), "g", + animated=True, lw=4) + self.line4, = self.ax.plot(self.x, np.cos(self.x), "r", + animated=True, lw=4) + + # center line + self.centerline, = self.ax.plot([3.5, 3.5], [1, -1], 'k', + alpha=.5, animated=True, lw=8) + + # puck (s) + self.puckdisp = self.ax.scatter([1], [1], label='_nolegend_', + s=200, c='g', + alpha=.9, animated=True) + + self.canvas = self.ax.figure.canvas + self.background = None + self.cnt = 0 + self.distract = True + self.res = 100.0 + self.on = False + self.inst = True # show instructions from the beginning + self.pads = [Pad(pA, pad_a_x, pad_a_y), + Pad(pB, pad_b_x, pad_b_y, 'r')] + self.pucks = [] + self.i = self.ax.annotate(instructions, (.5, 0.5), + name='monospace', + verticalalignment='center', + horizontalalignment='center', + multialignment='left', + xycoords='axes fraction', + animated=False) + self.canvas.mpl_connect('key_press_event', self.on_key_press) + + def draw(self): + draw_artist = self.ax.draw_artist + if self.background is None: + self.background = self.canvas.copy_from_bbox(self.ax.bbox) + + # restore the clean slate background + self.canvas.restore_region(self.background) + + # show the distractors + if self.distract: + self.line.set_ydata(np.sin(self.x + self.cnt/self.res)) + self.line2.set_ydata(np.cos(self.x - self.cnt/self.res)) + self.line3.set_ydata(np.tan(self.x + self.cnt/self.res)) + self.line4.set_ydata(np.tan(self.x - self.cnt/self.res)) + draw_artist(self.line) + draw_artist(self.line2) + draw_artist(self.line3) + draw_artist(self.line4) + + # pucks and pads + if self.on: + self.ax.draw_artist(self.centerline) + for pad in self.pads: + pad.disp.set_y(pad.y) + pad.disp.set_x(pad.x) + self.ax.draw_artist(pad.disp) + + for puck in self.pucks: + if puck.update(self.pads): + # we only get here if someone scored + self.pads[0].disp.set_label( + " " + str(self.pads[0].score)) + self.pads[1].disp.set_label( + " " + str(self.pads[1].score)) + self.ax.legend(loc='center', framealpha=.2, + facecolor='0.5', + prop=FontProperties(size='xx-large', + weight='bold')) + + self.background = None + self.ax.figure.canvas.draw_idle() + return + puck.disp.set_offsets([[puck.x, puck.y]]) + self.ax.draw_artist(puck.disp) + + # just redraw the axes rectangle + self.canvas.blit(self.ax.bbox) + self.canvas.flush_events() + if self.cnt == 50000: + # just so we don't get carried away + print("...and you've been playing for too long!!!") + plt.close() + + self.cnt += 1 + + def on_key_press(self, event): + if event.key == '3': + self.res *= 5.0 + if event.key == '4': + self.res /= 5.0 + + if event.key == 'e': + self.pads[0].y += .1 + if self.pads[0].y > 1 - .3: + self.pads[0].y = 1 - .3 + if event.key == 'd': + self.pads[0].y -= .1 + if self.pads[0].y < -1: + self.pads[0].y = -1 + + if event.key == 'i': + self.pads[1].y += .1 + if self.pads[1].y > 1 - .3: + self.pads[1].y = 1 - .3 + if event.key == 'k': + self.pads[1].y -= .1 + if self.pads[1].y < -1: + self.pads[1].y = -1 + + if event.key == 'a': + self.pucks.append(Puck(self.puckdisp, + self.pads[randint(2)], + self.ax.bbox)) + if event.key == 'A' and len(self.pucks): + self.pucks.pop() + if event.key == ' ' and len(self.pucks): + self.pucks[0]._reset(self.pads[randint(2)]) + if event.key == '1': + for p in self.pucks: + p._slower() + if event.key == '2': + for p in self.pucks: + p._faster() + + if event.key == 'n': + self.distract = not self.distract + + if event.key == 'g': + self.on = not self.on + if event.key == 't': + self.inst = not self.inst + self.i.set_visible(not self.i.get_visible()) + self.background = None + self.canvas.draw_idle() + if event.key == 'q': + plt.close() fig, ax = plt.subplots() canvas = ax.figure.canvas -animation = pipong.Game(ax) +animation = Game(ax) # disable the default key bindings if fig.canvas.manager.key_press_handler_id is not None: @@ -35,10 +316,7 @@ def on_redraw(event): def start_anim(event): canvas.mpl_disconnect(start_anim.cid) - def local_draw(): - if animation.ax.get_renderer_cache(): - animation.draw(None) - start_anim.timer.add_callback(local_draw) + start_anim.timer.add_callback(animation.draw) start_anim.timer.start() canvas.mpl_connect('draw_event', on_redraw) diff --git a/examples/event_handling/resample.py b/examples/event_handling/resample.py index 542e84f13c71..30d61debbbf9 100644 --- a/examples/event_handling/resample.py +++ b/examples/event_handling/resample.py @@ -6,6 +6,14 @@ Downsampling lowers the sample rate or sample size of a signal. In this tutorial, the signal is downsampled when the plot is adjusted through dragging and zooming. + +.. note:: + This example exercises the interactive capabilities of Matplotlib, and this + will not appear in the static documentation. Please run this code on your + machine to see the interactivity. + + You can copy and paste individual parts, or download the entire example + using the link at the bottom of the page. """ import numpy as np diff --git a/examples/event_handling/timers.py b/examples/event_handling/timers.py index 4db7fe40bdc3..43330097cf87 100644 --- a/examples/event_handling/timers.py +++ b/examples/event_handling/timers.py @@ -5,6 +5,14 @@ Simple example of using general timer objects. This is used to update the time placed in the title of the figure. + +.. note:: + This example exercises the interactive capabilities of Matplotlib, and this + will not appear in the static documentation. Please run this code on your + machine to see the interactivity. + + You can copy and paste individual parts, or download the entire example + using the link at the bottom of the page. """ import matplotlib.pyplot as plt import numpy as np @@ -26,10 +34,10 @@ def update_title(axes): timer.add_callback(update_title, ax) timer.start() -# Or could start the timer on first figure draw -#def start_timer(event): -# timer.start() -# fig.canvas.mpl_disconnect(drawid) -#drawid = fig.canvas.mpl_connect('draw_event', start_timer) +# Or could start the timer on first figure draw: +# def start_timer(event): +# timer.start() +# fig.canvas.mpl_disconnect(drawid) +# drawid = fig.canvas.mpl_connect('draw_event', start_timer) plt.show() diff --git a/examples/event_handling/trifinder_event_demo.py b/examples/event_handling/trifinder_event_demo.py index 6678003ab189..991c5c9a3a82 100644 --- a/examples/event_handling/trifinder_event_demo.py +++ b/examples/event_handling/trifinder_event_demo.py @@ -6,6 +6,14 @@ Example showing the use of a TriFinder object. As the mouse is moved over the triangulation, the triangle under the cursor is highlighted and the index of the triangle is displayed in the plot title. + +.. note:: + This example exercises the interactive capabilities of Matplotlib, and this + will not appear in the static documentation. Please run this code on your + machine to see the interactivity. + + You can copy and paste individual parts, or download the entire example + using the link at the bottom of the page. """ import matplotlib.pyplot as plt from matplotlib.tri import Triangulation diff --git a/examples/event_handling/viewlims.py b/examples/event_handling/viewlims.py index d6bdf99787d3..134419300a68 100644 --- a/examples/event_handling/viewlims.py +++ b/examples/event_handling/viewlims.py @@ -5,6 +5,14 @@ Creates two identical panels. Zooming in on the right panel will show a rectangle in the first panel, denoting the zoomed region. + +.. note:: + This example exercises the interactive capabilities of Matplotlib, and this + will not appear in the static documentation. Please run this code on your + machine to see the interactivity. + + You can copy and paste individual parts, or download the entire example + using the link at the bottom of the page. """ import numpy as np import matplotlib.pyplot as plt diff --git a/examples/event_handling/zoom_window.py b/examples/event_handling/zoom_window.py index 9afe60ce935c..c10cd820aa47 100644 --- a/examples/event_handling/zoom_window.py +++ b/examples/event_handling/zoom_window.py @@ -12,6 +12,14 @@ Note the diameter of the circles in the scatter are defined in points**2, so their size is independent of the zoom. + +.. note:: + This example exercises the interactive capabilities of Matplotlib, and this + will not appear in the static documentation. Please run this code on your + machine to see the interactivity. + + You can copy and paste individual parts, or download the entire example + using the link at the bottom of the page. """ import matplotlib.pyplot as plt @@ -21,8 +29,8 @@ # Fixing random state for reproducibility np.random.seed(19680801) -figsrc, axsrc = plt.subplots() -figzoom, axzoom = plt.subplots() +figsrc, axsrc = plt.subplots(figsize=(3.7, 3.7)) +figzoom, axzoom = plt.subplots(figsize=(3.7, 3.7)) axsrc.set(xlim=(0, 1), ylim=(0, 1), autoscale_on=False, title='Click to zoom') axzoom.set(xlim=(0.45, 0.55), ylim=(0.4, 0.6), autoscale_on=False, diff --git a/examples/frontpage/3D.py b/examples/frontpage/3D.py deleted file mode 100644 index 49ebcc008923..000000000000 --- a/examples/frontpage/3D.py +++ /dev/null @@ -1,36 +0,0 @@ -""" -==================== -Frontpage 3D example -==================== - -This example reproduces the frontpage 3D example. -""" - -from matplotlib import cbook -from matplotlib import cm -from matplotlib.colors import LightSource -import matplotlib.pyplot as plt -import numpy as np - -dem = cbook.get_sample_data('jacksboro_fault_dem.npz', np_load=True) -z = dem['elevation'] -nrows, ncols = z.shape -x = np.linspace(dem['xmin'], dem['xmax'], ncols) -y = np.linspace(dem['ymin'], dem['ymax'], nrows) -x, y = np.meshgrid(x, y) - -region = np.s_[5:50, 5:50] -x, y, z = x[region], y[region], z[region] - -fig, ax = plt.subplots(subplot_kw=dict(projection='3d')) - -ls = LightSource(270, 45) -# To use a custom hillshading mode, override the built-in shading and pass -# in the rgb colors of the shaded surface calculated from "shade". -rgb = ls.shade(z, cmap=cm.gist_earth, vert_exag=0.1, blend_mode='soft') -surf = ax.plot_surface(x, y, z, rstride=1, cstride=1, facecolors=rgb, - linewidth=0, antialiased=False, shade=False) -ax.set_xticks([]) -ax.set_yticks([]) -ax.set_zticks([]) -fig.savefig("surface3d_frontpage.png", dpi=25) # results in 160x120 px image diff --git a/examples/frontpage/README.txt b/examples/frontpage/README.txt deleted file mode 100644 index 1f70a8a9fda2..000000000000 --- a/examples/frontpage/README.txt +++ /dev/null @@ -1,4 +0,0 @@ -.. _front_page_examples: - -Front Page -========== diff --git a/examples/frontpage/contour.py b/examples/frontpage/contour.py deleted file mode 100644 index ddb0721c1203..000000000000 --- a/examples/frontpage/contour.py +++ /dev/null @@ -1,34 +0,0 @@ -""" -========================= -Frontpage contour example -========================= - -This example reproduces the frontpage contour example. -""" - -import matplotlib.pyplot as plt -import numpy as np -from matplotlib import cm - -extent = (-3, 3, -3, 3) - -delta = 0.5 -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 = Z1 - Z2 - -norm = cm.colors.Normalize(vmax=abs(Z).max(), vmin=-abs(Z).max()) - -fig, ax = plt.subplots() -cset1 = ax.contourf( - X, Y, Z, 40, - norm=norm) -ax.set_xlim(-2, 2) -ax.set_ylim(-2, 2) -ax.set_xticks([]) -ax.set_yticks([]) -fig.savefig("contour_frontpage.png", dpi=25) # results in 160x120 px image -plt.show() diff --git a/examples/frontpage/histogram.py b/examples/frontpage/histogram.py deleted file mode 100644 index a0938bdb8916..000000000000 --- a/examples/frontpage/histogram.py +++ /dev/null @@ -1,22 +0,0 @@ -""" -=========================== -Frontpage histogram example -=========================== - -This example reproduces the frontpage histogram example. -""" - -import matplotlib.pyplot as plt -import numpy as np - - -random_state = np.random.RandomState(19680801) -X = random_state.randn(10000) - -fig, ax = plt.subplots() -ax.hist(X, bins=25, density=True) -x = np.linspace(-5, 5, 1000) -ax.plot(x, 1 / np.sqrt(2*np.pi) * np.exp(-(x**2)/2), linewidth=4) -ax.set_xticks([]) -ax.set_yticks([]) -fig.savefig("histogram_frontpage.png", dpi=25) # results in 160x120 px image diff --git a/examples/frontpage/membrane.py b/examples/frontpage/membrane.py deleted file mode 100644 index 4e126eceda5f..000000000000 --- a/examples/frontpage/membrane.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -====================== -Frontpage plot example -====================== - -This example reproduces the frontpage simple plot example. -""" - -import matplotlib.pyplot as plt -import matplotlib.cbook as cbook -import numpy as np - - -with cbook.get_sample_data('membrane.dat') as datafile: - x = np.fromfile(datafile, np.float32) -# 0.0005 is the sample interval - -fig, ax = plt.subplots() -ax.plot(x, linewidth=4) -ax.set_xlim(5000, 6000) -ax.set_ylim(-0.6, 0.1) -ax.set_xticks([]) -ax.set_yticks([]) -fig.savefig("membrane_frontpage.png", dpi=25) # results in 160x120 px image diff --git a/examples/images_contours_and_fields/affine_image.py b/examples/images_contours_and_fields/affine_image.py index 8a3a78e2fee6..4b131d95588e 100644 --- a/examples/images_contours_and_fields/affine_image.py +++ b/examples/images_contours_and_fields/affine_image.py @@ -66,15 +66,10 @@ def do_plot(ax, Z, transform): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.imshow -matplotlib.pyplot.imshow -matplotlib.transforms.Affine2D +# - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow` +# - `matplotlib.transforms.Affine2D` diff --git a/examples/images_contours_and_fields/barb_demo.py b/examples/images_contours_and_fields/barb_demo.py index 04bf151e6814..4530dc813b3d 100644 --- a/examples/images_contours_and_fields/barb_demo.py +++ b/examples/images_contours_and_fields/barb_demo.py @@ -47,6 +47,7 @@ masked_u[4] = 1000 # Bad value that should not be plotted when masked masked_u[4] = np.ma.masked +############################################################################# # Identical plot to panel 2 in the first figure, but with the point at # (0.5, 0.25) missing (masked) fig2, ax2 = plt.subplots() @@ -56,14 +57,9 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.barbs -matplotlib.pyplot.barbs +# - `matplotlib.axes.Axes.barbs` / `matplotlib.pyplot.barbs` diff --git a/examples/images_contours_and_fields/barcode_demo.py b/examples/images_contours_and_fields/barcode_demo.py index de00656cf67e..20fec531c097 100644 --- a/examples/images_contours_and_fields/barcode_demo.py +++ b/examples/images_contours_and_fields/barcode_demo.py @@ -39,15 +39,10 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.imshow -matplotlib.pyplot.imshow -matplotlib.figure.Figure.add_axes +# - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow` +# - `matplotlib.figure.Figure.add_axes` diff --git a/examples/images_contours_and_fields/colormap_interactive_adjustment.py b/examples/images_contours_and_fields/colormap_interactive_adjustment.py new file mode 100644 index 000000000000..3ab9074fd1b6 --- /dev/null +++ b/examples/images_contours_and_fields/colormap_interactive_adjustment.py @@ -0,0 +1,33 @@ +""" +======================================== +Interactive Adjustment of Colormap Range +======================================== + +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 matplotlib.pyplot as plt +import numpy as np + +t = np.linspace(0, 2 * np.pi, 1024) +data2d = np.sin(t)[:, np.newaxis] * np.cos(t)[np.newaxis, :] + +fig, ax = plt.subplots() +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') + +fig.colorbar(im, ax=ax, label='Interactive colorbar') + +plt.show() diff --git a/examples/userdemo/colormap_normalizations.py b/examples/images_contours_and_fields/colormap_normalizations.py similarity index 98% rename from examples/userdemo/colormap_normalizations.py rename to examples/images_contours_and_fields/colormap_normalizations.py index febccf35a449..ecb15d842a10 100644 --- a/examples/userdemo/colormap_normalizations.py +++ b/examples/images_contours_and_fields/colormap_normalizations.py @@ -4,6 +4,8 @@ ======================= Demonstration of using norm to map colormaps onto data in non-linear ways. + +.. redirect-from:: /gallery/userdemo/colormap_normalizations """ import numpy as np diff --git a/examples/images_contours_and_fields/colormap_normalizations_symlognorm.py b/examples/images_contours_and_fields/colormap_normalizations_symlognorm.py new file mode 100644 index 000000000000..42a3878143d3 --- /dev/null +++ b/examples/images_contours_and_fields/colormap_normalizations_symlognorm.py @@ -0,0 +1,84 @@ +""" +================================== +Colormap Normalizations SymLogNorm +================================== + +Demonstration of using norm to map colormaps onto data in non-linear ways. + +.. redirect-from:: /gallery/userdemo/colormap_normalization_symlognorm +""" + +############################################################################### +# Synthetic dataset consisting of two humps, one negative and one positive, +# the positive with 8-times the amplitude. +# Linearly, the negative hump is almost invisible, +# and it is very difficult to see any detail of its profile. +# With the logarithmic scaling applied to both positive and negative values, +# it is much easier to see the shape of each hump. +# +# See `~.colors.SymLogNorm`. + +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.colors as colors + + +def rbf(x, y): + return 1.0 / (1 + 5 * ((x ** 2) + (y ** 2))) + +N = 200 +gain = 8 +X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)] +Z1 = rbf(X + 0.5, Y + 0.5) +Z2 = rbf(X - 0.5, Y - 0.5) +Z = gain * Z1 - Z2 + +shadeopts = {'cmap': 'PRGn', 'shading': 'gouraud'} +colormap = 'PRGn' +lnrwidth = 0.5 + +fig, ax = plt.subplots(2, 1, sharex=True, sharey=True) + +pcm = ax[0].pcolormesh(X, Y, Z, + norm=colors.SymLogNorm(linthresh=lnrwidth, linscale=1, + vmin=-gain, vmax=gain, base=10), + **shadeopts) +fig.colorbar(pcm, ax=ax[0], extend='both') +ax[0].text(-2.5, 1.5, 'symlog') + +pcm = ax[1].pcolormesh(X, Y, Z, vmin=-gain, vmax=gain, + **shadeopts) +fig.colorbar(pcm, ax=ax[1], extend='both') +ax[1].text(-2.5, 1.5, 'linear') + + +############################################################################### +# In order to find the best visualization for any particular dataset, +# it may be necessary to experiment with multiple different color scales. +# As well as the `~.colors.SymLogNorm` scaling, there is also +# the option of using `~.colors.AsinhNorm` (experimental), which has a smoother +# transition between the linear and logarithmic regions of the transformation +# applied to the data values, "Z". +# In the plots below, it may be possible to see contour-like artifacts +# around each hump despite there being no sharp features +# in the dataset itself. The ``asinh`` scaling shows a smoother shading +# of each hump. + +fig, ax = plt.subplots(2, 1, sharex=True, sharey=True) + +pcm = ax[0].pcolormesh(X, Y, Z, + norm=colors.SymLogNorm(linthresh=lnrwidth, linscale=1, + vmin=-gain, vmax=gain, base=10), + **shadeopts) +fig.colorbar(pcm, ax=ax[0], extend='both') +ax[0].text(-2.5, 1.5, 'symlog') + +pcm = ax[1].pcolormesh(X, Y, Z, + norm=colors.AsinhNorm(linear_width=lnrwidth, + vmin=-gain, vmax=gain), + **shadeopts) +fig.colorbar(pcm, ax=ax[1], extend='both') +ax[1].text(-2.5, 1.5, 'asinh') + + +plt.show() diff --git a/examples/images_contours_and_fields/contour_corner_mask.py b/examples/images_contours_and_fields/contour_corner_mask.py index 0482945d552b..280231acb950 100644 --- a/examples/images_contours_and_fields/contour_corner_mask.py +++ b/examples/images_contours_and_fields/contour_corner_mask.py @@ -39,16 +39,10 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.contour -matplotlib.pyplot.contour -matplotlib.axes.Axes.contourf -matplotlib.pyplot.contourf +# - `matplotlib.axes.Axes.contour` / `matplotlib.pyplot.contour` +# - `matplotlib.axes.Axes.contourf` / `matplotlib.pyplot.contourf` diff --git a/examples/images_contours_and_fields/contour_demo.py b/examples/images_contours_and_fields/contour_demo.py index 2c9881c83fe1..266acccf2409 100644 --- a/examples/images_contours_and_fields/contour_demo.py +++ b/examples/images_contours_and_fields/contour_demo.py @@ -10,7 +10,6 @@
`. """ -import matplotlib import numpy as np import matplotlib.cm as cm import matplotlib.pyplot as plt @@ -57,7 +56,7 @@ ############################################################################### # You can set negative contours to be solid instead of dashed: -matplotlib.rcParams['contour.negative_linestyle'] = 'solid' +plt.rcParams['contour.negative_linestyle'] = 'solid' fig, ax = plt.subplots() CS = ax.contour(X, Y, Z, 6, colors='k') # Negative contours default to dashed. ax.clabel(CS, fontsize=9, inline=True) @@ -86,8 +85,7 @@ linewidths=2, extent=(-3, 3, -2, 2)) # Thicken the zero contour. -zc = CS.collections[6] -plt.setp(zc, linewidth=4) +CS.collections[6].set_linewidth(4) ax.clabel(CS, levels[1::2], # label every second level inline=True, fmt='%1.1f', fontsize=14) @@ -111,20 +109,13 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.contour -matplotlib.pyplot.contour -matplotlib.figure.Figure.colorbar -matplotlib.pyplot.colorbar -matplotlib.axes.Axes.clabel -matplotlib.pyplot.clabel -matplotlib.axes.Axes.set_position -matplotlib.axes.Axes.get_position +# - `matplotlib.axes.Axes.contour` / `matplotlib.pyplot.contour` +# - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar` +# - `matplotlib.axes.Axes.clabel` / `matplotlib.pyplot.clabel` +# - `matplotlib.axes.Axes.get_position` +# - `matplotlib.axes.Axes.set_position` diff --git a/examples/images_contours_and_fields/contour_image.py b/examples/images_contours_and_fields/contour_image.py index cca7ea6da6a4..a2a07f20f273 100644 --- a/examples/images_contours_and_fields/contour_image.py +++ b/examples/images_contours_and_fields/contour_image.py @@ -41,7 +41,7 @@ axs = _axs.flatten() cset1 = axs[0].contourf(X, Y, Z, levels, norm=norm, - cmap=cm.get_cmap(cmap, len(levels) - 1)) + cmap=cmap.resampled(len(levels) - 1)) # It is not necessary, but for the colormap, we need only the # number of levels minus 1. To avoid discretization error, use # either this number or a large number such as the default (256). @@ -97,19 +97,12 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.contour -matplotlib.pyplot.contour -matplotlib.axes.Axes.imshow -matplotlib.pyplot.imshow -matplotlib.figure.Figure.colorbar -matplotlib.pyplot.colorbar -matplotlib.colors.Normalize +# - `matplotlib.axes.Axes.contour` / `matplotlib.pyplot.contour` +# - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow` +# - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar` +# - `matplotlib.colors.Normalize` diff --git a/examples/images_contours_and_fields/contour_label_demo.py b/examples/images_contours_and_fields/contour_label_demo.py index 733a17be48b9..0ea3d3694ca1 100644 --- a/examples/images_contours_and_fields/contour_label_demo.py +++ b/examples/images_contours_and_fields/contour_label_demo.py @@ -10,7 +10,6 @@ `. """ -import matplotlib import numpy as np import matplotlib.ticker as ticker import matplotlib.pyplot as plt @@ -76,17 +75,12 @@ def fmt(x): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown -# in this example: - -matplotlib.axes.Axes.contour -matplotlib.pyplot.contour -matplotlib.axes.Axes.clabel -matplotlib.pyplot.clabel -matplotlib.ticker.LogFormatterMathtext -matplotlib.ticker.TickHelper.create_dummy_axis +# - `matplotlib.axes.Axes.contour` / `matplotlib.pyplot.contour` +# - `matplotlib.axes.Axes.clabel` / `matplotlib.pyplot.clabel` +# - `matplotlib.ticker.LogFormatterMathtext` +# - `matplotlib.ticker.TickHelper.create_dummy_axis` diff --git a/examples/images_contours_and_fields/contourf_demo.py b/examples/images_contours_and_fields/contourf_demo.py index 68e540e34e9b..e51e2e6001dd 100644 --- a/examples/images_contours_and_fields/contourf_demo.py +++ b/examples/images_contours_and_fields/contourf_demo.py @@ -33,18 +33,20 @@ interior = np.sqrt(X**2 + Y**2) < 0.5 Z[interior] = np.ma.masked -# We are using automatic selection of contour levels; -# this is usually not such a good idea, because they don't -# occur on nice boundaries, but we do it here for purposes -# of illustration. +############################################################################# +# Automatic contour levels +# ------------------------ +# We are using automatic selection of contour levels; this is usually not such +# a good idea, because they don't occur on nice boundaries, but we do it here +# for purposes of illustration. fig1, ax2 = plt.subplots(constrained_layout=True) CS = ax2.contourf(X, Y, Z, 10, cmap=plt.cm.bone, origin=origin) -# Note that in the following, we explicitly pass in a subset of -# the contour levels used for the filled contours. Alternatively, -# We could pass in additional levels to provide extra resolution, -# or leave out the levels kwarg to use all of the original levels. +# Note that in the following, we explicitly pass in a subset of the contour +# levels used for the filled contours. Alternatively, we could pass in +# additional levels to provide extra resolution, or leave out the *levels* +# keyword argument to use all of the original levels. CS2 = ax2.contour(CS, levels=CS.levels[::2], colors='r', origin=origin) @@ -58,10 +60,13 @@ # Add the contour line levels to the colorbar cbar.add_lines(CS2) +############################################################################# +# Explicit contour levels +# ----------------------- +# Now make a contour plot with the levels specified, and with the colormap +# generated automatically from a list of colors. + fig2, ax2 = plt.subplots(constrained_layout=True) -# Now make a contour plot with the levels specified, -# and with the colormap generated automatically from a list -# of colors. levels = [-1.5, -1, -0.5, 0, 0.5, 1] CS3 = ax2.contourf(X, Y, Z, levels, colors=('r', 'g', 'b'), @@ -84,10 +89,12 @@ # needs from the ContourSet object, CS3. fig2.colorbar(CS3) +############################################################################# +# Extension settings +# ------------------ # Illustrate all 4 possible "extend" settings: extends = ["neither", "both", "min", "max"] -cmap = plt.cm.get_cmap("winter") -cmap = cmap.with_extremes(under="magenta", over="yellow") +cmap = plt.colormaps["winter"].with_extremes(under="magenta", over="yellow") # Note: contouring simply excludes masked or nan regions, so # instead of using the "bad" colormap value for them, it draws # nothing at all in them. Therefore the following would have @@ -96,7 +103,7 @@ fig, axs = plt.subplots(2, 2, constrained_layout=True) -for ax, extend in zip(axs.ravel(), extends): +for ax, extend in zip(axs.flat, extends): cs = ax.contourf(X, Y, Z, levels, cmap=cmap, extend=extend, origin=origin) fig.colorbar(cs, ax=ax, shrink=0.9) ax.set_title("extend = %s" % extend) @@ -106,24 +113,16 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.contour -matplotlib.pyplot.contour -matplotlib.axes.Axes.contourf -matplotlib.pyplot.contourf -matplotlib.axes.Axes.clabel -matplotlib.pyplot.clabel -matplotlib.figure.Figure.colorbar -matplotlib.pyplot.colorbar -matplotlib.colors.Colormap -matplotlib.colors.Colormap.set_bad -matplotlib.colors.Colormap.set_under -matplotlib.colors.Colormap.set_over +# - `matplotlib.axes.Axes.contour` / `matplotlib.pyplot.contour` +# - `matplotlib.axes.Axes.contourf` / `matplotlib.pyplot.contourf` +# - `matplotlib.axes.Axes.clabel` / `matplotlib.pyplot.clabel` +# - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar` +# - `matplotlib.colors.Colormap` +# - `matplotlib.colors.Colormap.set_bad` +# - `matplotlib.colors.Colormap.set_under` +# - `matplotlib.colors.Colormap.set_over` diff --git a/examples/images_contours_and_fields/contourf_hatching.py b/examples/images_contours_and_fields/contourf_hatching.py index 1711556db70a..10b4698eadc9 100644 --- a/examples/images_contours_and_fields/contourf_hatching.py +++ b/examples/images_contours_and_fields/contourf_hatching.py @@ -42,22 +42,14 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.contour -matplotlib.pyplot.contour -matplotlib.axes.Axes.contourf -matplotlib.pyplot.contourf -matplotlib.figure.Figure.colorbar -matplotlib.pyplot.colorbar -matplotlib.axes.Axes.legend -matplotlib.pyplot.legend -matplotlib.contour.ContourSet -matplotlib.contour.ContourSet.legend_elements +# - `matplotlib.axes.Axes.contour` / `matplotlib.pyplot.contour` +# - `matplotlib.axes.Axes.contourf` / `matplotlib.pyplot.contourf` +# - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar` +# - `matplotlib.axes.Axes.legend` / `matplotlib.pyplot.legend` +# - `matplotlib.contour.ContourSet` +# - `matplotlib.contour.ContourSet.legend_elements` diff --git a/examples/images_contours_and_fields/contourf_log.py b/examples/images_contours_and_fields/contourf_log.py index 813282249cd9..2f3976207b72 100644 --- a/examples/images_contours_and_fields/contourf_log.py +++ b/examples/images_contours_and_fields/contourf_log.py @@ -50,19 +50,12 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.contourf -matplotlib.pyplot.contourf -matplotlib.figure.Figure.colorbar -matplotlib.pyplot.colorbar -matplotlib.axes.Axes.legend -matplotlib.pyplot.legend -matplotlib.ticker.LogLocator +# - `matplotlib.axes.Axes.contourf` / `matplotlib.pyplot.contourf` +# - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar` +# - `matplotlib.axes.Axes.legend` / `matplotlib.pyplot.legend` +# - `matplotlib.ticker.LogLocator` diff --git a/examples/images_contours_and_fields/demo_bboximage.py b/examples/images_contours_and_fields/demo_bboximage.py index 15f38f7bcd95..0d43e328cb38 100644 --- a/examples/images_contours_and_fields/demo_bboximage.py +++ b/examples/images_contours_and_fields/demo_bboximage.py @@ -38,18 +38,18 @@ a = np.vstack((a, a)) # List of all colormaps; skip reversed colormaps. -maps = sorted(m for m in plt.colormaps() if not m.endswith("_r")) +cmap_names = sorted(m for m in plt.colormaps if not m.endswith("_r")) ncol = 2 -nrow = len(maps)//ncol + 1 +nrow = len(cmap_names) // ncol + 1 xpad_fraction = 0.3 -dx = 1./(ncol + xpad_fraction*(ncol - 1)) +dx = 1 / (ncol + xpad_fraction * (ncol - 1)) ypad_fraction = 0.3 -dy = 1./(nrow + ypad_fraction*(nrow - 1)) +dy = 1 / (nrow + ypad_fraction * (nrow - 1)) -for i, m in enumerate(maps): +for i, cmap_name in enumerate(cmap_names): ix, iy = divmod(i, nrow) bbox0 = Bbox.from_bounds(ix*dx*(1 + xpad_fraction), @@ -58,7 +58,7 @@ bbox = TransformedBbox(bbox0, ax2.transAxes) bbox_image = BboxImage(bbox, - cmap=plt.get_cmap(m), + cmap=cmap_name, norm=None, origin=None, **kwargs @@ -71,16 +71,12 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.image.BboxImage -matplotlib.transforms.Bbox -matplotlib.transforms.TransformedBbox -matplotlib.text.Text +# - `matplotlib.image.BboxImage` +# - `matplotlib.transforms.Bbox` +# - `matplotlib.transforms.TransformedBbox` +# - `matplotlib.text.Text` diff --git a/examples/images_contours_and_fields/figimage_demo.py b/examples/images_contours_and_fields/figimage_demo.py index ef805576cae0..1b1f1b19d694 100644 --- a/examples/images_contours_and_fields/figimage_demo.py +++ b/examples/images_contours_and_fields/figimage_demo.py @@ -7,7 +7,6 @@ """ import numpy as np -import matplotlib import matplotlib.pyplot as plt @@ -22,14 +21,10 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -matplotlib.figure.Figure -matplotlib.figure.Figure.figimage -matplotlib.pyplot.figimage +# - `matplotlib.figure.Figure` +# - `matplotlib.figure.Figure.figimage` / `matplotlib.pyplot.figimage` diff --git a/examples/images_contours_and_fields/image_annotated_heatmap.py b/examples/images_contours_and_fields/image_annotated_heatmap.py index 76e6f60a51e0..16054d145ea5 100644 --- a/examples/images_contours_and_fields/image_annotated_heatmap.py +++ b/examples/images_contours_and_fields/image_annotated_heatmap.py @@ -39,6 +39,7 @@ import numpy as np import matplotlib +import matplotlib as mpl import matplotlib.pyplot as plt # sphinx_gallery_thumbnail_number = 2 @@ -59,12 +60,9 @@ fig, ax = plt.subplots() im = ax.imshow(harvest) -# We want to show all ticks... -ax.set_xticks(np.arange(len(farmers))) -ax.set_yticks(np.arange(len(vegetables))) -# ... and label them with the respective list entries -ax.set_xticklabels(farmers) -ax.set_yticklabels(vegetables) +# Show all ticks and label them with the respective list entries +ax.set_xticks(np.arange(len(farmers)), labels=farmers) +ax.set_yticks(np.arange(len(vegetables)), labels=vegetables) # Rotate the tick labels and set their alignment. plt.setp(ax.get_xticklabels(), rotation=45, ha="right", @@ -100,18 +98,18 @@ def heatmap(data, row_labels, col_labels, ax=None, - cbar_kw={}, cbarlabel="", **kwargs): + cbar_kw=None, cbarlabel="", **kwargs): """ Create a heatmap from a numpy array and two lists of labels. Parameters ---------- data - A 2D numpy array of shape (N, M). + A 2D numpy array of shape (M, N). row_labels - A list or array of length N with the labels for the rows. + A list or array of length M with the labels for the rows. col_labels - A list or array of length M with the labels for the columns. + A list or array of length N with the labels for the columns. ax A `matplotlib.axes.Axes` instance to which the heatmap is plotted. If not provided, use current axes or create a new one. Optional. @@ -123,9 +121,12 @@ def heatmap(data, row_labels, col_labels, ax=None, All other arguments are forwarded to `imshow`. """ - if not ax: + if ax is None: ax = plt.gca() + if cbar_kw is None: + cbar_kw = {} + # Plot the heatmap im = ax.imshow(data, **kwargs) @@ -133,12 +134,9 @@ def heatmap(data, row_labels, col_labels, ax=None, cbar = ax.figure.colorbar(im, ax=ax, **cbar_kw) cbar.ax.set_ylabel(cbarlabel, rotation=-90, va="bottom") - # We want to show all ticks... - ax.set_xticks(np.arange(data.shape[1])) - ax.set_yticks(np.arange(data.shape[0])) - # ... and label them with the respective list entries. - ax.set_xticklabels(col_labels) - ax.set_yticklabels(row_labels) + # Show all ticks and label them with the respective list entries. + ax.set_xticks(np.arange(data.shape[1]), labels=col_labels) + ax.set_yticks(np.arange(data.shape[0]), labels=row_labels) # Let the horizontal axes labeling appear on top. ax.tick_params(top=True, bottom=False, @@ -275,7 +273,7 @@ def annotate_heatmap(im, data=None, valfmt="{x:.2f}", fmt = matplotlib.ticker.FuncFormatter(lambda x, pos: qrates[::-1][norm(x)]) im, _ = heatmap(data, y, x, ax=ax3, - cmap=plt.get_cmap("PiYG", 7), norm=norm, + cmap=mpl.colormaps["PiYG"].resampled(7), norm=norm, cbar_kw=dict(ticks=np.arange(-3, 4), format=fmt), cbarlabel="Quality Rating") @@ -305,15 +303,10 @@ def func(x, pos): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The usage of the following functions and methods is shown in this example: - - -matplotlib.axes.Axes.imshow -matplotlib.pyplot.imshow -matplotlib.figure.Figure.colorbar -matplotlib.pyplot.colorbar +# - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow` +# - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar` diff --git a/examples/images_contours_and_fields/image_antialiasing.py b/examples/images_contours_and_fields/image_antialiasing.py index 9fd2e3954e31..cb12bc3d9319 100644 --- a/examples/images_contours_and_fields/image_antialiasing.py +++ b/examples/images_contours_and_fields/image_antialiasing.py @@ -5,12 +5,21 @@ Images are represented by discrete pixels, either on the screen or in an image file. When data that makes up the image has a different resolution -than its representation on the screen we will see aliasing effects. +than its representation on the screen we will see aliasing effects. How +noticeable these are depends on how much down-sampling takes place in +the change of resolution (if any). -The default image interpolation in Matplotlib is 'antialiased'. This uses a -hanning interpolation for reduced aliasing in most situations. Only when there -is upsampling by a factor of 1, 2 or >=3 is 'nearest' neighbor interpolation -used. +When subsampling data, aliasing is reduced by smoothing first and then +subsampling the smoothed data. In Matplotlib, we can do that +smoothing before mapping the data to colors, or we can do the smoothing +on the RGB(A) data in the final image. The difference between these is +shown below, and controlled with the *interpolation_stage* keyword argument. + +The default image interpolation in Matplotlib is 'antialiased', and +it is applied to the data. This uses a +hanning interpolation on the data provided by the user for reduced aliasing +in most situations. Only when there is upsampling by a factor of 1, 2 or +>=3 is 'nearest' neighbor interpolation used. Other anti-aliasing filters can be specified in `.Axes.imshow` using the *interpolation* keyword argument. @@ -20,26 +29,55 @@ import matplotlib.pyplot as plt ############################################################################### -# First we generate a 500x500 px image with varying frequency content: -x = np.arange(500) / 500 - 0.5 -y = np.arange(500) / 500 - 0.5 +# First we generate a 450x450 pixel image with varying frequency content: +N = 450 +x = np.arange(N) / N - 0.5 +y = np.arange(N) / N - 0.5 +aa = np.ones((N, N)) +aa[::2, :] = -1 X, Y = np.meshgrid(x, y) R = np.sqrt(X**2 + Y**2) -f0 = 10 -k = 250 +f0 = 5 +k = 100 a = np.sin(np.pi * 2 * (f0 * R + k * R**2 / 2)) - - +# make the left hand side of this +a[:int(N / 2), :][R[:int(N / 2), :] < 0.4] = -1 +a[:int(N / 2), :][R[:int(N / 2), :] < 0.3] = 1 +aa[:, int(N / 3):] = a[:, int(N / 3):] +a = aa ############################################################################### -# The following images are subsampled from 500 data pixels to 303 rendered -# pixels. The Moire patterns in the 'nearest' interpolation are caused by the -# high-frequency data being subsampled. The 'antialiased' image +# The following images are subsampled from 450 data pixels to either +# 125 pixels or 250 pixels (depending on your display). +# The Moire patterns in the 'nearest' interpolation are caused by the +# high-frequency data being subsampled. The 'antialiased' imaged # still has some Moire patterns as well, but they are greatly reduced. -fig, axs = plt.subplots(1, 2, figsize=(7, 4), constrained_layout=True) -for ax, interp in zip(axs, ['nearest', 'antialiased']): - ax.imshow(a, interpolation=interp, cmap='gray') - ax.set_title(f"interpolation='{interp}'") +# +# There are substantial differences between the 'data' interpolation and +# the 'rgba' interpolation. The alternating bands of red and blue on the +# left third of the image are subsampled. By interpolating in 'data' space +# (the default) the antialiasing filter makes the stripes close to white, +# because the average of -1 and +1 is zero, and zero is white in this +# colormap. +# +# Conversely, when the anti-aliasing occurs in 'rgba' space, the red and +# blue are combined visually to make purple. This behaviour is more like a +# typical image processing package, but note that purple is not in the +# original colormap, so it is no longer possible to invert individual +# pixels back to their data value. + +fig, axs = plt.subplots(2, 2, figsize=(5, 6), constrained_layout=True) +axs[0, 0].imshow(a, interpolation='nearest', cmap='RdBu_r') +axs[0, 0].set_xlim(100, 200) +axs[0, 0].set_ylim(275, 175) +axs[0, 0].set_title('Zoom') + +for ax, interp, space in zip(axs.flat[1:], + ['nearest', 'antialiased', 'antialiased'], + ['data', 'data', 'rgba']): + ax.imshow(a, interpolation=interp, interpolation_stage=space, + cmap='RdBu_r') + ax.set_title(f"interpolation='{interp}'\nspace='{space}'") plt.show() ############################################################################### @@ -63,7 +101,7 @@ plt.show() ############################################################################### -# Apart from the default 'hanning' antialiasing `~.Axes.imshow` supports a +# Apart from the default 'hanning' antialiasing, `~.Axes.imshow` supports a # number of different interpolation algorithms, which may work better or # worse depending on the pattern. fig, axs = plt.subplots(1, 2, figsize=(7, 4), constrained_layout=True) @@ -72,16 +110,11 @@ ax.set_title(f"interpolation='{interp}'") plt.show() - ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.imshow +# - `matplotlib.axes.Axes.imshow` diff --git a/examples/images_contours_and_fields/image_clip_path.py b/examples/images_contours_and_fields/image_clip_path.py index 3123be4138e0..f51dacb2a09e 100644 --- a/examples/images_contours_and_fields/image_clip_path.py +++ b/examples/images_contours_and_fields/image_clip_path.py @@ -23,15 +23,10 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.imshow -matplotlib.pyplot.imshow -matplotlib.artist.Artist.set_clip_path +# - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow` +# - `matplotlib.artist.Artist.set_clip_path` diff --git a/examples/images_contours_and_fields/image_demo.py b/examples/images_contours_and_fields/image_demo.py index d24169518ea1..c929d7869950 100644 --- a/examples/images_contours_and_fields/image_demo.py +++ b/examples/images_contours_and_fields/image_demo.py @@ -46,28 +46,27 @@ with cbook.get_sample_data('grace_hopper.jpg') as image_file: image = plt.imread(image_file) -fig, ax = plt.subplots() -ax.imshow(image) -ax.axis('off') # clear x-axis and y-axis - - -# And another image - -# Data are 256x256 16-bit integers. +# And another image, using 256x256 16-bit integers. w, h = 256, 256 with cbook.get_sample_data('s1045.ima.gz') as datafile: s = datafile.read() A = np.frombuffer(s, np.uint16).astype(float).reshape((w, h)) - -fig, ax = plt.subplots() extent = (0, 25, 0, 25) -im = ax.imshow(A, cmap=plt.cm.hot, origin='upper', extent=extent) + +fig, ax = plt.subplot_mosaic([ + ['hopper', 'mri'] +], figsize=(7, 3.5)) + +ax['hopper'].imshow(image) +ax['hopper'].axis('off') # clear x-axis and y-axis + +im = ax['mri'].imshow(A, cmap=plt.cm.hot, origin='upper', extent=extent) markers = [(15.9, 14.5), (16.8, 15)] x, y = zip(*markers) -ax.plot(x, y, 'o') +ax['mri'].plot(x, y, 'o') -ax.set_title('MRI') +ax['mri'].set_title('MRI') plt.show() @@ -175,16 +174,11 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.imshow -matplotlib.pyplot.imshow -matplotlib.artist.Artist.set_clip_path -matplotlib.patches.PathPatch +# - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow` +# - `matplotlib.artist.Artist.set_clip_path` +# - `matplotlib.patches.PathPatch` diff --git a/examples/images_contours_and_fields/image_masked.py b/examples/images_contours_and_fields/image_masked.py index fff125abda63..fa5f8047a8ff 100644 --- a/examples/images_contours_and_fields/image_masked.py +++ b/examples/images_contours_and_fields/image_masked.py @@ -50,8 +50,7 @@ ax1.set_title('Green=low, Red=high, Blue=masked') cbar = fig.colorbar(im, extend='both', shrink=0.9, ax=ax1) cbar.set_label('uniform') -for ticklabel in ax1.xaxis.get_ticklabels(): - ticklabel.set_visible(False) +ax1.tick_params(axis='x', labelbottom=False) # Plot using a small number of colors, with unevenly spaced boundaries. im = ax2.imshow(Zm, interpolation='nearest', @@ -71,18 +70,12 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.imshow -matplotlib.pyplot.imshow -matplotlib.figure.Figure.colorbar -matplotlib.pyplot.colorbar -matplotlib.colors.BoundaryNorm -matplotlib.colorbar.ColorbarBase.set_label +# - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow` +# - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar` +# - `matplotlib.colors.BoundaryNorm` +# - `matplotlib.colorbar.Colorbar.set_label` diff --git a/examples/images_contours_and_fields/image_transparency_blend.py b/examples/images_contours_and_fields/image_transparency_blend.py index 7de2b42fedde..48e0a28c89b1 100644 --- a/examples/images_contours_and_fields/image_transparency_blend.py +++ b/examples/images_contours_and_fields/image_transparency_blend.py @@ -113,18 +113,12 @@ def normal_pdf(x, mean, var): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.imshow -matplotlib.pyplot.imshow -matplotlib.axes.Axes.contour -matplotlib.pyplot.contour -matplotlib.colors.Normalize -matplotlib.axes.Axes.set_axis_off +# - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow` +# - `matplotlib.axes.Axes.contour` / `matplotlib.pyplot.contour` +# - `matplotlib.colors.Normalize` +# - `matplotlib.axes.Axes.set_axis_off` diff --git a/examples/images_contours_and_fields/image_zcoord.py b/examples/images_contours_and_fields/image_zcoord.py index a97bff8a1583..7fc57ca10710 100644 --- a/examples/images_contours_and_fields/image_zcoord.py +++ b/examples/images_contours_and_fields/image_zcoord.py @@ -38,14 +38,10 @@ def format_coord(x, y): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.format_coord -matplotlib.axes.Axes.imshow +# - `matplotlib.axes.Axes.format_coord` +# - `matplotlib.axes.Axes.imshow` diff --git a/examples/images_contours_and_fields/interpolation_methods.py b/examples/images_contours_and_fields/interpolation_methods.py index e392c476d098..4d3151696dcd 100644 --- a/examples/images_contours_and_fields/interpolation_methods.py +++ b/examples/images_contours_and_fields/interpolation_methods.py @@ -10,12 +10,12 @@ If the interpolation is ``'none'``, then no interpolation is performed for the Agg, ps and pdf backends. Other backends will default to ``'antialiased'``. -For the Agg, ps and pdf backends, ``interpolation = 'none'`` works well when a -big image is scaled down, while ``interpolation = 'nearest'`` works well when +For the Agg, ps and pdf backends, ``interpolation='none'`` works well when a +big image is scaled down, while ``interpolation='nearest'`` works well when a small image is scaled up. See :doc:`/gallery/images_contours_and_fields/image_antialiasing` for a -discussion on the default ``interpolation="antialiased"`` option. +discussion on the default ``interpolation='antialiased'`` option. """ import matplotlib.pyplot as plt @@ -42,14 +42,9 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.imshow -matplotlib.pyplot.imshow +# - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow` diff --git a/examples/images_contours_and_fields/irregulardatagrid.py b/examples/images_contours_and_fields/irregulardatagrid.py index d49a47ee06d9..aedf8033c9b4 100644 --- a/examples/images_contours_and_fields/irregulardatagrid.py +++ b/examples/images_contours_and_fields/irregulardatagrid.py @@ -52,8 +52,8 @@ # Note that scipy.interpolate provides means to interpolate data on a grid # as well. The following would be an alternative to the four lines above: -#from scipy.interpolate import griddata -#zi = griddata((x, y), z, (xi[None, :], yi[:, None]), method='linear') +# from scipy.interpolate import griddata +# zi = griddata((x, y), z, (xi[None, :], yi[:, None]), method='linear') ax1.contour(xi, yi, zi, levels=14, linewidths=0.5, colors='k') cntr1 = ax1.contourf(xi, yi, zi, levels=14, cmap="RdBu_r") @@ -83,19 +83,12 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.contour -matplotlib.pyplot.contour -matplotlib.axes.Axes.contourf -matplotlib.pyplot.contourf -matplotlib.axes.Axes.tricontour -matplotlib.pyplot.tricontour -matplotlib.axes.Axes.tricontourf -matplotlib.pyplot.tricontourf +# - `matplotlib.axes.Axes.contour` / `matplotlib.pyplot.contour` +# - `matplotlib.axes.Axes.contourf` / `matplotlib.pyplot.contourf` +# - `matplotlib.axes.Axes.tricontour` / `matplotlib.pyplot.tricontour` +# - `matplotlib.axes.Axes.tricontourf` / `matplotlib.pyplot.tricontourf` diff --git a/examples/images_contours_and_fields/layer_images.py b/examples/images_contours_and_fields/layer_images.py index 5b2ba0738a68..abd8b2a9008e 100644 --- a/examples/images_contours_and_fields/layer_images.py +++ b/examples/images_contours_and_fields/layer_images.py @@ -43,14 +43,9 @@ def func3(x, y): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.imshow -matplotlib.pyplot.imshow +# - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow` diff --git a/examples/images_contours_and_fields/matshow.py b/examples/images_contours_and_fields/matshow.py index fbe93921c670..43a06bda9353 100644 --- a/examples/images_contours_and_fields/matshow.py +++ b/examples/images_contours_and_fields/matshow.py @@ -17,14 +17,9 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.matshow -matplotlib.pyplot.matshow +# - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow` diff --git a/examples/images_contours_and_fields/multi_image.py b/examples/images_contours_and_fields/multi_image.py index aa9067e55d06..e517e3f326ca 100644 --- a/examples/images_contours_and_fields/multi_image.py +++ b/examples/images_contours_and_fields/multi_image.py @@ -47,27 +47,21 @@ def update(changed_image): for im in images: - im.callbacksSM.connect('changed', update) + im.callbacks.connect('changed', update) plt.show() ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.imshow -matplotlib.pyplot.imshow -matplotlib.figure.Figure.colorbar -matplotlib.pyplot.colorbar -matplotlib.colors.Normalize -matplotlib.cm.ScalarMappable.set_cmap -matplotlib.cm.ScalarMappable.set_norm -matplotlib.cm.ScalarMappable.set_clim -matplotlib.cbook.CallbackRegistry.connect +# - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow` +# - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar` +# - `matplotlib.colors.Normalize` +# - `matplotlib.cm.ScalarMappable.set_cmap` +# - `matplotlib.cm.ScalarMappable.set_norm` +# - `matplotlib.cm.ScalarMappable.set_clim` +# - `matplotlib.cbook.CallbackRegistry.connect` diff --git a/examples/images_contours_and_fields/pcolor_demo.py b/examples/images_contours_and_fields/pcolor_demo.py index 4862bb583288..bc578f25cf09 100644 --- a/examples/images_contours_and_fields/pcolor_demo.py +++ b/examples/images_contours_and_fields/pcolor_demo.py @@ -111,22 +111,14 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.pcolor -matplotlib.pyplot.pcolor -matplotlib.axes.Axes.pcolormesh -matplotlib.pyplot.pcolormesh -matplotlib.axes.Axes.pcolorfast -matplotlib.axes.Axes.imshow -matplotlib.pyplot.imshow -matplotlib.figure.Figure.colorbar -matplotlib.pyplot.colorbar -matplotlib.colors.LogNorm +# - `matplotlib.axes.Axes.pcolor` / `matplotlib.pyplot.pcolor` +# - `matplotlib.axes.Axes.pcolormesh` / `matplotlib.pyplot.pcolormesh` +# - `matplotlib.axes.Axes.pcolorfast` +# - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow` +# - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar` +# - `matplotlib.colors.LogNorm` diff --git a/examples/images_contours_and_fields/pcolormesh_grids.py b/examples/images_contours_and_fields/pcolormesh_grids.py index 202a9d167616..3b628efe58bd 100644 --- a/examples/images_contours_and_fields/pcolormesh_grids.py +++ b/examples/images_contours_and_fields/pcolormesh_grids.py @@ -15,7 +15,6 @@ """ -import matplotlib import matplotlib.pyplot as plt import numpy as np @@ -121,12 +120,9 @@ def _annotate(ax, x, y, title): plt.show() ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -matplotlib.axes.Axes.pcolormesh -matplotlib.pyplot.pcolormesh +# - `matplotlib.axes.Axes.pcolormesh` / `matplotlib.pyplot.pcolormesh` diff --git a/examples/images_contours_and_fields/pcolormesh_levels.py b/examples/images_contours_and_fields/pcolormesh_levels.py index 7f1073295709..d42643f15574 100644 --- a/examples/images_contours_and_fields/pcolormesh_levels.py +++ b/examples/images_contours_and_fields/pcolormesh_levels.py @@ -8,7 +8,6 @@ """ -import matplotlib import matplotlib.pyplot as plt from matplotlib.colors import BoundaryNorm from matplotlib.ticker import MaxNLocator @@ -95,7 +94,7 @@ # pick the desired colormap, sensible levels, and define a normalization # instance which takes data values and translates those into levels. -cmap = plt.get_cmap('PiYG') +cmap = plt.colormaps['PiYG'] norm = BoundaryNorm(levels, ncolors=cmap.N, clip=True) fig, (ax0, ax1) = plt.subplots(nrows=2) @@ -121,18 +120,13 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -matplotlib.axes.Axes.pcolormesh -matplotlib.pyplot.pcolormesh -matplotlib.axes.Axes.contourf -matplotlib.pyplot.contourf -matplotlib.figure.Figure.colorbar -matplotlib.pyplot.colorbar -matplotlib.colors.BoundaryNorm -matplotlib.ticker.MaxNLocator +# - `matplotlib.axes.Axes.pcolormesh` / `matplotlib.pyplot.pcolormesh` +# - `matplotlib.axes.Axes.contourf` / `matplotlib.pyplot.contourf` +# - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar` +# - `matplotlib.colors.BoundaryNorm` +# - `matplotlib.ticker.MaxNLocator` diff --git a/examples/images_contours_and_fields/plot_streamplot.py b/examples/images_contours_and_fields/plot_streamplot.py index 2051764bb383..fd587527fbc5 100644 --- a/examples/images_contours_and_fields/plot_streamplot.py +++ b/examples/images_contours_and_fields/plot_streamplot.py @@ -11,10 +11,12 @@ * Varying the line width along a streamline. * Controlling the starting points of streamlines. * Streamlines skipping masked regions and NaN values. +* Unbroken streamlines even when exceeding the limit of lines within a single + grid cell. """ import numpy as np import matplotlib.pyplot as plt -import matplotlib.gridspec as gridspec + w = 3 Y, X = np.mgrid[-w:w:100j, -w:w:100j] @@ -22,38 +24,34 @@ V = 1 + X - Y**2 speed = np.sqrt(U**2 + V**2) -fig = plt.figure(figsize=(7, 9)) -gs = gridspec.GridSpec(nrows=3, ncols=2, height_ratios=[1, 1, 2]) +fig, axs = plt.subplots(3, 2, figsize=(7, 9), height_ratios=[1, 1, 2]) +axs = axs.flat # Varying density along a streamline -ax0 = fig.add_subplot(gs[0, 0]) -ax0.streamplot(X, Y, U, V, density=[0.5, 1]) -ax0.set_title('Varying Density') +axs[0].streamplot(X, Y, U, V, density=[0.5, 1]) +axs[0].set_title('Varying Density') # Varying color along a streamline -ax1 = fig.add_subplot(gs[0, 1]) -strm = ax1.streamplot(X, Y, U, V, color=U, linewidth=2, cmap='autumn') +strm = axs[1].streamplot(X, Y, U, V, color=U, linewidth=2, cmap='autumn') fig.colorbar(strm.lines) -ax1.set_title('Varying Color') +axs[1].set_title('Varying Color') # Varying line width along a streamline -ax2 = fig.add_subplot(gs[1, 0]) lw = 5*speed / speed.max() -ax2.streamplot(X, Y, U, V, density=0.6, color='k', linewidth=lw) -ax2.set_title('Varying Line Width') +axs[2].streamplot(X, Y, U, V, density=0.6, color='k', linewidth=lw) +axs[2].set_title('Varying Line Width') # Controlling the starting points of the streamlines seed_points = np.array([[-2, -1, 0, 1, 2, -1], [-2, -1, 0, 1, 2, 2]]) -ax3 = fig.add_subplot(gs[1, 1]) -strm = ax3.streamplot(X, Y, U, V, color=U, linewidth=2, - cmap='autumn', start_points=seed_points.T) +strm = axs[3].streamplot(X, Y, U, V, color=U, linewidth=2, + cmap='autumn', start_points=seed_points.T) fig.colorbar(strm.lines) -ax3.set_title('Controlling Starting Points') +axs[3].set_title('Controlling Starting Points') # Displaying the starting points with blue symbols. -ax3.plot(seed_points[0], seed_points[1], 'bo') -ax3.set(xlim=(-w, w), ylim=(-w, w)) +axs[3].plot(seed_points[0], seed_points[1], 'bo') +axs[3].set(xlim=(-w, w), ylim=(-w, w)) # Create a mask mask = np.zeros(U.shape, dtype=bool) @@ -61,26 +59,24 @@ U[:20, :20] = np.nan U = np.ma.array(U, mask=mask) -ax4 = fig.add_subplot(gs[2:, :]) -ax4.streamplot(X, Y, U, V, color='r') -ax4.set_title('Streamplot with Masking') +axs[4].streamplot(X, Y, U, V, color='r') +axs[4].set_title('Streamplot with Masking') + +axs[4].imshow(~mask, extent=(-w, w, -w, w), alpha=0.5, cmap='gray', + aspect='auto') +axs[4].set_aspect('equal') -ax4.imshow(~mask, extent=(-w, w, -w, w), alpha=0.5, cmap='gray', aspect='auto') -ax4.set_aspect('equal') +axs[5].streamplot(X, Y, U, V, broken_streamlines=False) +axs[5].set_title('Streamplot with unbroken streamlines') plt.tight_layout() plt.show() ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.streamplot -matplotlib.pyplot.streamplot -matplotlib.gridspec -matplotlib.gridspec.GridSpec +# - `matplotlib.axes.Axes.streamplot` / `matplotlib.pyplot.streamplot` +# - `matplotlib.gridspec.GridSpec` diff --git a/examples/images_contours_and_fields/quadmesh_demo.py b/examples/images_contours_and_fields/quadmesh_demo.py index 005d693f7aa0..430c428f5170 100644 --- a/examples/images_contours_and_fields/quadmesh_demo.py +++ b/examples/images_contours_and_fields/quadmesh_demo.py @@ -9,7 +9,7 @@ This demo illustrates a bug in quadmesh with masked data. """ -from matplotlib import cm, pyplot as plt +from matplotlib import pyplot as plt import numpy as np n = 12 @@ -29,7 +29,7 @@ axs[0].set_title('Without masked values') # You can control the color of the masked region. -cmap = cm.get_cmap(plt.rcParams['image.cmap']).with_extremes(bad='y') +cmap = plt.colormaps[plt.rcParams['image.cmap']].with_extremes(bad='y') axs[1].pcolormesh(Qx, Qz, Zm, shading='gouraud', cmap=cmap) axs[1].set_title('With masked values') @@ -42,13 +42,9 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.pcolormesh -matplotlib.pyplot.pcolormesh +# - `matplotlib.axes.Axes.pcolormesh` / `matplotlib.pyplot.pcolormesh` diff --git a/examples/images_contours_and_fields/quiver_demo.py b/examples/images_contours_and_fields/quiver_demo.py index f06f4e17198a..396e1c047e1b 100644 --- a/examples/images_contours_and_fields/quiver_demo.py +++ b/examples/images_contours_and_fields/quiver_demo.py @@ -54,15 +54,10 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.quiver -matplotlib.pyplot.quiver -matplotlib.axes.Axes.quiverkey -matplotlib.pyplot.quiverkey +# - `matplotlib.axes.Axes.quiver` / `matplotlib.pyplot.quiver` +# - `matplotlib.axes.Axes.quiverkey` / `matplotlib.pyplot.quiverkey` diff --git a/examples/images_contours_and_fields/quiver_simple_demo.py b/examples/images_contours_and_fields/quiver_simple_demo.py index 0393a4857cbd..1437d6f138c2 100644 --- a/examples/images_contours_and_fields/quiver_simple_demo.py +++ b/examples/images_contours_and_fields/quiver_simple_demo.py @@ -24,15 +24,10 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.quiver -matplotlib.pyplot.quiver -matplotlib.axes.Axes.quiverkey -matplotlib.pyplot.quiverkey +# - `matplotlib.axes.Axes.quiver` / `matplotlib.pyplot.quiver` +# - `matplotlib.axes.Axes.quiverkey` / `matplotlib.pyplot.quiverkey` diff --git a/examples/images_contours_and_fields/shading_example.py b/examples/images_contours_and_fields/shading_example.py index e1357f26070f..c3b969dcff8b 100644 --- a/examples/images_contours_and_fields/shading_example.py +++ b/examples/images_contours_and_fields/shading_example.py @@ -7,7 +7,7 @@ `Generic Mapping Tools`_. .. _Mathematica: http://reference.wolfram.com/mathematica/ref/ReliefPlot.html -.. _Generic Mapping Tools: https://gmt.soest.hawaii.edu/ +.. _Generic Mapping Tools: https://www.generic-mapping-tools.org/ """ import numpy as np @@ -64,15 +64,10 @@ def compare(z, cmap, ve=1): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown in this -# example: - -import matplotlib -matplotlib.colors.LightSource -matplotlib.axes.Axes.imshow -matplotlib.pyplot.imshow +# - `matplotlib.colors.LightSource` +# - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow` diff --git a/examples/images_contours_and_fields/specgram_demo.py b/examples/images_contours_and_fields/specgram_demo.py index 6a7eb8d696f8..43ee7b3c1af8 100644 --- a/examples/images_contours_and_fields/specgram_demo.py +++ b/examples/images_contours_and_fields/specgram_demo.py @@ -38,14 +38,9 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.specgram -matplotlib.pyplot.specgram +# - `matplotlib.axes.Axes.specgram` / `matplotlib.pyplot.specgram` diff --git a/examples/images_contours_and_fields/spy_demos.py b/examples/images_contours_and_fields/spy_demos.py index af70f48cd4c8..a61a3ffefcad 100644 --- a/examples/images_contours_and_fields/spy_demos.py +++ b/examples/images_contours_and_fields/spy_demos.py @@ -33,14 +33,9 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.spy -matplotlib.pyplot.spy +# - `matplotlib.axes.Axes.spy` / `matplotlib.pyplot.spy` diff --git a/examples/images_contours_and_fields/tricontour_demo.py b/examples/images_contours_and_fields/tricontour_demo.py index d657aef01af7..a5d4651b7818 100644 --- a/examples/images_contours_and_fields/tricontour_demo.py +++ b/examples/images_contours_and_fields/tricontour_demo.py @@ -45,6 +45,41 @@ ax1.tricontour(triang, z, colors='k') ax1.set_title('Contour plot of Delaunay triangulation') + +############################################################################### +# You could also specify hatching patterns along with different cmaps. + +fig2, ax2 = plt.subplots() +ax2.set_aspect("equal") +tcf = ax2.tricontourf( + triang, + z, + hatches=["*", "-", "/", "//", "\\", None], + cmap="cividis" +) +fig2.colorbar(tcf) +ax2.tricontour(triang, z, linestyles="solid", colors="k", linewidths=2.0) +ax2.set_title("Hatched Contour plot of Delaunay triangulation") + +############################################################################### +# You could also generate hatching patterns labeled with no color. + +fig3, ax3 = plt.subplots() +n_levels = 7 +tcf = ax3.tricontourf( + triang, + z, + n_levels, + colors="none", + hatches=[".", "/", "\\", None, "\\\\", "*"], +) +ax3.tricontour(triang, z, n_levels, colors="black", linestyles="-") + + +# create a legend for the contour set +artists, labels = tcf.legend_elements(str_format="{:2.1f}".format) +ax3.legend(artists, labels, handleheight=2, framealpha=1) + ############################################################################### # You can specify your own triangulation rather than perform a Delaunay # triangulation of the points, where each triangle is given by the indices of @@ -101,27 +136,25 @@ # object if the same triangulation was to be used more than once to save # duplicated calculations. -fig2, ax2 = plt.subplots() -ax2.set_aspect('equal') -tcf = ax2.tricontourf(x, y, triangles, z) -fig2.colorbar(tcf) -ax2.set_title('Contour plot of user-specified triangulation') -ax2.set_xlabel('Longitude (degrees)') -ax2.set_ylabel('Latitude (degrees)') +fig4, ax4 = plt.subplots() +ax4.set_aspect('equal') +tcf = ax4.tricontourf(x, y, triangles, z) +fig4.colorbar(tcf) +ax4.set_title('Contour plot of user-specified triangulation') +ax4.set_xlabel('Longitude (degrees)') +ax4.set_ylabel('Latitude (degrees)') plt.show() ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.tricontourf -matplotlib.pyplot.tricontourf -matplotlib.tri.Triangulation +# - `matplotlib.axes.Axes.tricontourf` / `matplotlib.pyplot.tricontourf` +# - `matplotlib.tri.Triangulation` +# - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar` +# - `matplotlib.axes.Axes.legend` / `matplotlib.pyplot.legend` +# - `matplotlib.contour.ContourSet.legend_elements` diff --git a/examples/images_contours_and_fields/tricontour_smooth_delaunay.py b/examples/images_contours_and_fields/tricontour_smooth_delaunay.py index ab187ef8eac4..0815e78fb32d 100644 --- a/examples/images_contours_and_fields/tricontour_smooth_delaunay.py +++ b/examples/images_contours_and_fields/tricontour_smooth_delaunay.py @@ -25,13 +25,12 @@ """ from matplotlib.tri import Triangulation, TriAnalyzer, UniformTriRefiner import matplotlib.pyplot as plt -import matplotlib.cm as cm import numpy as np -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- # Analytical test function -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- def experiment_res(x, y): """An analytic function representing experiment results.""" x = 2 * x @@ -44,9 +43,9 @@ def experiment_res(x, y): 2 * (x**2 + y**2)) return (np.max(z) - z) / (np.max(z) - np.min(z)) -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- # Generating the initial data test points and triangulation for the demo -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- # User parameters for data test points # Number of test data points, tested from 3 to 5000 for subdiv=3 @@ -83,9 +82,9 @@ def experiment_res(x, y): tri.set_mask(mask_init) -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- # Improving the triangulation before high-res plots: removing flat triangles -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- # masking badly shaped triangles at the border of the triangular mesh. mask = TriAnalyzer(tri).get_flat_tri_mask(min_circle_ratio) tri.set_mask(mask) @@ -102,9 +101,9 @@ def experiment_res(x, y): flat_tri.set_mask(~mask) -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- # Now the plots -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- # User options for plots plot_tri = True # plot of base triangulation plot_masked_tri = True # plot of excessively flat excluded triangles @@ -114,7 +113,6 @@ def experiment_res(x, y): # Graphical options for tricontouring levels = np.arange(0., 1., 0.025) -cmap = cm.get_cmap(name='Blues', lut=None) fig, ax = plt.subplots() ax.set_aspect('equal') @@ -122,11 +120,11 @@ def experiment_res(x, y): "(application to high-resolution tricontouring)") # 1) plot of the refined (computed) data contours: -ax.tricontour(tri_refi, z_test_refi, levels=levels, cmap=cmap, +ax.tricontour(tri_refi, z_test_refi, levels=levels, cmap='Blues', linewidths=[2.0, 0.5, 1.0, 0.5]) # 2) plot of the expected (analytical) data contours (dashed): if plot_expected: - ax.tricontour(tri_refi, z_expected, levels=levels, cmap=cmap, + ax.tricontour(tri_refi, z_expected, levels=levels, cmap='Blues', linestyles='--') # 3) plot of the fine mesh on which interpolation was done: if plot_refi_tri: @@ -142,22 +140,15 @@ def experiment_res(x, y): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.tricontour -matplotlib.pyplot.tricontour -matplotlib.axes.Axes.tricontourf -matplotlib.pyplot.tricontourf -matplotlib.axes.Axes.triplot -matplotlib.pyplot.triplot -matplotlib.tri -matplotlib.tri.Triangulation -matplotlib.tri.TriAnalyzer -matplotlib.tri.UniformTriRefiner +# - `matplotlib.axes.Axes.tricontour` / `matplotlib.pyplot.tricontour` +# - `matplotlib.axes.Axes.tricontourf` / `matplotlib.pyplot.tricontourf` +# - `matplotlib.axes.Axes.triplot` / `matplotlib.pyplot.triplot` +# - `matplotlib.tri` +# - `matplotlib.tri.Triangulation` +# - `matplotlib.tri.TriAnalyzer` +# - `matplotlib.tri.UniformTriRefiner` diff --git a/examples/images_contours_and_fields/tricontour_smooth_user.py b/examples/images_contours_and_fields/tricontour_smooth_user.py index ef82ff8ff4b2..c5a0a408f8e5 100644 --- a/examples/images_contours_and_fields/tricontour_smooth_user.py +++ b/examples/images_contours_and_fields/tricontour_smooth_user.py @@ -8,13 +8,12 @@ """ import matplotlib.tri as tri import matplotlib.pyplot as plt -import matplotlib.cm as cm import numpy as np -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- # Analytical test function -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- def function_z(x, y): r1 = np.sqrt((0.5 - x)**2 + (0.5 - y)**2) theta1 = np.arctan2(0.5 - x, 0.5 - y) @@ -25,9 +24,9 @@ def function_z(x, y): 0.7 * (x**2 + y**2)) return (np.max(z) - z) / (np.max(z) - np.min(z)) -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- # Creating a Triangulation -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- # First create the x and y coordinates of the points. n_angles = 20 n_radii = 10 @@ -52,22 +51,21 @@ def function_z(x, y): y[triang.triangles].mean(axis=1)) < min_radius) -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- # Refine data -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- refiner = tri.UniformTriRefiner(triang) tri_refi, z_test_refi = refiner.refine_field(z, subdiv=3) -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- # Plot the triangulation and the high-res iso-contours -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- fig, ax = plt.subplots() ax.set_aspect('equal') ax.triplot(triang, lw=0.5, color='white') levels = np.arange(0., 1., 0.025) -cmap = cm.get_cmap(name='terrain', lut=None) -ax.tricontourf(tri_refi, z_test_refi, levels=levels, cmap=cmap) +ax.tricontourf(tri_refi, z_test_refi, levels=levels, cmap='terrain') ax.tricontour(tri_refi, z_test_refi, levels=levels, colors=['0.25', '0.5', '0.5', '0.5', '0.5'], linewidths=[1.0, 0.5, 0.5, 0.5, 0.5]) @@ -78,19 +76,13 @@ def function_z(x, y): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.tricontour -matplotlib.pyplot.tricontour -matplotlib.axes.Axes.tricontourf -matplotlib.pyplot.tricontourf -matplotlib.tri -matplotlib.tri.Triangulation -matplotlib.tri.UniformTriRefiner +# - `matplotlib.axes.Axes.tricontour` / `matplotlib.pyplot.tricontour` +# - `matplotlib.axes.Axes.tricontourf` / `matplotlib.pyplot.tricontourf` +# - `matplotlib.tri` +# - `matplotlib.tri.Triangulation` +# - `matplotlib.tri.UniformTriRefiner` diff --git a/examples/images_contours_and_fields/trigradient_demo.py b/examples/images_contours_and_fields/trigradient_demo.py index d1120ebf59b5..5abda65f5307 100644 --- a/examples/images_contours_and_fields/trigradient_demo.py +++ b/examples/images_contours_and_fields/trigradient_demo.py @@ -9,13 +9,12 @@ from matplotlib.tri import ( Triangulation, UniformTriRefiner, CubicTriInterpolator) import matplotlib.pyplot as plt -import matplotlib.cm as cm import numpy as np -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- # Electrical potential of a dipole -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- def dipole_potential(x, y): """The electric dipole potential V, at position *x*, *y*.""" r_sq = x**2 + y**2 @@ -24,9 +23,9 @@ def dipole_potential(x, y): return (np.max(z) - z) / (np.max(z) - np.min(z)) -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- # Creating a Triangulation -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- # First create the x and y coordinates of the points. n_angles = 30 n_radii = 10 @@ -50,23 +49,23 @@ def dipole_potential(x, y): y[triang.triangles].mean(axis=1)) < min_radius) -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- # Refine data - interpolates the electrical potential V -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- refiner = UniformTriRefiner(triang) tri_refi, z_test_refi = refiner.refine_field(V, subdiv=3) -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- # Computes the electrical field (Ex, Ey) as gradient of electrical potential -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- tci = CubicTriInterpolator(triang, -V) # Gradient requested here at the mesh nodes but could be anywhere else: (Ex, Ey) = tci.gradient(triang.x, triang.y) E_norm = np.sqrt(Ex**2 + Ey**2) -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- # Plot the triangulation, the potential iso-contours and the vector field -#----------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- fig, ax = plt.subplots() ax.set_aspect('equal') # Enforce the margins, and enlarge them to give room for the vectors. @@ -76,8 +75,7 @@ def dipole_potential(x, y): ax.triplot(triang, color='0.8') levels = np.arange(0., 1., 0.01) -cmap = cm.get_cmap(name='hot', lut=None) -ax.tricontour(tri_refi, z_test_refi, levels=levels, cmap=cmap, +ax.tricontour(tri_refi, z_test_refi, levels=levels, cmap='hot', linewidths=[2.0, 1.0, 1.0, 1.0]) # Plots direction of the electrical vector field ax.quiver(triang.x, triang.y, Ex/E_norm, Ey/E_norm, @@ -89,23 +87,16 @@ def dipole_potential(x, y): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.tricontour -matplotlib.pyplot.tricontour -matplotlib.axes.Axes.triplot -matplotlib.pyplot.triplot -matplotlib.tri -matplotlib.tri.Triangulation -matplotlib.tri.CubicTriInterpolator -matplotlib.tri.CubicTriInterpolator.gradient -matplotlib.tri.UniformTriRefiner -matplotlib.axes.Axes.quiver -matplotlib.pyplot.quiver +# - `matplotlib.axes.Axes.tricontour` / `matplotlib.pyplot.tricontour` +# - `matplotlib.axes.Axes.triplot` / `matplotlib.pyplot.triplot` +# - `matplotlib.tri` +# - `matplotlib.tri.Triangulation` +# - `matplotlib.tri.CubicTriInterpolator` +# - `matplotlib.tri.CubicTriInterpolator.gradient` +# - `matplotlib.tri.UniformTriRefiner` +# - `matplotlib.axes.Axes.quiver` / `matplotlib.pyplot.quiver` diff --git a/examples/images_contours_and_fields/triinterp_demo.py b/examples/images_contours_and_fields/triinterp_demo.py index 087a2839ac11..b06d0086d248 100644 --- a/examples/images_contours_and_fields/triinterp_demo.py +++ b/examples/images_contours_and_fields/triinterp_demo.py @@ -61,24 +61,16 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.tricontourf -matplotlib.pyplot.tricontourf -matplotlib.axes.Axes.triplot -matplotlib.pyplot.triplot -matplotlib.axes.Axes.contourf -matplotlib.pyplot.contourf -matplotlib.axes.Axes.plot -matplotlib.pyplot.plot -matplotlib.tri -matplotlib.tri.LinearTriInterpolator -matplotlib.tri.CubicTriInterpolator -matplotlib.tri.Triangulation +# - `matplotlib.axes.Axes.tricontourf` / `matplotlib.pyplot.tricontourf` +# - `matplotlib.axes.Axes.triplot` / `matplotlib.pyplot.triplot` +# - `matplotlib.axes.Axes.contourf` / `matplotlib.pyplot.contourf` +# - `matplotlib.axes.Axes.plot` / `matplotlib.pyplot.plot` +# - `matplotlib.tri` +# - `matplotlib.tri.LinearTriInterpolator` +# - `matplotlib.tri.CubicTriInterpolator` +# - `matplotlib.tri.Triangulation` diff --git a/examples/images_contours_and_fields/tripcolor_demo.py b/examples/images_contours_and_fields/tripcolor_demo.py index 67f638ad4364..f78d275df2fc 100644 --- a/examples/images_contours_and_fields/tripcolor_demo.py +++ b/examples/images_contours_and_fields/tripcolor_demo.py @@ -113,7 +113,7 @@ # object if the same triangulation was to be used more than once to save # duplicated calculations. # Can specify one color value per face rather than one per point by using the -# facecolors kwarg. +# *facecolors* keyword argument. fig3, ax3 = plt.subplots() ax3.set_aspect('equal') @@ -127,16 +127,11 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.tripcolor -matplotlib.pyplot.tripcolor -matplotlib.tri -matplotlib.tri.Triangulation +# - `matplotlib.axes.Axes.tripcolor` / `matplotlib.pyplot.tripcolor` +# - `matplotlib.tri` +# - `matplotlib.tri.Triangulation` diff --git a/examples/images_contours_and_fields/triplot_demo.py b/examples/images_contours_and_fields/triplot_demo.py index 836848205e35..b25ca4a575c8 100644 --- a/examples/images_contours_and_fields/triplot_demo.py +++ b/examples/images_contours_and_fields/triplot_demo.py @@ -107,16 +107,11 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.triplot -matplotlib.pyplot.triplot -matplotlib.tri -matplotlib.tri.Triangulation +# - `matplotlib.axes.Axes.triplot` / `matplotlib.pyplot.triplot` +# - `matplotlib.tri` +# - `matplotlib.tri.Triangulation` diff --git a/examples/images_contours_and_fields/watermark_image.py b/examples/images_contours_and_fields/watermark_image.py index 38a3aa0d9656..a8f0b0d90b04 100644 --- a/examples/images_contours_and_fields/watermark_image.py +++ b/examples/images_contours_and_fields/watermark_image.py @@ -25,16 +25,11 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.image -matplotlib.image.imread -matplotlib.pyplot.imread -matplotlib.figure.Figure.figimage +# - `matplotlib.image` +# - `matplotlib.image.imread` / `matplotlib.pyplot.imread` +# - `matplotlib.figure.Figure.figimage` diff --git a/examples/lines_bars_and_markers/bar_colors.py b/examples/lines_bars_and_markers/bar_colors.py new file mode 100644 index 000000000000..35e7a64ef605 --- /dev/null +++ b/examples/lines_bars_and_markers/bar_colors.py @@ -0,0 +1,26 @@ +""" +============== +Bar color demo +============== + +This is an example showing how to control bar color and legend entries +using the *color* and *label* parameters of `~matplotlib.pyplot.bar`. +Note that labels with a preceding underscore won't show up in the legend. +""" + +import matplotlib.pyplot as plt + +fig, ax = plt.subplots() + +fruits = ['apple', 'blueberry', 'cherry', 'orange'] +counts = [40, 100, 30, 55] +bar_labels = ['red', 'blue', '_red', 'orange'] +bar_colors = ['tab:red', 'tab:blue', 'tab:red', 'tab:orange'] + +ax.bar(fruits, counts, label=bar_labels, color=bar_colors) + +ax.set_ylabel('fruit supply') +ax.set_title('Fruit supply by kind and color') +ax.legend(title='Fruit color') + +plt.show() diff --git a/examples/lines_bars_and_markers/bar_label_demo.py b/examples/lines_bars_and_markers/bar_label_demo.py index 3b5db07d71dd..3ed3f79018cc 100644 --- a/examples/lines_bars_and_markers/bar_label_demo.py +++ b/examples/lines_bars_and_markers/bar_label_demo.py @@ -14,7 +14,6 @@ ` examples. """ -import matplotlib import matplotlib.pyplot as plt import numpy as np @@ -41,8 +40,7 @@ ax.axhline(0, color='grey', linewidth=0.8) ax.set_ylabel('Scores') ax.set_title('Scores by group and gender') -ax.set_xticks(ind) -ax.set_xticklabels(('G1', 'G2', 'G3', 'G4', 'G5')) +ax.set_xticks(ind, labels=['G1', 'G2', 'G3', 'G4', 'G5']) ax.legend() # Label with label_type 'center' instead of the default 'edge' @@ -67,8 +65,7 @@ fig, ax = plt.subplots() hbars = ax.barh(y_pos, performance, xerr=error, align='center') -ax.set_yticks(y_pos) -ax.set_yticklabels(people) +ax.set_yticks(y_pos, labels=people) ax.invert_yaxis() # labels read top-to-bottom ax.set_xlabel('Performance') ax.set_title('How fast do you want to go today?') @@ -85,8 +82,7 @@ fig, ax = plt.subplots() hbars = ax.barh(y_pos, performance, xerr=error, align='center') -ax.set_yticks(y_pos) -ax.set_yticklabels(people) +ax.set_yticks(y_pos, labels=people) ax.invert_yaxis() # labels read top-to-bottom ax.set_xlabel('Performance') ax.set_title('How fast do you want to go today?') @@ -100,17 +96,11 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown -# in this example: - -matplotlib.axes.Axes.bar -matplotlib.pyplot.bar -matplotlib.axes.Axes.barh -matplotlib.pyplot.barh -matplotlib.axes.Axes.bar_label -matplotlib.pyplot.bar_label +# - `matplotlib.axes.Axes.bar` / `matplotlib.pyplot.bar` +# - `matplotlib.axes.Axes.barh` / `matplotlib.pyplot.barh` +# - `matplotlib.axes.Axes.bar_label` / `matplotlib.pyplot.bar_label` diff --git a/examples/lines_bars_and_markers/barchart.py b/examples/lines_bars_and_markers/barchart.py index 1140381870aa..8e2dc9f1ed44 100644 --- a/examples/lines_bars_and_markers/barchart.py +++ b/examples/lines_bars_and_markers/barchart.py @@ -7,7 +7,6 @@ bars with labels. """ -import matplotlib import matplotlib.pyplot as plt import numpy as np @@ -26,8 +25,7 @@ # Add some text for labels, title and custom x-axis tick labels, etc. ax.set_ylabel('Scores') ax.set_title('Scores by group and gender') -ax.set_xticks(x) -ax.set_xticklabels(labels) +ax.set_xticks(x, labels) ax.legend() ax.bar_label(rects1, padding=3) @@ -39,15 +37,10 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown -# in this example: - -matplotlib.axes.Axes.bar -matplotlib.pyplot.bar -matplotlib.axes.Axes.bar_label -matplotlib.pyplot.bar_label +# - `matplotlib.axes.Axes.bar` / `matplotlib.pyplot.bar` +# - `matplotlib.axes.Axes.bar_label` / `matplotlib.pyplot.bar_label` diff --git a/examples/lines_bars_and_markers/barh.py b/examples/lines_bars_and_markers/barh.py index c537c0d9b215..64d5a5137906 100644 --- a/examples/lines_bars_and_markers/barh.py +++ b/examples/lines_bars_and_markers/barh.py @@ -22,8 +22,7 @@ error = np.random.rand(len(people)) ax.barh(y_pos, performance, xerr=error, align='center') -ax.set_yticks(y_pos) -ax.set_yticklabels(people) +ax.set_yticks(y_pos, labels=people) ax.invert_yaxis() # labels read top-to-bottom ax.set_xlabel('Performance') ax.set_title('How fast do you want to go today?') diff --git a/examples/lines_bars_and_markers/broken_barh.py b/examples/lines_bars_and_markers/broken_barh.py index c0691beaf255..6da38f1e465f 100644 --- a/examples/lines_bars_and_markers/broken_barh.py +++ b/examples/lines_bars_and_markers/broken_barh.py @@ -7,6 +7,7 @@ """ import matplotlib.pyplot as plt +# Horizontal bar plot with gaps fig, ax = plt.subplots() ax.broken_barh([(110, 30), (150, 10)], (10, 9), facecolors='tab:blue') ax.broken_barh([(10, 50), (100, 20), (130, 10)], (20, 9), @@ -14,9 +15,8 @@ ax.set_ylim(5, 35) ax.set_xlim(0, 200) ax.set_xlabel('seconds since start') -ax.set_yticks([15, 25]) -ax.set_yticklabels(['Bill', 'Jim']) -ax.grid(True) +ax.set_yticks([15, 25], labels=['Bill', 'Jim']) # Modify y-axis tick labels +ax.grid(True) # Make grid lines visible ax.annotate('race interrupted', (61, 25), xytext=(0.8, 0.9), textcoords='axes fraction', arrowprops=dict(facecolor='black', shrink=0.05), diff --git a/examples/lines_bars_and_markers/cohere.py b/examples/lines_bars_and_markers/cohere.py index 370149695398..7881a0a31b1e 100644 --- a/examples/lines_bars_and_markers/cohere.py +++ b/examples/lines_bars_and_markers/cohere.py @@ -16,19 +16,19 @@ nse1 = np.random.randn(len(t)) # white noise 1 nse2 = np.random.randn(len(t)) # white noise 2 -# Two signals with a coherent part at 10Hz and a random part +# Two signals with a coherent part at 10 Hz and a random part s1 = np.sin(2 * np.pi * 10 * t) + nse1 s2 = np.sin(2 * np.pi * 10 * t) + nse2 fig, axs = plt.subplots(2, 1) axs[0].plot(t, s1, t, s2) axs[0].set_xlim(0, 2) -axs[0].set_xlabel('time') +axs[0].set_xlabel('Time') axs[0].set_ylabel('s1 and s2') axs[0].grid(True) cxy, f = axs[1].cohere(s1, s2, 256, 1. / dt) -axs[1].set_ylabel('coherence') +axs[1].set_ylabel('Coherence') fig.tight_layout() plt.show() diff --git a/examples/lines_bars_and_markers/csd_demo.py b/examples/lines_bars_and_markers/csd_demo.py index 1eacc649b204..8894333f94d0 100644 --- a/examples/lines_bars_and_markers/csd_demo.py +++ b/examples/lines_bars_and_markers/csd_demo.py @@ -33,10 +33,10 @@ ax1.plot(t, s1, t, s2) ax1.set_xlim(0, 5) -ax1.set_xlabel('time') +ax1.set_xlabel('Time') ax1.set_ylabel('s1 and s2') ax1.grid(True) cxy, f = ax2.csd(s1, s2, 256, 1. / dt) -ax2.set_ylabel('CSD (db)') +ax2.set_ylabel('CSD (dB)') plt.show() diff --git a/examples/lines_bars_and_markers/curve_error_band.py b/examples/lines_bars_and_markers/curve_error_band.py index 3d1675570b24..e55b467849a8 100644 --- a/examples/lines_bars_and_markers/curve_error_band.py +++ b/examples/lines_bars_and_markers/curve_error_band.py @@ -10,7 +10,6 @@ # sphinx_gallery_thumbnail_number = 2 import numpy as np -from scipy.interpolate import splprep, splev import matplotlib.pyplot as plt from matplotlib.path import Path @@ -22,8 +21,8 @@ x, y = r * np.cos(t), r * np.sin(t) fig, ax = plt.subplots() -ax.plot(x, y) -plt.show() +ax.plot(x, y, "k") +ax.set(aspect=1) ############################################################################# # An error band can be used to indicate the uncertainty of the curve. @@ -39,46 +38,51 @@ # `~.Axes.fill_between` method (see also # :doc:`/gallery/lines_bars_and_markers/fill_between_demo`). -# Error amplitudes depending on the curve parameter *t* -# (actual values are arbitrary and only for illustrative purposes): -err = 0.05 * np.sin(2 * t) ** 2 + 0.04 + 0.02 * np.cos(9 * t + 2) -# calculate normals via derivatives of splines -tck, u = splprep([x, y], s=0) -dx, dy = splev(u, tck, der=1) -l = np.hypot(dx, dy) -nx = dy / l -ny = -dx / l +def draw_error_band(ax, x, y, err, **kwargs): + # Calculate normals via centered finite differences (except the first point + # which uses a forward difference and the last point which uses a backward + # difference). + dx = np.concatenate([[x[1] - x[0]], x[2:] - x[:-2], [x[-1] - x[-2]]]) + dy = np.concatenate([[y[1] - y[0]], y[2:] - y[:-2], [y[-1] - y[-2]]]) + l = np.hypot(dx, dy) + nx = dy / l + ny = -dx / l -# end points of errors -xp = x + nx * err -yp = y + ny * err -xn = x - nx * err -yn = y - ny * err + # end points of errors + xp = x + nx * err + yp = y + ny * err + xn = x - nx * err + yn = y - ny * err -vertices = np.block([[xp, xn[::-1]], - [yp, yn[::-1]]]).T -codes = Path.LINETO * np.ones(len(vertices), dtype=Path.code_type) -codes[0] = codes[len(xp)] = Path.MOVETO -path = Path(vertices, codes) + vertices = np.block([[xp, xn[::-1]], + [yp, yn[::-1]]]).T + codes = np.full(len(vertices), Path.LINETO) + codes[0] = codes[len(xp)] = Path.MOVETO + path = Path(vertices, codes) + ax.add_patch(PathPatch(path, **kwargs)) -patch = PathPatch(path, facecolor='C0', edgecolor='none', alpha=0.3) -fig, ax = plt.subplots() -ax.plot(x, y) -ax.add_patch(patch) +axs = (plt.figure(constrained_layout=True) + .subplots(1, 2, sharex=True, sharey=True)) +errs = [ + (axs[0], "constant error", 0.05), + (axs[1], "variable error", 0.05 * np.sin(2 * t) ** 2 + 0.04), +] +for i, (ax, title, err) in enumerate(errs): + ax.set(title=title, aspect=1, xticks=[], yticks=[]) + ax.plot(x, y, "k") + draw_error_band(ax, x, y, err=err, + facecolor=f"C{i}", edgecolor="none", alpha=.3) + plt.show() ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown -# in this example: - -import matplotlib -matplotlib.patches.PathPatch -matplotlib.path.Path +# - `matplotlib.patches.PathPatch` +# - `matplotlib.path.Path` diff --git a/examples/lines_bars_and_markers/errorbar_limits_simple.py b/examples/lines_bars_and_markers/errorbar_limits_simple.py index 2a69b205b3ce..42d0daf6f281 100644 --- a/examples/lines_bars_and_markers/errorbar_limits_simple.py +++ b/examples/lines_bars_and_markers/errorbar_limits_simple.py @@ -55,14 +55,9 @@ ############################################################################## # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.errorbar -matplotlib.pyplot.errorbar +# - `matplotlib.axes.Axes.errorbar` / `matplotlib.pyplot.errorbar` diff --git a/examples/lines_bars_and_markers/eventcollection_demo.py b/examples/lines_bars_and_markers/eventcollection_demo.py index abdb6e6c05f5..e4fe6f61bdab 100644 --- a/examples/lines_bars_and_markers/eventcollection_demo.py +++ b/examples/lines_bars_and_markers/eventcollection_demo.py @@ -1,10 +1,10 @@ -""" +r""" ==================== EventCollection Demo ==================== -Plot two curves, then use EventCollections to mark the locations of the x -and y data points on the respective axes for each curve +Plot two curves, then use `.EventCollection`\s to mark the locations of the x +and y data points on the respective axes for each curve. """ import matplotlib.pyplot as plt diff --git a/examples/lines_bars_and_markers/fill.py b/examples/lines_bars_and_markers/fill.py index e6d2e4293f35..79642a9e5ed5 100644 --- a/examples/lines_bars_and_markers/fill.py +++ b/examples/lines_bars_and_markers/fill.py @@ -3,7 +3,7 @@ Filled polygon ============== -`~.Axes.fill()` draws a filled polygon based based on lists of point +`~.Axes.fill()` draws a filled polygon based on lists of point coordinates *x*, *y*. This example uses the `Koch snowflake`_ as an example polygon. @@ -62,7 +62,7 @@ def _koch_snowflake_complex(order): plt.show() ############################################################################### -# Use keyword arguments *facecolor* and *edgecolor* to modify the the colors +# Use keyword arguments *facecolor* and *edgecolor* to modify the colors # of the polygon. Since the *linewidth* of the edge is 0 in the default # Matplotlib style, we have to set it as well for the edge to become visible. @@ -76,18 +76,12 @@ def _koch_snowflake_complex(order): plt.show() -############################################################################# +############################################################################### # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.fill -matplotlib.pyplot.fill -matplotlib.axes.Axes.axis -matplotlib.pyplot.axis +# - `matplotlib.axes.Axes.fill` / `matplotlib.pyplot.fill` +# - `matplotlib.axes.Axes.axis` / `matplotlib.pyplot.axis` diff --git a/examples/lines_bars_and_markers/fill_between_alpha.py b/examples/lines_bars_and_markers/fill_between_alpha.py index acd6cebbbd7f..9f4351b0006f 100644 --- a/examples/lines_bars_and_markers/fill_between_alpha.py +++ b/examples/lines_bars_and_markers/fill_between_alpha.py @@ -7,9 +7,9 @@ It has a very handy ``where`` argument to combine filling with logical ranges, e.g., to just fill in a curve over some threshold value. -At its most basic level, ``fill_between`` can be use to enhance a graphs visual -appearance. Let's compare two graphs of a financial times with a simple line -plot on the left and a filled line on the right. +At its most basic level, ``fill_between`` can be used to enhance a graph's +visual appearance. Let's compare two graphs of financial data with a simple +line plot on the left and a filled line on the right. """ import matplotlib.pyplot as plt @@ -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,14 +26,13 @@ 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) + ax.label_outer() ax1.set_ylabel('price') -for label in ax2.get_yticklabels(): - label.set_visible(False) fig.suptitle('Google (GOOG) daily closing price') fig.autofmt_xdate() @@ -52,16 +48,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 +76,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 +92,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,23 +116,23 @@ 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() ############################################################################### # Another handy use of filled regions is to highlight horizontal or vertical -# spans of an axes -- for that Matplotlib has the helper functions +# spans of an Axes -- for that Matplotlib has the helper functions # `~matplotlib.axes.Axes.axhspan` and `~matplotlib.axes.Axes.axvspan`. See # :doc:`/gallery/subplots_axes_and_figures/axhspan_demo`. diff --git a/examples/lines_bars_and_markers/fill_between_demo.py b/examples/lines_bars_and_markers/fill_between_demo.py index 8f7e7c11d024..79aef67ab4d9 100644 --- a/examples/lines_bars_and_markers/fill_between_demo.py +++ b/examples/lines_bars_and_markers/fill_between_demo.py @@ -114,7 +114,7 @@ # ------------------------------------------------------------ # The same selection mechanism can be applied to fill the full vertical height # of the axes. To be independent of y-limits, we add a transform that -# interprets the x-values in data coorindates and the y-values in axes +# interprets the x-values in data coordinates and the y-values in axes # coordinates. # # The following example marks the regions in which the y-data are above a @@ -132,15 +132,10 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.fill_between -matplotlib.pyplot.fill_between -matplotlib.axes.Axes.get_xaxis_transform +# - `matplotlib.axes.Axes.fill_between` / `matplotlib.pyplot.fill_between` +# - `matplotlib.axes.Axes.get_xaxis_transform` diff --git a/examples/lines_bars_and_markers/fill_betweenx_demo.py b/examples/lines_bars_and_markers/fill_betweenx_demo.py index 1f449c372348..06f219d9fb31 100644 --- a/examples/lines_bars_and_markers/fill_betweenx_demo.py +++ b/examples/lines_bars_and_markers/fill_betweenx_demo.py @@ -26,9 +26,12 @@ ax3.fill_betweenx(y, x1, x2) ax3.set_title('between (x1, x2)') -# now fill between x1 and x2 where a logical condition is met. Note -# this is different than calling +############################################################################# +# Now fill between x1 and x2 where a logical condition is met. Note this is +# different than calling:: +# # fill_between(y[where], x1[where], x2[where]) +# # because of edge effects over multiple contiguous regions. fig, [ax, ax1] = plt.subplots(1, 2, sharey=True, figsize=(6, 6)) @@ -44,9 +47,9 @@ ax1.fill_betweenx(y, x1, x2, where=x2 <= x1, facecolor='red') ax1.set_title('regions with x2 > 1 are masked') -# This example illustrates a problem; because of the data -# gridding, there are undesired unfilled triangles at the crossover -# points. A brute-force solution would be to interpolate all -# arrays to a very fine grid before plotting. +############################################################################# +# This example illustrates a problem; because of the data gridding, there are +# undesired unfilled triangles at the crossover points. A brute-force solution +# would be to interpolate all arrays to a very fine grid before plotting. plt.show() diff --git a/examples/lines_bars_and_markers/filled_step.py b/examples/lines_bars_and_markers/filled_step.py index 66a60ebe55ec..a4185366b7a2 100644 --- a/examples/lines_bars_and_markers/filled_step.py +++ b/examples/lines_bars_and_markers/filled_step.py @@ -7,7 +7,6 @@ """ import itertools -from collections import OrderedDict from functools import partial import numpy as np @@ -21,8 +20,6 @@ def filled_hist(ax, edges, values, bottoms=None, orientation='v', """ Draw a histogram as a stepped patch. - Extra kwargs are passed through to `fill_between` - Parameters ---------- ax : Axes @@ -42,6 +39,9 @@ def filled_hist(ax, edges, values, bottoms=None, orientation='v', Orientation of the histogram. 'v' (default) has the bars increasing in the positive y-direction. + **kwargs + Extra keyword arguments are passed through to `.fill_between`. + Returns ------- ret : PolyCollection @@ -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): @@ -86,7 +87,7 @@ def stack_hist(ax, stacked_data, sty_cycle, bottoms=None, The axes to add artists too stacked_data : array or Mapping - A (N, M) shaped array. The first dimension will be iterated over to + A (M, N) shaped array. The first dimension will be iterated over to compute histograms row-wise sty_cycle : Cycler or operable of dict @@ -104,11 +105,11 @@ def stack_hist(ax, stacked_data, sty_cycle, bottoms=None, If not given and stacked data is an array defaults to 'default set {n}' - If stacked_data is a mapping, and labels is None, default to the keys - (which may come out in a random order). + If *stacked_data* is a mapping, and *labels* is None, default to the + keys. - If stacked_data is a mapping and labels is given then only - the columns listed by be plotted. + If *stacked_data* is a mapping and *labels* is given then only the + columns listed will be plotted. plot_func : callable, optional Function to call to draw the histogram must have signature: @@ -117,9 +118,9 @@ def stack_hist(ax, stacked_data, sty_cycle, bottoms=None, label=label, **kwargs) plot_kwargs : dict, optional - Any extra kwargs to pass through to the plotting function. This - will be the same for all calls to the plotting function and will - over-ride the values in cycle. + Any extra keyword arguments to pass through to the plotting function. + This will be the same for all calls to the plotting function and will + override the values in *sty_cycle*. Returns ------- @@ -188,7 +189,7 @@ def stack_hist(ax, stacked_data, sty_cycle, bottoms=None, np.random.seed(19680801) stack_data = np.random.randn(4, 12250) -dict_data = OrderedDict(zip((c['label'] for c in label_cycle), stack_data)) +dict_data = dict(zip((c['label'] for c in label_cycle), stack_data)) ############################################################################### # Work with plain arrays @@ -225,15 +226,11 @@ def stack_hist(ax, stacked_data, sty_cycle, bottoms=None, ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.fill_betweenx -matplotlib.axes.Axes.fill_between -matplotlib.axis.Axis.set_major_locator +# - `matplotlib.axes.Axes.fill_betweenx` / `matplotlib.pyplot.fill_betweenx` +# - `matplotlib.axes.Axes.fill_between` / `matplotlib.pyplot.fill_between` +# - `matplotlib.axis.Axis.set_major_locator` diff --git a/examples/lines_bars_and_markers/gradient_bar.py b/examples/lines_bars_and_markers/gradient_bar.py index 7f39267ddf86..93d346941f31 100644 --- a/examples/lines_bars_and_markers/gradient_bar.py +++ b/examples/lines_bars_and_markers/gradient_bar.py @@ -12,8 +12,8 @@ by a unit vector *v*. The values at the corners are then obtained by the lengths of the projections of the corner vectors on *v*. -A similar approach can be used to create a gradient background for an axes. -In that case, it is helpful to uses Axes coordinates (``extent=(0, 1, 0, 1), +A similar approach can be used to create a gradient background for an Axes. +In that case, it is helpful to use Axes coordinates (``extent=(0, 1, 0, 1), transform=ax.transAxes``) to be independent of the data coordinates. """ @@ -34,7 +34,7 @@ def gradient_image(ax, extent, direction=0.3, cmap_range=(0, 1), **kwargs): extent The extent of the image as (xmin, xmax, ymin, ymax). By default, this is in Axes coordinates but may be - changed using the *transform* kwarg. + changed using the *transform* keyword argument. direction : float The direction of the gradient. This is a number in range 0 (=vertical) to 1 (=horizontal). @@ -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/hat_graph.py b/examples/lines_bars_and_markers/hat_graph.py index 4c29f6f85625..6c939167b536 100644 --- a/examples/lines_bars_and_markers/hat_graph.py +++ b/examples/lines_bars_and_markers/hat_graph.py @@ -7,7 +7,6 @@ .. _hat graph: https://doi.org/10.1186/s41235-019-0182-3 """ -import matplotlib import numpy as np import matplotlib.pyplot as plt @@ -41,8 +40,7 @@ def label_bars(heights, rects): values = np.asarray(values) x = np.arange(values.shape[1]) - ax.set_xticks(x) - ax.set_xticklabels(xlabels) + ax.set_xticks(x, labels=xlabels) spacing = 0.3 # spacing between hat groups width = (1 - spacing) / values.shape[0] heights0 = values[0] @@ -73,14 +71,10 @@ def label_bars(heights, rects): plt.show() ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown -# in this example: -matplotlib.axes.Axes.bar -matplotlib.pyplot.bar -matplotlib.axes.Axes.annotate -matplotlib.pyplot.annotate +# - `matplotlib.axes.Axes.bar` / `matplotlib.pyplot.bar` +# - `matplotlib.axes.Axes.annotate` / `matplotlib.pyplot.annotate` diff --git a/examples/lines_bars_and_markers/horizontal_barchart_distribution.py b/examples/lines_bars_and_markers/horizontal_barchart_distribution.py index 9b269db2b547..3a22ce3ed4ae 100644 --- a/examples/lines_bars_and_markers/horizontal_barchart_distribution.py +++ b/examples/lines_bars_and_markers/horizontal_barchart_distribution.py @@ -43,7 +43,7 @@ def survey(results, category_names): labels = list(results.keys()) data = np.array(list(results.values())) data_cum = data.cumsum(axis=1) - category_colors = plt.get_cmap('RdYlGn')( + category_colors = plt.colormaps['RdYlGn']( np.linspace(0.15, 0.85, data.shape[1])) fig, ax = plt.subplots(figsize=(9.2, 5)) @@ -71,18 +71,11 @@ def survey(results, category_names): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.barh -matplotlib.pyplot.barh -matplotlib.axes.Axes.bar_label -matplotlib.pyplot.bar_label -matplotlib.axes.Axes.legend -matplotlib.pyplot.legend +# - `matplotlib.axes.Axes.barh` / `matplotlib.pyplot.barh` +# - `matplotlib.axes.Axes.bar_label` / `matplotlib.pyplot.bar_label` +# - `matplotlib.axes.Axes.legend` / `matplotlib.pyplot.legend` diff --git a/examples/lines_bars_and_markers/line_demo_dash_control.py b/examples/lines_bars_and_markers/line_demo_dash_control.py index 78043dfed2ff..9d02981597c0 100644 --- a/examples/lines_bars_and_markers/line_demo_dash_control.py +++ b/examples/lines_bars_and_markers/line_demo_dash_control.py @@ -17,6 +17,11 @@ :doc:`property_cycle ` by passing a list of dash sequences using the keyword *dashes* to the cycler. This is not shown within this example. + +Other attributes of the dash may also be set either with the relevant method +(`~.Line2D.set_dash_capstyle`, `~.Line2D.set_dash_joinstyle`, +`~.Line2D.set_gapcolor`) or by passing the property through a plotting +function. """ import numpy as np import matplotlib.pyplot as plt @@ -24,14 +29,21 @@ x = np.linspace(0, 10, 500) y = np.sin(x) +plt.rc('lines', linewidth=2.5) fig, ax = plt.subplots() -# Using set_dashes() to modify dashing of an existing line -line1, = ax.plot(x, y, label='Using set_dashes()') -line1.set_dashes([2, 2, 10, 2]) # 2pt line, 2pt break, 10pt line, 2pt break +# Using set_dashes() and set_capstyle() to modify dashing of an existing line. +line1, = ax.plot(x, y, label='Using set_dashes() and set_dash_capstyle()') +line1.set_dashes([2, 2, 10, 2]) # 2pt line, 2pt break, 10pt line, 2pt break. +line1.set_dash_capstyle('round') -# Using plot(..., dashes=...) to set the dashing when creating a line +# Using plot(..., dashes=...) to set the dashing when creating a line. line2, = ax.plot(x, y - 0.2, dashes=[6, 2], label='Using the dashes parameter') -ax.legend() +# Using plot(..., dashes=..., gapcolor=...) to set the dashing and +# alternating color when creating a line. +line3, = ax.plot(x, y - 0.4, dashes=[4, 4], gapcolor='tab:pink', + label='Using the dashes and gapcolor parameters') + +ax.legend(handlelength=4) plt.show() diff --git a/examples/lines_bars_and_markers/lines_with_ticks_demo.py b/examples/lines_bars_and_markers/lines_with_ticks_demo.py index c7b902f09ddf..f7ef646c647f 100644 --- a/examples/lines_bars_and_markers/lines_with_ticks_demo.py +++ b/examples/lines_bars_and_markers/lines_with_ticks_demo.py @@ -15,10 +15,12 @@ import matplotlib.pyplot as plt from matplotlib import patheffects +# Plot a straight diagonal line with ticked style path fig, ax = plt.subplots(figsize=(6, 6)) ax.plot([0, 1], [0, 1], label="Line", path_effects=[patheffects.withTickedStroke(spacing=7, angle=135)]) +# Plot a curved line with ticked style path nx = 101 x = np.linspace(0.0, 1.0, nx) y = 0.3*np.sin(x*8) + 0.4 diff --git a/examples/lines_bars_and_markers/linestyles.py b/examples/lines_bars_and_markers/linestyles.py index 35920617c90c..96c9113ee4cc 100644 --- a/examples/lines_bars_and_markers/linestyles.py +++ b/examples/lines_bars_and_markers/linestyles.py @@ -6,8 +6,9 @@ Simple linestyles can be defined using the strings "solid", "dotted", "dashed" or "dashdot". More refined control can be achieved by providing a dash tuple ``(offset, (on_off_seq))``. For example, ``(0, (3, 10, 1, 15))`` means -(3pt line, 10pt space, 1pt line, 15pt space) with no offset. See also -`.Line2D.set_linestyle`. +(3pt line, 10pt space, 1pt line, 15pt space) with no offset, while +``(5, (10, 3))``, means (10pt line, 3pt space), but skip the first 5pt line. +See also `.Line2D.set_linestyle`. *Note*: The dash style can also be configured via `.Line2D.set_dashes` as shown in :doc:`/gallery/lines_bars_and_markers/line_demo_dash_control` @@ -19,7 +20,7 @@ linestyle_str = [ ('solid', 'solid'), # Same as (0, ()) or '-' - ('dotted', 'dotted'), # Same as (0, (1, 1)) or '.' + ('dotted', 'dotted'), # Same as (0, (1, 1)) or ':' ('dashed', 'dashed'), # Same as '--' ('dashdot', 'dashdot')] # Same as '-.' @@ -27,7 +28,7 @@ ('loosely dotted', (0, (1, 10))), ('dotted', (0, (1, 1))), ('densely dotted', (0, (1, 1))), - + ('long dash with offset', (5, (10, 3))), ('loosely dashed', (0, (5, 10))), ('dashed', (0, (5, 5))), ('densely dashed', (0, (5, 1))), @@ -65,9 +66,7 @@ def plot_linestyles(ax, linestyles, title): color="blue", fontsize=8, ha="right", family="monospace") -ax0, ax1 = (plt.figure(figsize=(10, 8)) - .add_gridspec(2, 1, height_ratios=[1, 3]) - .subplots()) +fig, (ax0, ax1) = plt.subplots(2, 1, figsize=(10, 8), height_ratios=[1, 3]) plot_linestyles(ax0, linestyle_str[::-1], title='Named linestyles') plot_linestyles(ax1, linestyle_tuple[::-1], title='Parametrized linestyles') diff --git a/examples/lines_bars_and_markers/marker_reference.py b/examples/lines_bars_and_markers/marker_reference.py index 3d73ddee94ab..c4564d2b027d 100644 --- a/examples/lines_bars_and_markers/marker_reference.py +++ b/examples/lines_bars_and_markers/marker_reference.py @@ -9,17 +9,20 @@ - `Unfilled markers`_ - `Filled markers`_ - `Markers created from TeX symbols`_ -- Custom markers can be created from paths. See - :doc:`/gallery/shapes_and_collections/marker_path`. +- `Markers created from Paths`_ For a list of all markers see also the `matplotlib.markers` documentation. For example usages see :doc:`/gallery/lines_bars_and_markers/scatter_star_poly`. + +.. 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', @@ -116,7 +119,7 @@ def split_list(a_list): fig.suptitle('Mathtext markers', fontsize=14) fig.subplots_adjust(left=0.4) -marker_style.update(markeredgecolor="None", markersize=15) +marker_style.update(markeredgecolor="none", markersize=15) markers = ["$1$", r"$\frac{1}{2}$", "$f$", "$\u266B$", r"$\mathcal{A}$"] for y, marker in enumerate(markers): @@ -126,3 +129,128 @@ def split_list(a_list): format_axes(ax) plt.show() + + +############################################################################### +# Markers created from Paths +# ========================== +# +# Any `~.path.Path` can be used as a marker. The following example shows two +# simple paths *star* and *circle*, and a more elaborate path of a circle with +# a cut-out star. + +import matplotlib.path as mpath +import numpy as np + +star = mpath.Path.unit_regular_star(6) +circle = mpath.Path.unit_circle() +# concatenate the circle with an internal cutout of the star +cut_star = mpath.Path( + vertices=np.concatenate([circle.vertices, star.vertices[::-1, ...]]), + codes=np.concatenate([circle.codes, star.codes])) + +fig, ax = plt.subplots() +fig.suptitle('Path markers', fontsize=14) +fig.subplots_adjust(left=0.4) + +markers = {'star': star, 'circle': circle, 'cut_star': cut_star} + +for y, (name, marker) in enumerate(markers.items()): + ax.text(-0.5, y, name, **text_style) + ax.plot([y] * 3, marker=marker, **marker_style) +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/markevery_demo.py b/examples/lines_bars_and_markers/markevery_demo.py index caf12c9563c7..8dc02f52e28a 100644 --- a/examples/lines_bars_and_markers/markevery_demo.py +++ b/examples/lines_bars_and_markers/markevery_demo.py @@ -3,99 +3,96 @@ Markevery Demo ============== -This example demonstrates the various options for showing a marker at a -subset of data points using the ``markevery`` property of a Line2D object. - -Integer arguments are fairly intuitive. e.g. ``markevery=5`` will plot every -5th marker starting from the first data point. - -Float arguments allow markers to be spaced at approximately equal distances -along the line. The theoretical distance along the line between markers is -determined by multiplying the display-coordinate distance of the axes -bounding-box diagonal by the value of ``markevery``. The data points closest -to the theoretical distances will be shown. - -A slice or list/array can also be used with ``markevery`` to specify the -markers to show. +The ``markevery`` property of `.Line2D` allows drawing markers at a subset of +data points. + +The list of possible parameters is specified at `.Line2D.set_markevery`. +In short: + +- A single integer N draws every N-th marker. +- A tuple of integers (start, N) draws every N-th marker, starting at data + index *start*. +- A list of integers draws the markers at the specified indices. +- A slice draws the markers at the sliced indices. +- A float specifies the distance between markers as a fraction of the Axes + diagonal in screen space. This will lead to a visually uniform distribution + of the points along the line, irrespective of scales and zooming. """ import numpy as np import matplotlib.pyplot as plt # define a list of markevery cases to plot -cases = [None, - 8, - (30, 8), - [16, 24, 30], [0, -1], - slice(100, 200, 3), - 0.1, 0.3, 1.5, - (0.0, 0.1), (0.45, 0.1)] - -# define the figure size and grid layout properties -figsize = (10, 8) -cols = 3 -rows = len(cases) // cols + 1 -# define the data for cartesian plots +cases = [ + None, + 8, + (30, 8), + [16, 24, 32], + [0, -1], + slice(100, 200, 3), + 0.1, + 0.4, + (0.2, 0.4) +] + +# data points delta = 0.11 x = np.linspace(0, 10 - 2 * delta, 200) + delta y = np.sin(x) + 1.0 + delta - -def trim_axs(axs, N): - """ - Reduce *axs* to *N* Axes. All further Axes are removed from the figure. - """ - axs = axs.flat - for ax in axs[N:]: - ax.remove() - return axs[:N] - ############################################################################### -# Plot each markevery case for linear x and y scales +# markevery with linear scales +# ---------------------------- -axs = plt.figure(figsize=figsize, constrained_layout=True).subplots(rows, cols) -axs = trim_axs(axs, len(cases)) -for ax, case in zip(axs, cases): - ax.set_title('markevery=%s' % str(case)) - ax.plot(x, y, 'o', ls='-', ms=4, markevery=case) +fig, axs = plt.subplots(3, 3, figsize=(10, 6), constrained_layout=True) +for ax, markevery in zip(axs.flat, cases): + ax.set_title(f'markevery={markevery}') + ax.plot(x, y, 'o', ls='-', ms=4, markevery=markevery) ############################################################################### -# Plot each markevery case for log x and y scales - -axs = plt.figure(figsize=figsize, constrained_layout=True).subplots(rows, cols) -axs = trim_axs(axs, len(cases)) -for ax, case in zip(axs, cases): - ax.set_title('markevery=%s' % str(case)) +# markevery with log scales +# ------------------------- +# +# Note that the log scale causes a visual asymmetry in the marker distance for +# when subsampling the data using an integer. In contrast, subsampling on +# fraction of figure size creates even distributions, because it's based on +# fractions of the Axes diagonal, not on data coordinates or data indices. + +fig, axs = plt.subplots(3, 3, figsize=(10, 6), constrained_layout=True) +for ax, markevery in zip(axs.flat, cases): + ax.set_title(f'markevery={markevery}') ax.set_xscale('log') ax.set_yscale('log') - ax.plot(x, y, 'o', ls='-', ms=4, markevery=case) + ax.plot(x, y, 'o', ls='-', ms=4, markevery=markevery) ############################################################################### -# Plot each markevery case for linear x and y scales but zoomed in -# note the behaviour when zoomed in. When a start marker offset is specified -# it is always interpreted with respect to the first data point which might be -# different to the first visible data point. - -axs = plt.figure(figsize=figsize, constrained_layout=True).subplots(rows, cols) -axs = trim_axs(axs, len(cases)) -for ax, case in zip(axs, cases): - ax.set_title('markevery=%s' % str(case)) - ax.plot(x, y, 'o', ls='-', ms=4, markevery=case) +# markevery on zoomed plots +# ------------------------- +# +# Integer-based *markevery* specifications select points from the underlying +# data and are independent on the view. In contrast, float-based specifications +# are related to the Axes diagonal. While zooming does not change the Axes +# diagonal, it changes the displayed data range, and more points will be +# displayed when zooming. + +fig, axs = plt.subplots(3, 3, figsize=(10, 6), constrained_layout=True) +for ax, markevery in zip(axs.flat, cases): + ax.set_title(f'markevery={markevery}') + ax.plot(x, y, 'o', ls='-', ms=4, markevery=markevery) ax.set_xlim((6, 6.7)) ax.set_ylim((1.1, 1.7)) -# define data for polar plots +############################################################################### +# markevery on polar plots +# ------------------------ + r = np.linspace(0, 3.0, 200) theta = 2 * np.pi * r -############################################################################### -# Plot each markevery case for polar plots - -axs = plt.figure(figsize=figsize, constrained_layout=True).subplots( - rows, cols, subplot_kw={'projection': 'polar'}) -axs = trim_axs(axs, len(cases)) -for ax, case in zip(axs, cases): - ax.set_title('markevery=%s' % str(case)) - ax.plot(theta, r, 'o', ls='-', ms=4, markevery=case) +fig, axs = plt.subplots(3, 3, figsize=(10, 6), constrained_layout=True, + subplot_kw={'projection': 'polar'}) +for ax, markevery in zip(axs.flat, cases): + ax.set_title(f'markevery={markevery}') + ax.plot(theta, r, 'o', ls='-', ms=4, markevery=markevery) plt.show() diff --git a/examples/lines_bars_and_markers/markevery_prop_cycle.py b/examples/lines_bars_and_markers/markevery_prop_cycle.py deleted file mode 100644 index 295de4756736..000000000000 --- a/examples/lines_bars_and_markers/markevery_prop_cycle.py +++ /dev/null @@ -1,63 +0,0 @@ -""" -========================================= -prop_cycle property markevery in rcParams -========================================= - -This example demonstrates a working solution to issue #8576, providing full -support of the markevery property for axes.prop_cycle assignments through -rcParams. Makes use of the same list of markevery cases from the -:doc:`markevery demo -`. - -Renders a plot with shifted-sine curves along each column with -a unique markevery value for each sine curve. -""" -from cycler import cycler -import numpy as np -import matplotlib as mpl -import matplotlib.pyplot as plt - -# Define a list of markevery cases and color cases to plot -cases = [None, - 8, - (30, 8), - [16, 24, 30], - [0, -1], - slice(100, 200, 3), - 0.1, - 0.3, - 1.5, - (0.0, 0.1), - (0.45, 0.1)] - -colors = ['#1f77b4', - '#ff7f0e', - '#2ca02c', - '#d62728', - '#9467bd', - '#8c564b', - '#e377c2', - '#7f7f7f', - '#bcbd22', - '#17becf', - '#1a55FF'] - -# Configure rcParams axes.prop_cycle to simultaneously cycle cases and colors. -mpl.rcParams['axes.prop_cycle'] = cycler(markevery=cases, color=colors) - -# Create data points and offsets -x = np.linspace(0, 2 * np.pi) -offsets = np.linspace(0, 2 * np.pi, 11, endpoint=False) -yy = np.transpose([np.sin(x + phi) for phi in offsets]) - -# Set the plot curve with markers and a title -fig = plt.figure() -ax = fig.add_axes([0.1, 0.1, 0.6, 0.75]) - -for i in range(len(cases)): - ax.plot(yy[:, i], marker='o', label=str(cases[i])) - ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0.) - -plt.title('Support for axes.prop_cycle cycler with markevery') - -plt.show() diff --git a/examples/lines_bars_and_markers/masked_demo.py b/examples/lines_bars_and_markers/masked_demo.py index 52ea1a68b772..1f94a4f61891 100644 --- a/examples/lines_bars_and_markers/masked_demo.py +++ b/examples/lines_bars_and_markers/masked_demo.py @@ -15,7 +15,7 @@ plotting with a line, it will be broken there. .. _masked array: - https://docs.scipy.org/doc/numpy/reference/maskedarray.generic.html + https://numpy.org/doc/stable/reference/maskedarray.generic.html The following example illustrates the three cases: 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..89670b5a223f --- /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) +successful = np.random.randint(0, 3, size=N) +positions = np.random.normal(size=(N, 2)) * 5 +data = zip(skills, takeoff_angles, thrusts, successful, positions) + +cmap = plt.colormaps["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/psd_demo.py b/examples/lines_bars_and_markers/psd_demo.py index 43fcc9bd47b4..0ab038c2991b 100644 --- a/examples/lines_bars_and_markers/psd_demo.py +++ b/examples/lines_bars_and_markers/psd_demo.py @@ -12,7 +12,6 @@ import matplotlib.pyplot as plt import numpy as np import matplotlib.mlab as mlab -import matplotlib.gridspec as gridspec # Fixing random state for reproducibility np.random.seed(19680801) @@ -58,39 +57,44 @@ y = y + np.random.randn(*t.shape) # Plot the raw time series -fig = plt.figure(constrained_layout=True) -gs = gridspec.GridSpec(2, 3, figure=fig) -ax = fig.add_subplot(gs[0, :]) -ax.plot(t, y) -ax.set_xlabel('time [s]') -ax.set_ylabel('signal') +fig, axs = plt.subplot_mosaic([ + ['signal', 'signal', 'signal'], + ['zero padding', 'block size', 'overlap'], +], layout='constrained') + +axs['signal'].plot(t, y) +axs['signal'].set_xlabel('time [s]') +axs['signal'].set_ylabel('signal') # Plot the PSD with different amounts of zero padding. This uses the entire # time series at once -ax2 = fig.add_subplot(gs[1, 0]) -ax2.psd(y, NFFT=len(t), pad_to=len(t), Fs=fs) -ax2.psd(y, NFFT=len(t), pad_to=len(t) * 2, Fs=fs) -ax2.psd(y, NFFT=len(t), pad_to=len(t) * 4, Fs=fs) -ax2.set_title('zero padding') +axs['zero padding'].psd(y, NFFT=len(t), pad_to=len(t), Fs=fs) +axs['zero padding'].psd(y, NFFT=len(t), pad_to=len(t) * 2, Fs=fs) +axs['zero padding'].psd(y, NFFT=len(t), pad_to=len(t) * 4, Fs=fs) # Plot the PSD with different block sizes, Zero pad to the length of the # original data sequence. -ax3 = fig.add_subplot(gs[1, 1], sharex=ax2, sharey=ax2) -ax3.psd(y, NFFT=len(t), pad_to=len(t), Fs=fs) -ax3.psd(y, NFFT=len(t) // 2, pad_to=len(t), Fs=fs) -ax3.psd(y, NFFT=len(t) // 4, pad_to=len(t), Fs=fs) -ax3.set_ylabel('') -ax3.set_title('block size') +axs['block size'].psd(y, NFFT=len(t), pad_to=len(t), Fs=fs) +axs['block size'].psd(y, NFFT=len(t) // 2, pad_to=len(t), Fs=fs) +axs['block size'].psd(y, NFFT=len(t) // 4, pad_to=len(t), Fs=fs) +axs['block size'].set_ylabel('') # Plot the PSD with different amounts of overlap between blocks -ax4 = fig.add_subplot(gs[1, 2], sharex=ax2, sharey=ax2) -ax4.psd(y, NFFT=len(t) // 2, pad_to=len(t), noverlap=0, Fs=fs) -ax4.psd(y, NFFT=len(t) // 2, pad_to=len(t), - noverlap=int(0.05 * len(t) / 2.), Fs=fs) -ax4.psd(y, NFFT=len(t) // 2, pad_to=len(t), - noverlap=int(0.2 * len(t) / 2.), Fs=fs) -ax4.set_ylabel('') -ax4.set_title('overlap') +axs['overlap'].psd(y, NFFT=len(t) // 2, pad_to=len(t), noverlap=0, Fs=fs) +axs['overlap'].psd(y, NFFT=len(t) // 2, pad_to=len(t), + noverlap=int(0.025 * len(t)), Fs=fs) +axs['overlap'].psd(y, NFFT=len(t) // 2, pad_to=len(t), + noverlap=int(0.1 * len(t)), Fs=fs) +axs['overlap'].set_ylabel('') +axs['overlap'].set_title('overlap') + +for title, ax in axs.items(): + if title == 'signal': + continue + + ax.set_title(title) + ax.sharex(axs['zero padding']) + ax.sharey(axs['zero padding']) plt.show() diff --git a/examples/lines_bars_and_markers/scatter_custom_symbol.py b/examples/lines_bars_and_markers/scatter_custom_symbol.py index 428a68318671..b8e57b0e5e1f 100644 --- a/examples/lines_bars_and_markers/scatter_custom_symbol.py +++ b/examples/lines_bars_and_markers/scatter_custom_symbol.py @@ -1,18 +1,43 @@ """ -===================== -Scatter Custom Symbol -===================== - -Creating a custom ellipse symbol in scatter plot. +================================= +Scatter plots with custom symbols +================================= +.. redirect-from:: /gallery/lines_bars_and_markers/scatter_symbol +.. redirect-from:: /gallery/lines_bars_and_markers/scatter_piecharts """ + +############################################################################## +# Using TeX symbols +# ----------------- +# An easy way to customize scatter symbols is passing a TeX symbol name +# enclosed in $-signs as a marker. Below we use ``marker=r'$\clubsuit$'``. + import matplotlib.pyplot as plt import numpy as np - # Fixing random state for reproducibility np.random.seed(19680801) + +x = np.arange(0.0, 50.0, 2.0) +y = x ** 1.3 + np.random.rand(*x.shape) * 30.0 +sizes = np.random.rand(*x.shape) * 800 + 500 + +fig, ax = plt.subplots() +ax.scatter(x, y, sizes, c="green", alpha=0.5, marker=r'$\clubsuit$', + label="Luck") +ax.set_xlabel("Leprechauns") +ax.set_ylabel("Gold") +ax.legend() +plt.show() + +############################################################################## +# Using a custom path +# ------------------- +# Alternatively, one can also pass a custom path of N vertices as a Nx2 array +# of x, y values as *marker*. + # unit area ellipse rx, ry = 3., 1. area = rx * ry * np.pi diff --git a/examples/lines_bars_and_markers/scatter_demo2.py b/examples/lines_bars_and_markers/scatter_demo2.py index 7a669ff05d11..ec7e8183f8c4 100644 --- a/examples/lines_bars_and_markers/scatter_demo2.py +++ b/examples/lines_bars_and_markers/scatter_demo2.py @@ -9,9 +9,10 @@ import matplotlib.pyplot as plt import matplotlib.cbook as cbook -# Load a numpy record array from yahoo csv data with fields date, open, close, -# volume, adj_close from the mpl-data/example directory. The record array -# stores the date as an np.datetime64 with a day unit ('D') in the date column. +# Load a numpy record array from yahoo csv data with fields date, open, high, +# low, close, volume, adj_close from the mpl-data/sample_data directory. The +# record array stores the date as an np.datetime64 with a day unit ('D') in +# the date column. price_data = (cbook.get_sample_data('goog.npz', np_load=True)['price_data'] .view(np.recarray)) price_data = price_data[-250:] # get the most recent 250 trading days diff --git a/examples/lines_bars_and_markers/scatter_hist.py b/examples/lines_bars_and_markers/scatter_hist.py index 73fbee3dc6a2..29b2f96947a4 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/arranging_axes` 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() @@ -113,17 +110,13 @@ def scatter_hist(x, y, ax, ax_histx, ax_histy): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown -# in this example: - -import matplotlib -matplotlib.figure.Figure.add_axes -matplotlib.figure.Figure.add_subplot -matplotlib.figure.Figure.add_gridspec -matplotlib.axes.Axes.scatter -matplotlib.axes.Axes.hist +# - `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/scatter_piecharts.py b/examples/lines_bars_and_markers/scatter_piecharts.py deleted file mode 100644 index 6b2b4aa88824..000000000000 --- a/examples/lines_bars_and_markers/scatter_piecharts.py +++ /dev/null @@ -1,57 +0,0 @@ -""" -=================================== -Scatter plot with pie chart markers -=================================== - -This example makes custom 'pie charts' as the markers for a scatter plot. - -Thanks to Manuel Metz for the example. -""" - -import numpy as np -import matplotlib.pyplot as plt - -# first define the ratios -r1 = 0.2 # 20% -r2 = r1 + 0.4 # 40% - -# define some sizes of the scatter marker -sizes = np.array([60, 80, 120]) - -# calculate the points of the first pie marker -# these are just the origin (0, 0) + some (cos, sin) points on a circle -x1 = np.cos(2 * np.pi * np.linspace(0, r1)) -y1 = np.sin(2 * np.pi * np.linspace(0, r1)) -xy1 = np.row_stack([[0, 0], np.column_stack([x1, y1])]) -s1 = np.abs(xy1).max() - -x2 = np.cos(2 * np.pi * np.linspace(r1, r2)) -y2 = np.sin(2 * np.pi * np.linspace(r1, r2)) -xy2 = np.row_stack([[0, 0], np.column_stack([x2, y2])]) -s2 = np.abs(xy2).max() - -x3 = np.cos(2 * np.pi * np.linspace(r2, 1)) -y3 = np.sin(2 * np.pi * np.linspace(r2, 1)) -xy3 = np.row_stack([[0, 0], np.column_stack([x3, y3])]) -s3 = np.abs(xy3).max() - -fig, ax = plt.subplots() -ax.scatter(range(3), range(3), marker=xy1, s=s1**2 * sizes, facecolor='blue') -ax.scatter(range(3), range(3), marker=xy2, s=s2**2 * sizes, facecolor='green') -ax.scatter(range(3), range(3), marker=xy3, s=s3**2 * sizes, facecolor='red') - -plt.show() - -############################################################################# -# -# ------------ -# -# References -# """""""""" -# -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.scatter -matplotlib.pyplot.scatter diff --git a/examples/lines_bars_and_markers/scatter_symbol.py b/examples/lines_bars_and_markers/scatter_symbol.py deleted file mode 100644 index d99b6a80a740..000000000000 --- a/examples/lines_bars_and_markers/scatter_symbol.py +++ /dev/null @@ -1,25 +0,0 @@ -""" -============== -Scatter Symbol -============== - -Scatter plot with clover symbols. - -""" -import matplotlib.pyplot as plt -import numpy as np - -# Fixing random state for reproducibility -np.random.seed(19680801) - - -x = np.arange(0.0, 50.0, 2.0) -y = x ** 1.3 + np.random.rand(*x.shape) * 30.0 -s = np.random.rand(*x.shape) * 800 + 500 - -plt.scatter(x, y, s, c="g", alpha=0.5, marker=r'$\clubsuit$', - label="Luck") -plt.xlabel("Leprechauns") -plt.ylabel("Gold") -plt.legend(loc='upper left') -plt.show() diff --git a/examples/lines_bars_and_markers/scatter_with_legend.py b/examples/lines_bars_and_markers/scatter_with_legend.py index 6ff312711aa5..56e539644bf9 100644 --- a/examples/lines_bars_and_markers/scatter_with_legend.py +++ b/examples/lines_bars_and_markers/scatter_with_legend.py @@ -12,9 +12,10 @@ """ import numpy as np -np.random.seed(19680801) import matplotlib.pyplot as plt +np.random.seed(19680801) + fig, ax = plt.subplots() for color in ['tab:blue', 'tab:orange', 'tab:green']: @@ -100,16 +101,11 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The usage of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.scatter -matplotlib.pyplot.scatter -matplotlib.axes.Axes.legend -matplotlib.pyplot.legend -matplotlib.collections.PathCollection.legend_elements +# - `matplotlib.axes.Axes.scatter` / `matplotlib.pyplot.scatter` +# - `matplotlib.axes.Axes.legend` / `matplotlib.pyplot.legend` +# - `matplotlib.collections.PathCollection.legend_elements` diff --git a/examples/lines_bars_and_markers/simple_plot.py b/examples/lines_bars_and_markers/simple_plot.py index f51e16015b2e..86b51db41259 100644 --- a/examples/lines_bars_and_markers/simple_plot.py +++ b/examples/lines_bars_and_markers/simple_plot.py @@ -6,7 +6,6 @@ Create a simple plot. """ -import matplotlib import matplotlib.pyplot as plt import numpy as np @@ -26,14 +25,11 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -matplotlib.axes.Axes.plot -matplotlib.pyplot.plot -matplotlib.pyplot.subplots -matplotlib.figure.Figure.savefig +# - `matplotlib.axes.Axes.plot` / `matplotlib.pyplot.plot` +# - `matplotlib.pyplot.subplots` +# - `matplotlib.figure.Figure.savefig` diff --git a/examples/lines_bars_and_markers/span_regions.py b/examples/lines_bars_and_markers/span_regions.py index 54aae613de77..79ebfcd008f2 100644 --- a/examples/lines_bars_and_markers/span_regions.py +++ b/examples/lines_bars_and_markers/span_regions.py @@ -38,16 +38,12 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.collections.BrokenBarHCollection -matplotlib.collections.BrokenBarHCollection.span_where -matplotlib.axes.Axes.add_collection -matplotlib.axes.Axes.axhline +# - `matplotlib.collections.BrokenBarHCollection` +# - `matplotlib.collections.BrokenBarHCollection.span_where` +# - `matplotlib.axes.Axes.add_collection` +# - `matplotlib.axes.Axes.axhline` 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/lines_bars_and_markers/stairs_demo.py b/examples/lines_bars_and_markers/stairs_demo.py index d1f02d37ff34..79f63975b32b 100644 --- a/examples/lines_bars_and_markers/stairs_demo.py +++ b/examples/lines_bars_and_markers/stairs_demo.py @@ -82,15 +82,10 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.stairs -matplotlib.pyplot.stairs -matplotlib.patches.StepPatch +# - `matplotlib.axes.Axes.stairs` / `matplotlib.pyplot.stairs` +# - `matplotlib.patches.StepPatch` diff --git a/examples/lines_bars_and_markers/stem_plot.py b/examples/lines_bars_and_markers/stem_plot.py index 57476ab5729f..4f74d50bde16 100644 --- a/examples/lines_bars_and_markers/stem_plot.py +++ b/examples/lines_bars_and_markers/stem_plot.py @@ -21,7 +21,7 @@ # The parameters *linefmt*, *markerfmt*, and *basefmt* control basic format # properties of the plot. However, in contrast to `~.pyplot.plot` not all # properties are configurable via keyword arguments. For more advanced -# control adapt the line objects returned by `~.pyplot`. +# control adapt the line objects returned by `.pyplot`. markerline, stemlines, baseline = plt.stem( x, y, linefmt='grey', markerfmt='D', bottom=1.1) @@ -30,14 +30,9 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.pyplot.stem -matplotlib.axes.Axes.stem +# - `matplotlib.axes.Axes.stem` / `matplotlib.pyplot.stem` diff --git a/examples/lines_bars_and_markers/step_demo.py b/examples/lines_bars_and_markers/step_demo.py index c079ef05daad..f1016ead5283 100644 --- a/examples/lines_bars_and_markers/step_demo.py +++ b/examples/lines_bars_and_markers/step_demo.py @@ -56,16 +56,10 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.step -matplotlib.pyplot.step -matplotlib.axes.Axes.plot -matplotlib.pyplot.plot +# - `matplotlib.axes.Axes.step` / `matplotlib.pyplot.step` +# - `matplotlib.axes.Axes.plot` / `matplotlib.pyplot.plot` diff --git a/examples/lines_bars_and_markers/timeline.py b/examples/lines_bars_and_markers/timeline.py index ee1d016f8f95..087e7320f6b8 100644 --- a/examples/lines_bars_and_markers/timeline.py +++ b/examples/lines_bars_and_markers/timeline.py @@ -97,18 +97,14 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.annotate -matplotlib.axes.Axes.vlines -matplotlib.axis.Axis.set_major_locator -matplotlib.axis.Axis.set_major_formatter -matplotlib.dates.MonthLocator -matplotlib.dates.DateFormatter +# - `matplotlib.axes.Axes.annotate` +# - `matplotlib.axes.Axes.vlines` +# - `matplotlib.axis.Axis.set_major_locator` +# - `matplotlib.axis.Axis.set_major_formatter` +# - `matplotlib.dates.MonthLocator` +# - `matplotlib.dates.DateFormatter` diff --git a/examples/lines_bars_and_markers/xcorr_acorr_demo.py b/examples/lines_bars_and_markers/xcorr_acorr_demo.py index e78eefa84493..493cad034de8 100644 --- a/examples/lines_bars_and_markers/xcorr_acorr_demo.py +++ b/examples/lines_bars_and_markers/xcorr_acorr_demo.py @@ -26,16 +26,10 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.acorr -matplotlib.axes.Axes.xcorr -matplotlib.pyplot.acorr -matplotlib.pyplot.xcorr +# - `matplotlib.axes.Axes.acorr` / `matplotlib.pyplot.acorr` +# - `matplotlib.axes.Axes.xcorr` / `matplotlib.pyplot.xcorr` diff --git a/examples/misc/agg_buffer.py b/examples/misc/agg_buffer.py deleted file mode 100644 index e63dcbfda1f6..000000000000 --- a/examples/misc/agg_buffer.py +++ /dev/null @@ -1,28 +0,0 @@ -""" -========== -Agg Buffer -========== - -Use backend agg to access the figure canvas as an RGBA buffer, convert it to an -array, and pass it to Pillow for rendering. -""" - -import numpy as np - -from matplotlib.backends.backend_agg import FigureCanvasAgg -import matplotlib.pyplot as plt - -plt.plot([1, 2, 3]) - -canvas = plt.gcf().canvas - -agg = canvas.switch_backends(FigureCanvasAgg) -agg.draw() -X = np.asarray(agg.buffer_rgba()) - -# Pass off to PIL. -from PIL import Image -im = Image.fromarray(X) - -# Uncomment this line to display the image using ImageMagick's `display` tool. -# im.show() diff --git a/examples/misc/agg_buffer_to_array.py b/examples/misc/agg_buffer_to_array.py deleted file mode 100644 index 1202702367ae..000000000000 --- a/examples/misc/agg_buffer_to_array.py +++ /dev/null @@ -1,30 +0,0 @@ -""" -=================== -Agg Buffer To Array -=================== - -Convert a rendered figure to its image (NumPy array) representation. -""" -import matplotlib.pyplot as plt -import numpy as np -from matplotlib.figure import Figure -from matplotlib.backends.backend_agg import FigureCanvas - -# Create a figure that pyplot does not know about. -fig = Figure() -# attach a non-interactive Agg canvas to the figure -# (as a side-effect of the ``__init__``) -canvas = FigureCanvas(fig) -ax = fig.subplots() -ax.plot([1, 2, 3]) -ax.set_title('a simple figure') -# Force a draw so we can grab the pixel buffer -canvas.draw() -# grab the pixel buffer and dump it into a numpy array -X = np.array(canvas.renderer.buffer_rgba()) - -# now display the array X as an Axes in a new figure -fig2 = plt.figure() -ax2 = fig2.add_subplot(frameon=False) -ax2.imshow(X) -plt.show() diff --git a/examples/misc/anchored_artists.py b/examples/misc/anchored_artists.py index 80176e40f3ec..101c692ced3a 100644 --- a/examples/misc/anchored_artists.py +++ b/examples/misc/anchored_artists.py @@ -9,76 +9,43 @@ :doc:`/gallery/axes_grid1/simple_anchored_artists`, but it is implemented using only the matplotlib namespace, without the help of additional toolkits. + +.. redirect-from:: /gallery/userdemo/anchored_box01 +.. redirect-from:: /gallery/userdemo/anchored_box02 +.. redirect-from:: /gallery/userdemo/anchored_box03 """ from matplotlib import pyplot as plt from matplotlib.lines import Line2D -from matplotlib.patches import Ellipse +from matplotlib.patches import Circle, Ellipse from matplotlib.offsetbox import ( AnchoredOffsetbox, AuxTransformBox, DrawingArea, TextArea, VPacker) -class AnchoredText(AnchoredOffsetbox): - def __init__(self, s, loc, pad=0.4, borderpad=0.5, - prop=None, frameon=True): - self.txt = TextArea(s) - super().__init__(loc, pad=pad, borderpad=borderpad, - child=self.txt, prop=prop, frameon=frameon) - - def draw_text(ax): - """ - Draw a text-box anchored to the upper-left corner of the figure. - """ - at = AnchoredText("Figure 1a", loc='upper left', frameon=True) - at.patch.set_boxstyle("round,pad=0.,rounding_size=0.2") - ax.add_artist(at) - - -class AnchoredDrawingArea(AnchoredOffsetbox): - def __init__(self, width, height, xdescent, ydescent, - loc, pad=0.4, borderpad=0.5, prop=None, frameon=True): - self.da = DrawingArea(width, height, xdescent, ydescent) - super().__init__(loc, pad=pad, borderpad=borderpad, - child=self.da, prop=None, frameon=frameon) - - -def draw_circle(ax): - """ - Draw a circle in axis coordinates - """ - from matplotlib.patches import Circle - ada = AnchoredDrawingArea(20, 20, 0, 0, - loc='upper right', pad=0., frameon=False) - p = Circle((10, 10), 10) - ada.da.add_artist(p) - ax.add_artist(ada) + """Draw a text-box anchored to the upper-left corner of the figure.""" + box = AnchoredOffsetbox(child=TextArea("Figure 1a"), + loc="upper left", frameon=True) + box.patch.set_boxstyle("round,pad=0.,rounding_size=0.2") + ax.add_artist(box) -class AnchoredEllipse(AnchoredOffsetbox): - def __init__(self, transform, width, height, angle, loc, - pad=0.1, borderpad=0.1, prop=None, frameon=True): - """ - Draw an ellipse the size in data coordinate of the give axes. - - pad, borderpad in fraction of the legend font size (or prop) - """ - self._box = AuxTransformBox(transform) - self.ellipse = Ellipse((0, 0), width, height, angle) - self._box.add_artist(self.ellipse) - super().__init__(loc, pad=pad, borderpad=borderpad, - child=self._box, prop=prop, frameon=frameon) +def draw_circles(ax): + """Draw circles in axes coordinates.""" + area = DrawingArea(40, 20, 0, 0) + area.add_artist(Circle((10, 10), 10, fc="tab:blue")) + area.add_artist(Circle((30, 10), 5, fc="tab:red")) + box = AnchoredOffsetbox( + child=area, loc="upper right", pad=0, frameon=False) + ax.add_artist(box) def draw_ellipse(ax): - """ - Draw an ellipse of width=0.1, height=0.15 in data coordinates - """ - ae = AnchoredEllipse(ax.transData, width=0.1, height=0.15, angle=0., - loc='lower left', pad=0.5, borderpad=0.4, - frameon=True) - - ax.add_artist(ae) + """Draw an ellipse of width=0.1, height=0.15 in data coordinates.""" + aux_tr_box = AuxTransformBox(ax.transData) + aux_tr_box.add_artist(Ellipse((0, 0), width=0.1, height=0.15)) + box = AnchoredOffsetbox(child=aux_tr_box, loc="lower left", frameon=True) + ax.add_artist(box) class AnchoredSizeBar(AnchoredOffsetbox): @@ -115,11 +82,11 @@ def draw_sizebar(ax): ax.add_artist(asb) -ax = plt.gca() -ax.set_aspect(1.) +fig, ax = plt.subplots() +ax.set_aspect(1) draw_text(ax) -draw_circle(ax) +draw_circles(ax) draw_ellipse(ax) draw_sizebar(ax) diff --git a/examples/misc/coords_report.py b/examples/misc/coords_report.py index 84ce03e09a7f..127ce712fc1e 100644 --- a/examples/misc/coords_report.py +++ b/examples/misc/coords_report.py @@ -3,7 +3,8 @@ Coords Report ============= -Override the default reporting of coords. +Override the default reporting of coords as the mouse moves over the axes +in an interactive backend. """ import matplotlib.pyplot as plt @@ -11,14 +12,14 @@ def millions(x): - return '$%1.1fM' % (x*1e-6) + return '$%1.1fM' % (x * 1e-6) # Fixing random state for reproducibility np.random.seed(19680801) x = np.random.rand(20) -y = 1e7*np.random.rand(20) +y = 1e7 * np.random.rand(20) fig, ax = plt.subplots() ax.fmt_ydata = millions diff --git a/examples/misc/custom_projection.py b/examples/misc/custom_projection.py index 7e5a4b0d405c..4f6e85fc30fd 100644 --- a/examples/misc/custom_projection.py +++ b/examples/misc/custom_projection.py @@ -52,8 +52,9 @@ def _init_axis(self): # self.spines['geo'].register_axis(self.yaxis) self._update_transScale() - def cla(self): - super().cla() + def clear(self): + # docstring inherited + super().clear() self.set_longitude_grid(30) self.set_latitude_grid(15) @@ -270,14 +271,8 @@ def format_coord(self, lon, lat): In this case, we want them to be displayed in degrees N/S/E/W. """ lon, lat = np.rad2deg([lon, lat]) - if lat >= 0.0: - ns = 'N' - else: - ns = 'S' - if lon >= 0.0: - ew = 'E' - else: - ew = 'W' + ns = 'N' if lat >= 0.0 else 'S' + ew = 'E' if lon >= 0.0 else 'W' return ('%f\N{DEGREE SIGN}%s, %f\N{DEGREE SIGN}%s' % (abs(lat), ns, abs(lon), ew)) @@ -430,7 +425,7 @@ def __init__(self, *args, **kwargs): self._longitude_cap = np.pi / 2.0 super().__init__(*args, **kwargs) self.set_aspect(0.5, adjustable='box', anchor='C') - self.cla() + self.clear() def _get_core_transform(self, resolution): return self.HammerTransform(resolution) diff --git a/examples/misc/demo_agg_filter.py b/examples/misc/demo_agg_filter.py index 16f1221563d2..0cdbf81f50b5 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 @@ -19,7 +19,7 @@ def smooth1d(x, window_len): - # copied from http://www.scipy.org/Cookbook/SignalSmooth + # copied from https://scipy-cookbook.readthedocs.io/items/SignalSmooth.html s = np.r_[2*x[0] - x[window_len:1:-1], x, 2*x[-1] - x[-1:-window_len:-1]] w = np.hanning(window_len) y = np.convolve(w/w.sum(), s, mode='same') @@ -38,7 +38,7 @@ class BaseFilter: def get_pad(self, dpi): return 0 - def process_image(padded_src, dpi): + def process_image(self, padded_src, dpi): raise NotImplementedError("Should be overridden by subclasses") def __call__(self, im, dpi): @@ -99,8 +99,19 @@ def process_image(self, padded_src, dpi): class LightFilter(BaseFilter): - - def __init__(self, sigma, fraction=0.5): + """Apply LightSource filter""" + + def __init__(self, sigma, fraction=1): + """ + Parameters + ---------- + sigma : float + sigma for gaussian filter + fraction: number, default: 1 + Increases or decreases the contrast of the hillshade. + See `matplotlib.colors.LightSource` + + """ self.gauss_filter = GaussianFilter(sigma, alpha=1) self.light_source = LightSource() self.fraction = fraction @@ -114,7 +125,8 @@ def process_image(self, padded_src, dpi): rgb = padded_src[:, :, :3] alpha = padded_src[:, :, 3:] rgb2 = self.light_source.shade_rgb(rgb, elevation, - fraction=self.fraction) + fraction=self.fraction, + blend_mode="overlay") return np.concatenate([rgb2, alpha], -1) @@ -213,10 +225,9 @@ def drop_shadow_line(ax): shadow.update_from(l) # offset transform - ot = mtransforms.offset_copy(l.get_transform(), ax.figure, - x=4.0, y=-6.0, units='points') - - shadow.set_transform(ot) + transform = mtransforms.offset_copy(l.get_transform(), ax.figure, + x=4.0, y=-6.0, units='points') + shadow.set_transform(transform) # adjust zorder of the shadow lines so that it is drawn below the # original lines @@ -234,20 +245,19 @@ def drop_shadow_line(ax): def drop_shadow_patches(ax): # Copied from barchart_demo.py N = 5 - men_means = [20, 35, 30, 35, 27] + group1_means = [20, 35, 30, 35, 27] ind = np.arange(N) # the x locations for the groups width = 0.35 # the width of the bars - rects1 = ax.bar(ind, men_means, width, color='r', ec="w", lw=2) + rects1 = ax.bar(ind, group1_means, width, color='r', ec="w", lw=2) - women_means = [25, 32, 34, 20, 25] - rects2 = ax.bar(ind + width + 0.1, women_means, width, + group2_means = [25, 32, 34, 20, 25] + rects2 = ax.bar(ind + width + 0.1, group2_means, width, color='y', ec="w", lw=2) - # gauss = GaussianFilter(1.5, offsets=(1, 1)) - gauss = DropShadowFilter(5, offsets=(1, 1)) - shadow = FilteredArtistList(rects1 + rects2, gauss) + drop = DropShadowFilter(5, offsets=(1, 1)) + shadow = FilteredArtistList(rects1 + rects2, drop) ax.add_artist(shadow) shadow.set_zorder(rects1[0].get_zorder() - 0.1) @@ -259,7 +269,7 @@ def drop_shadow_patches(ax): def light_filter_pie(ax): fracs = [15, 30, 45, 10] - explode = (0, 0.05, 0, 0) + explode = (0.1, 0.2, 0.1, 0.1) pies = ax.pie(fracs, explode=explode) light_filter = LightFilter(9) @@ -269,7 +279,7 @@ def light_filter_pie(ax): p.set(ec="none", lw=2) - gauss = DropShadowFilter(9, offsets=(3, 4), alpha=0.7) + gauss = DropShadowFilter(9, offsets=(3, -4), alpha=0.7) shadow = FilteredArtistList(pies[0], gauss) ax.add_artist(shadow) shadow.set_zorder(pies[0][0].get_zorder() - 0.1) diff --git a/examples/pyplots/fig_x.py b/examples/misc/fig_x.py similarity index 60% rename from examples/pyplots/fig_x.py rename to examples/misc/fig_x.py index 52a8840892cc..eaa16d80fa35 100644 --- a/examples/pyplots/fig_x.py +++ b/examples/misc/fig_x.py @@ -4,6 +4,8 @@ ======================= Adding lines to a figure without any axes. + +.. redirect-from:: /gallery/pyplots/fig_x """ import matplotlib.pyplot as plt @@ -17,15 +19,11 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.pyplot.figure -matplotlib.lines -matplotlib.lines.Line2D +# - `matplotlib.pyplot.figure` +# - `matplotlib.lines` +# - `matplotlib.lines.Line2D` diff --git a/examples/misc/histogram_path.py b/examples/misc/histogram_path.py index d93df6b2c863..8cbb64977623 100644 --- a/examples/misc/histogram_path.py +++ b/examples/misc/histogram_path.py @@ -78,23 +78,20 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.patches -matplotlib.patches.PathPatch -matplotlib.path -matplotlib.path.Path -matplotlib.path.Path.make_compound_path_from_polys -matplotlib.axes.Axes.add_patch -matplotlib.collections.PathCollection - -# This example shows an alternative to -matplotlib.collections.PolyCollection -matplotlib.axes.Axes.hist +# - `matplotlib.patches` +# - `matplotlib.patches.PathPatch` +# - `matplotlib.path` +# - `matplotlib.path.Path` +# - `matplotlib.path.Path.make_compound_path_from_polys` +# - `matplotlib.axes.Axes.add_patch` +# - `matplotlib.collections.PathCollection` +# +# This example shows an alternative to +# +# - `matplotlib.collections.PolyCollection` +# - `matplotlib.axes.Axes.hist` diff --git a/examples/misc/hyperlinks_sgskip.py b/examples/misc/hyperlinks_sgskip.py index 7f9cade91a29..2d0f6819cfb8 100644 --- a/examples/misc/hyperlinks_sgskip.py +++ b/examples/misc/hyperlinks_sgskip.py @@ -18,7 +18,7 @@ fig = plt.figure() s = plt.scatter([1, 2, 3], [4, 5, 6]) -s.set_urls(['http://www.bbc.co.uk/news', 'http://www.google.com', None]) +s.set_urls(['https://www.bbc.com/news', 'https://www.google.com/', None]) fig.savefig('scatter.svg') ############################################################################### @@ -34,5 +34,5 @@ im = plt.imshow(Z, interpolation='bilinear', cmap=cm.gray, origin='lower', extent=[-3, 3, -3, 3]) -im.set_url('http://www.google.com') +im.set_url('https://www.google.com/') fig.savefig('image.svg') diff --git a/examples/misc/image_thumbnail_sgskip.py b/examples/misc/image_thumbnail_sgskip.py index 97a9e9a627ea..68e3ba85ed38 100644 --- a/examples/misc/image_thumbnail_sgskip.py +++ b/examples/misc/image_thumbnail_sgskip.py @@ -20,7 +20,7 @@ description="Build thumbnails of all images in a directory.") parser.add_argument("imagedir", type=Path) args = parser.parse_args() -if not args.imagedir.isdir(): +if not args.imagedir.is_dir(): sys.exit(f"Could not find input directory {args.imagedir}") outdir = Path("thumbs") diff --git a/examples/misc/load_converter.py b/examples/misc/load_converter.py deleted file mode 100644 index 793de7dc9264..000000000000 --- a/examples/misc/load_converter.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -============== -Load converter -============== - -This example demonstrates passing a custom converter to `numpy.genfromtxt` to -extract dates from a CSV file. -""" - -import dateutil.parser -from matplotlib import cbook -import matplotlib.pyplot as plt -import numpy as np - - -datafile = cbook.get_sample_data('msft.csv', asfileobj=False) -print('loading', datafile) - -data = np.genfromtxt( - datafile, delimiter=',', names=True, - dtype=None, converters={0: dateutil.parser.parse}) - -fig, ax = plt.subplots() -ax.plot(data['Date'], data['High'], '-') -fig.autofmt_xdate() -plt.show() diff --git a/examples/misc/logos2.py b/examples/misc/logos2.py index 528f09e92c18..06e3b5d65e7a 100644 --- a/examples/misc/logos2.py +++ b/examples/misc/logos2.py @@ -89,7 +89,7 @@ def create_icon_axes(fig, ax_position, lw_bars, lw_grid, lw_border, rgrid): def create_text_axes(fig, height_px): - """Create an axes in *fig* that contains 'matplotlib' as Text.""" + """Create an Axes in *fig* that contains 'matplotlib' as Text.""" ax = fig.add_axes((0, 0, 1, 1)) ax.set_aspect("equal") ax.set_axis_off() diff --git a/examples/misc/pythonic_matplotlib.py b/examples/misc/pythonic_matplotlib.py index b04d931264f0..1d9f2b5bcf9f 100644 --- a/examples/misc/pythonic_matplotlib.py +++ b/examples/misc/pythonic_matplotlib.py @@ -3,9 +3,9 @@ Pythonic Matplotlib =================== -Some people prefer to write more pythonic, object-oriented code -rather than use the pyplot interface to matplotlib. This example shows -you how. +Some people prefer to write more "Pythonic", explicit object-oriented code, +rather than use the implicit pyplot interface to Matplotlib. This example +shows you how to take advantage of the explicit Matplotlib interface. Unless you are an application developer, I recommend using part of the pyplot interface, particularly the figure, close, subplot, axes, and @@ -14,7 +14,7 @@ instances, managing the bounding boxes of the figure elements, creating and realizing GUI windows and embedding figures in them. -If you are an application developer and want to embed matplotlib in +If you are an application developer and want to embed Matplotlib in your application, follow the lead of examples/embedding_in_wx.py, examples/embedding_in_gtk.py or examples/embedding_in_tk.py. In this case you will want to control the creation of all your figures, @@ -28,7 +28,7 @@ application developers, however. If you see an example in the examples dir written in pyplot interface, -and you want to emulate that using the true python method calls, there +and you want to emulate that using the true Python method calls, there is an easy mapping. Many of those examples use 'set' to control figure properties. Here's how to map those commands onto instance methods @@ -52,6 +52,7 @@ a.set_yticklabels([]) a.set_xticks([]) a.set_yticks([]) + """ import matplotlib.pyplot as plt diff --git a/examples/misc/rasterization_demo.py b/examples/misc/rasterization_demo.py index 6df264d7dac7..824b6ffb3c97 100644 --- a/examples/misc/rasterization_demo.py +++ b/examples/misc/rasterization_demo.py @@ -84,16 +84,11 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.artist.Artist.set_rasterized -matplotlib.axes.Axes.set_rasterization_zorder -matplotlib.axes.Axes.pcolormesh -matplotlib.pyplot.pcolormesh +# - `matplotlib.artist.Artist.set_rasterized` +# - `matplotlib.axes.Axes.set_rasterization_zorder` +# - `matplotlib.axes.Axes.pcolormesh` / `matplotlib.pyplot.pcolormesh` diff --git a/examples/misc/svg_filter_line.py b/examples/misc/svg_filter_line.py index d329e6f243b1..da67250890df 100644 --- a/examples/misc/svg_filter_line.py +++ b/examples/misc/svg_filter_line.py @@ -3,12 +3,14 @@ SVG Filter Line =============== -Demonstrate SVG filtering effects which might be used with mpl. +Demonstrate SVG filtering effects which might be used with Matplotlib. -Note that the filtering effects are only effective if your svg renderer +Note that the filtering effects are only effective if your SVG renderer support it. """ +import io +import xml.etree.ElementTree as ET import matplotlib.pyplot as plt import matplotlib.transforms as mtransforms @@ -39,10 +41,9 @@ shadow.set_zorder(l.get_zorder() - 0.5) # offset transform - ot = mtransforms.offset_copy(l.get_transform(), fig1, - x=4.0, y=-6.0, units='points') - - shadow.set_transform(ot) + transform = mtransforms.offset_copy(l.get_transform(), fig1, + x=4.0, y=-6.0, units='points') + shadow.set_transform(transform) # set the id for a later use shadow.set_gid(l.get_label() + "_shadow") @@ -52,13 +53,10 @@ ax.set_ylim(0., 1.) # save the figure as a bytes string in the svg format. -from io import BytesIO -f = BytesIO() +f = io.BytesIO() plt.savefig(f, format="svg") -import xml.etree.ElementTree as ET - # filter definition for a gaussian blur filter_def = """ `. - -Note that a similar result would be achieved using `~.Figure.tight_layout` -or `~.Figure.set_constrained_layout`; this example shows how one could -customize the subplot parameter adjustment. -""" - -import matplotlib.pyplot as plt -import matplotlib.transforms as mtransforms - -fig, ax = plt.subplots() -ax.plot(range(10)) -ax.set_yticks((2, 5, 7)) -labels = ax.set_yticklabels(('really, really, really', 'long', 'labels')) - -def on_draw(event): - bboxes = [] - for label in labels: - bbox = label.get_window_extent() - # the figure transform goes from relative coords->pixels and we - # want the inverse of that - bboxi = bbox.transformed(fig.transFigure.inverted()) - bboxes.append(bboxi) - # the bbox that bounds all the bboxes, again in relative figure coords - bbox = mtransforms.Bbox.union(bboxes) - if fig.subplotpars.left < bbox.width: - # we need to move it over - fig.subplots_adjust(left=1.1*bbox.width) # pad a little - fig.canvas.draw() - -fig.canvas.mpl_connect('draw_event', on_draw) - -plt.show() - -############################################################################# -# -# ------------ -# -# References -# """""""""" -# -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.artist.Artist.get_window_extent -matplotlib.transforms.Bbox -matplotlib.transforms.Bbox.transformed -matplotlib.transforms.Bbox.union -matplotlib.transforms.Transform.inverted -matplotlib.figure.Figure.subplots_adjust -matplotlib.figure.SubplotParams -matplotlib.backend_bases.FigureCanvasBase.mpl_connect diff --git a/examples/pyplots/axline.py b/examples/pyplots/axline.py index 94694e4e7a4e..68149eaafc4f 100644 --- a/examples/pyplots/axline.py +++ b/examples/pyplots/axline.py @@ -42,18 +42,11 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.pyplot.axhline -matplotlib.pyplot.axvline -matplotlib.pyplot.axline -matplotlib.axes.Axes.axhline -matplotlib.axes.Axes.axvline -matplotlib.axes.Axes.axline +# - `matplotlib.axes.Axes.axhline` / `matplotlib.pyplot.axhline` +# - `matplotlib.axes.Axes.axvline` / `matplotlib.pyplot.axvline` +# - `matplotlib.axes.Axes.axline` / `matplotlib.pyplot.axline` diff --git a/examples/pyplots/boxplot_demo_pyplot.py b/examples/pyplots/boxplot_demo_pyplot.py deleted file mode 100644 index a92be1a3f808..000000000000 --- a/examples/pyplots/boxplot_demo_pyplot.py +++ /dev/null @@ -1,94 +0,0 @@ -""" -============ -Boxplot Demo -============ - -Example boxplot code -""" - -import numpy as np -import matplotlib.pyplot as plt - -# Fixing random state for reproducibility -np.random.seed(19680801) - -# fake up some data -spread = np.random.rand(50) * 100 -center = np.ones(25) * 50 -flier_high = np.random.rand(10) * 100 + 100 -flier_low = np.random.rand(10) * -100 -data = np.concatenate((spread, center, flier_high, flier_low)) - -############################################################################### - -fig1, ax1 = plt.subplots() -ax1.set_title('Basic Plot') -ax1.boxplot(data) - -############################################################################### - -fig2, ax2 = plt.subplots() -ax2.set_title('Notched boxes') -ax2.boxplot(data, notch=True) - -############################################################################### - -green_diamond = dict(markerfacecolor='g', marker='D') -fig3, ax3 = plt.subplots() -ax3.set_title('Changed Outlier Symbols') -ax3.boxplot(data, flierprops=green_diamond) - -############################################################################### - -fig4, ax4 = plt.subplots() -ax4.set_title('Hide Outlier Points') -ax4.boxplot(data, showfliers=False) - -############################################################################### - -red_square = dict(markerfacecolor='r', marker='s') -fig5, ax5 = plt.subplots() -ax5.set_title('Horizontal Boxes') -ax5.boxplot(data, vert=False, flierprops=red_square) - -############################################################################### - -fig6, ax6 = plt.subplots() -ax6.set_title('Shorter Whisker Length') -ax6.boxplot(data, flierprops=red_square, vert=False, whis=0.75) - -############################################################################### -# Fake up some more data - -spread = np.random.rand(50) * 100 -center = np.ones(25) * 40 -flier_high = np.random.rand(10) * 100 + 100 -flier_low = np.random.rand(10) * -100 -d2 = np.concatenate((spread, center, flier_high, flier_low)) - -############################################################################### -# Making a 2-D array only works if all the columns are the -# same length. If they are not, then use a list instead. -# This is actually more efficient because boxplot converts -# a 2-D array into a list of vectors internally anyway. - -data = [data, d2, d2[::2]] -fig7, ax7 = plt.subplots() -ax7.set_title('Multiple Samples with Different sizes') -ax7.boxplot(data) - -plt.show() - -############################################################################# -# -# ------------ -# -# References -# """""""""" -# -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.boxplot -matplotlib.pyplot.boxplot diff --git a/examples/pyplots/fig_axes_customize_simple.py b/examples/pyplots/fig_axes_customize_simple.py deleted file mode 100644 index bc77aab61441..000000000000 --- a/examples/pyplots/fig_axes_customize_simple.py +++ /dev/null @@ -1,57 +0,0 @@ -""" -========================= -Fig Axes Customize Simple -========================= - -Customize the background, labels and ticks of a simple plot. -""" - -import matplotlib.pyplot as plt - -############################################################################### -# `.pyplot.figure` creates a `matplotlib.figure.Figure` instance. - -fig = plt.figure() -rect = fig.patch # a rectangle instance -rect.set_facecolor('lightgoldenrodyellow') - -ax1 = fig.add_axes([0.1, 0.3, 0.4, 0.4]) -rect = ax1.patch -rect.set_facecolor('lightslategray') - - -for label in ax1.xaxis.get_ticklabels(): - # label is a Text instance - label.set_color('tab:red') - label.set_rotation(45) - label.set_fontsize(16) - -for line in ax1.yaxis.get_ticklines(): - # line is a Line2D instance - line.set_color('tab:green') - line.set_markersize(25) - line.set_markeredgewidth(3) - -plt.show() - -############################################################################# -# -# ------------ -# -# References -# """""""""" -# -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axis.Axis.get_ticklabels -matplotlib.axis.Axis.get_ticklines -matplotlib.text.Text.set_rotation -matplotlib.text.Text.set_fontsize -matplotlib.text.Text.set_color -matplotlib.lines.Line2D -matplotlib.lines.Line2D.set_color -matplotlib.lines.Line2D.set_markersize -matplotlib.lines.Line2D.set_markeredgewidth -matplotlib.patches.Patch.set_facecolor diff --git a/examples/pyplots/fig_axes_labels_simple.py b/examples/pyplots/fig_axes_labels_simple.py deleted file mode 100644 index b7d3bd25b74c..000000000000 --- a/examples/pyplots/fig_axes_labels_simple.py +++ /dev/null @@ -1,46 +0,0 @@ -""" -================== -Simple axes labels -================== - -Label the axes of a plot. -""" -import numpy as np -import matplotlib.pyplot as plt - -fig = plt.figure() -fig.subplots_adjust(top=0.8) -ax1 = fig.add_subplot(211) -ax1.set_ylabel('volts') -ax1.set_title('a sine wave') - -t = np.arange(0.0, 1.0, 0.01) -s = np.sin(2 * np.pi * t) -line, = ax1.plot(t, s, lw=2) - -# Fixing random state for reproducibility -np.random.seed(19680801) - -ax2 = fig.add_axes([0.15, 0.1, 0.7, 0.3]) -n, bins, patches = ax2.hist(np.random.randn(1000), 50) -ax2.set_xlabel('time (s)') - -plt.show() - -############################################################################# -# -# ------------ -# -# References -# """""""""" -# -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.set_xlabel -matplotlib.axes.Axes.set_ylabel -matplotlib.axes.Axes.set_title -matplotlib.axes.Axes.plot -matplotlib.axes.Axes.hist -matplotlib.figure.Figure.add_axes diff --git a/examples/pyplots/pyplot_formatstr.py b/examples/pyplots/pyplot_formatstr.py deleted file mode 100644 index 3e5b1806b5c5..000000000000 --- a/examples/pyplots/pyplot_formatstr.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -==================== -plot() format string -==================== - -Use a format string (here, 'ro') to set the color and markers of a -`~matplotlib.axes.Axes.plot`. -""" - -import matplotlib.pyplot as plt -plt.plot([1, 2, 3, 4], [1, 4, 9, 16], 'ro') -plt.show() - -############################################################################# -# -# ------------ -# -# References -# """""""""" -# -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.pyplot.plot -matplotlib.axes.Axes.plot diff --git a/examples/pyplots/pyplot_mathtext.py b/examples/pyplots/pyplot_mathtext.py index 40bbaccf72d2..a62dd6d95da0 100644 --- a/examples/pyplots/pyplot_mathtext.py +++ b/examples/pyplots/pyplot_mathtext.py @@ -16,20 +16,15 @@ plt.text(1, -0.6, r'$\sum_{i=0}^\infty x_i$', fontsize=20) plt.text(0.6, 0.6, r'$\mathcal{A}\mathrm{sin}(2 \omega t)$', fontsize=20) -plt.xlabel('time (s)') -plt.ylabel('volts (mV)') +plt.xlabel('Time [s]') +plt.ylabel('Voltage [mV]') plt.show() ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.pyplot.text -matplotlib.axes.Axes.text +# - `matplotlib.axes.Axes.text` / `matplotlib.pyplot.text` diff --git a/examples/pyplots/pyplot_simple.py b/examples/pyplots/pyplot_simple.py index 25bb924c76ed..1a3b457acedd 100644 --- a/examples/pyplots/pyplot_simple.py +++ b/examples/pyplots/pyplot_simple.py @@ -3,26 +3,27 @@ Pyplot Simple ============= -A very simple pyplot where a list of numbers are ploted against their +A very simple pyplot where a list of numbers are plotted against their index. Creates a straight line due to the rate of change being 1 for -both the X and Y axis. +both the X and Y axis. Use a format string (here, 'o-r') to set the +markers (circles), linestyle (solid line) and color (red). + +.. redirect-from:: /gallery/pyplots/fig_axes_labels_simple +.. redirect-from:: /gallery/pyplots/pyplot_formatstr """ import matplotlib.pyplot as plt -plt.plot([1, 2, 3, 4]) + +plt.plot([1, 2, 3, 4], 'o-r') plt.ylabel('some numbers') plt.show() ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.pyplot.plot -matplotlib.pyplot.ylabel -matplotlib.pyplot.show +# - `matplotlib.pyplot.plot` +# - `matplotlib.pyplot.ylabel` +# - `matplotlib.pyplot.show` diff --git a/examples/pyplots/pyplot_text.py b/examples/pyplots/pyplot_text.py index b5952ba00706..e50e9038827a 100644 --- a/examples/pyplots/pyplot_text.py +++ b/examples/pyplots/pyplot_text.py @@ -28,18 +28,14 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.pyplot.hist -matplotlib.pyplot.xlabel -matplotlib.pyplot.ylabel -matplotlib.pyplot.text -matplotlib.pyplot.grid -matplotlib.pyplot.show +# - `matplotlib.pyplot.hist` +# - `matplotlib.pyplot.xlabel` +# - `matplotlib.pyplot.ylabel` +# - `matplotlib.pyplot.text` +# - `matplotlib.pyplot.grid` +# - `matplotlib.pyplot.show` diff --git a/examples/pyplots/pyplot_three.py b/examples/pyplots/pyplot_three.py index 9026e4acae1f..476796b45f81 100644 --- a/examples/pyplots/pyplot_three.py +++ b/examples/pyplots/pyplot_three.py @@ -17,14 +17,9 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.pyplot.plot -matplotlib.axes.Axes.plot +# - `matplotlib.axes.Axes.plot` / `matplotlib.pyplot.plot` diff --git a/examples/pyplots/pyplot_two_subplots.py b/examples/pyplots/pyplot_two_subplots.py index 9a2f1ada246c..5280fb0bb37a 100644 --- a/examples/pyplots/pyplot_two_subplots.py +++ b/examples/pyplots/pyplot_two_subplots.py @@ -27,14 +27,10 @@ def f(t): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.pyplot.figure -matplotlib.pyplot.subplot +# - `matplotlib.pyplot.figure` +# - `matplotlib.pyplot.subplot` diff --git a/examples/pyplots/text_layout.py b/examples/pyplots/text_layout.py deleted file mode 100644 index 8b1db3b6af3c..000000000000 --- a/examples/pyplots/text_layout.py +++ /dev/null @@ -1,67 +0,0 @@ -""" -=========== -Text Layout -=========== - -Create text with different alignment and rotation. -""" - -import matplotlib.pyplot as plt -import matplotlib.patches as patches - -fig = plt.figure() - -left, width = .25, .5 -bottom, height = .25, .5 -right = left + width -top = bottom + height - -# Draw a rectangle in figure coordinates ((0, 0) is bottom left and (1, 1) is -# upper right). -p = patches.Rectangle((left, bottom), width, height, fill=False) -fig.add_artist(p) - -# Figure.text (aka. plt.figtext) behaves like Axes.text (aka. plt.text), with -# the sole exception that the coordinates are relative to the figure ((0, 0) is -# bottom left and (1, 1) is upper right). -fig.text(left, bottom, 'left top', - horizontalalignment='left', verticalalignment='top') -fig.text(left, bottom, 'left bottom', - horizontalalignment='left', verticalalignment='bottom') -fig.text(right, top, 'right bottom', - horizontalalignment='right', verticalalignment='bottom') -fig.text(right, top, 'right top', - horizontalalignment='right', verticalalignment='top') -fig.text(right, bottom, 'center top', - horizontalalignment='center', verticalalignment='top') -fig.text(left, 0.5*(bottom+top), 'right center', - horizontalalignment='right', verticalalignment='center', - rotation='vertical') -fig.text(left, 0.5*(bottom+top), 'left center', - horizontalalignment='left', verticalalignment='center', - rotation='vertical') -fig.text(0.5*(left+right), 0.5*(bottom+top), 'middle', - horizontalalignment='center', verticalalignment='center', - fontsize=20, color='red') -fig.text(right, 0.5*(bottom+top), 'centered', - horizontalalignment='center', verticalalignment='center', - rotation='vertical') -fig.text(left, top, 'rotated\nwith newlines', - horizontalalignment='center', verticalalignment='center', - rotation=45) - -plt.show() - -############################################################################# -# -# ------------ -# -# References -# """""""""" -# -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.figure.Figure.add_artist -matplotlib.figure.Figure.text diff --git a/examples/pyplots/whats_new_1_subplot3d.py b/examples/pyplots/whats_new_1_subplot3d.py deleted file mode 100644 index 6e304c2a94e2..000000000000 --- a/examples/pyplots/whats_new_1_subplot3d.py +++ /dev/null @@ -1,53 +0,0 @@ -""" -====================== -What's New 1 Subplot3d -====================== - -Create two three-dimensional plots in the same figure. -""" - -from matplotlib import cm -#from matplotlib.ticker import LinearLocator, FixedLocator, FormatStrFormatter -import matplotlib.pyplot as plt -import numpy as np - -fig = plt.figure() - -ax = fig.add_subplot(1, 2, 1, projection='3d') -X = np.arange(-5, 5, 0.25) -Y = np.arange(-5, 5, 0.25) -X, Y = np.meshgrid(X, Y) -R = np.sqrt(X**2 + Y**2) -Z = np.sin(R) -surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.viridis, - linewidth=0, antialiased=False) -ax.set_zlim3d(-1.01, 1.01) - -#ax.zaxis.set_major_locator(LinearLocator(10)) -#ax.zaxis.set_major_formatter(FormatStrFormatter('%.03f')) - -fig.colorbar(surf, shrink=0.5, aspect=5) - -from mpl_toolkits.mplot3d.axes3d import get_test_data -ax = fig.add_subplot(1, 2, 2, projection='3d') -X, Y, Z = get_test_data(0.05) -ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10) - -plt.show() - -############################################################################# -# -# ------------ -# -# References -# """""""""" -# -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -import mpl_toolkits -matplotlib.figure.Figure.add_subplot -mpl_toolkits.mplot3d.axes3d.Axes3D.plot_surface -mpl_toolkits.mplot3d.axes3d.Axes3D.plot_wireframe -mpl_toolkits.mplot3d.axes3d.Axes3D.set_zlim3d diff --git a/examples/pyplots/whats_new_98_4_fill_between.py b/examples/pyplots/whats_new_98_4_fill_between.py deleted file mode 100644 index 7193cfd02d1f..000000000000 --- a/examples/pyplots/whats_new_98_4_fill_between.py +++ /dev/null @@ -1,34 +0,0 @@ -""" -============ -Fill Between -============ - -Fill the area between two curves. -""" -import matplotlib.pyplot as plt -import numpy as np - -x = np.arange(-5, 5, 0.01) -y1 = -5*x*x + x + 10 -y2 = 5*x*x + x - -fig, ax = plt.subplots() -ax.plot(x, y1, x, y2, color='black') -ax.fill_between(x, y1, y2, where=(y2 > y1), facecolor='yellow', alpha=0.5) -ax.fill_between(x, y1, y2, where=(y2 <= y1), facecolor='red', alpha=0.5) -ax.set_title('Fill Between') - -plt.show() - -############################################################################# -# -# ------------ -# -# References -# """""""""" -# -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.fill_between diff --git a/examples/pyplots/whats_new_98_4_legend.py b/examples/pyplots/whats_new_98_4_legend.py deleted file mode 100644 index 01c773c2c0d4..000000000000 --- a/examples/pyplots/whats_new_98_4_legend.py +++ /dev/null @@ -1,37 +0,0 @@ -""" -======================== -What's New 0.98.4 Legend -======================== - -Create a legend and tweak it with a shadow and a box. -""" -import matplotlib.pyplot as plt -import numpy as np - - -ax = plt.subplot() -t1 = np.arange(0.0, 1.0, 0.01) -for n in [1, 2, 3, 4]: - plt.plot(t1, t1**n, label=f"n={n}") - -leg = plt.legend(loc='best', ncol=2, mode="expand", shadow=True, fancybox=True) -leg.get_frame().set_alpha(0.5) - - -plt.show() - -############################################################################# -# -# ------------ -# -# References -# """""""""" -# -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.legend -matplotlib.pyplot.legend -matplotlib.legend.Legend -matplotlib.legend.Legend.get_frame diff --git a/examples/pyplots/whats_new_99_axes_grid.py b/examples/pyplots/whats_new_99_axes_grid.py deleted file mode 100644 index 1429e0d4d268..000000000000 --- a/examples/pyplots/whats_new_99_axes_grid.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -========================= -What's New 0.99 Axes Grid -========================= - -Create RGB composite images. -""" -import numpy as np -import matplotlib.pyplot as plt -from mpl_toolkits.axes_grid1.axes_rgb import RGBAxes - - -def get_demo_image(): - # prepare image - delta = 0.5 - - extent = (-3, 4, -4, 3) - 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 = (Z1 - Z2) * 2 - - return Z, extent - - -def get_rgb(): - Z, extent = get_demo_image() - - Z[Z < 0] = 0. - Z = Z / Z.max() - - R = Z[:13, :13] - G = Z[2:, 2:] - B = Z[:13, 2:] - - return R, G, B - - -fig = plt.figure() -ax = RGBAxes(fig, [0.1, 0.1, 0.8, 0.8]) - -r, g, b = get_rgb() -ax.imshow_rgb(r, g, b, origin="lower") - -ax.RGB.set_xlim(0., 9.5) -ax.RGB.set_ylim(0.9, 10.6) - -plt.show() - -############################################################################# -# -# ------------ -# -# References -# """""""""" -# -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import mpl_toolkits -mpl_toolkits.axes_grid1.axes_rgb.RGBAxes -mpl_toolkits.axes_grid1.axes_rgb.RGBAxes.imshow_rgb diff --git a/examples/pyplots/whats_new_99_mplot3d.py b/examples/pyplots/whats_new_99_mplot3d.py deleted file mode 100644 index 28a383aa7659..000000000000 --- a/examples/pyplots/whats_new_99_mplot3d.py +++ /dev/null @@ -1,38 +0,0 @@ -""" -======================= -What's New 0.99 Mplot3d -======================= - -Create a 3D surface plot. -""" -import numpy as np -import matplotlib.pyplot as plt -from matplotlib import cm -from mpl_toolkits.mplot3d import Axes3D - -X = np.arange(-5, 5, 0.25) -Y = np.arange(-5, 5, 0.25) -X, Y = np.meshgrid(X, Y) -R = np.sqrt(X**2 + Y**2) -Z = np.sin(R) - -fig = plt.figure() -ax = Axes3D(fig, auto_add_to_figure=False) -fig.add_axes(ax) -ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.viridis) - -plt.show() - -############################################################################# -# -# ------------ -# -# References -# """""""""" -# -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import mpl_toolkits -mpl_toolkits.mplot3d.Axes3D -mpl_toolkits.mplot3d.Axes3D.plot_surface diff --git a/examples/pyplots/whats_new_99_spines.py b/examples/pyplots/whats_new_99_spines.py deleted file mode 100644 index 29aae70d85f7..000000000000 --- a/examples/pyplots/whats_new_99_spines.py +++ /dev/null @@ -1,71 +0,0 @@ -""" -====================== -What's New 0.99 Spines -====================== - -""" -import matplotlib.pyplot as plt -import numpy as np - - -def adjust_spines(ax, spines): - for loc, spine in ax.spines.items(): - if loc in spines: - spine.set_position(('outward', 10)) # outward by 10 points - else: - spine.set_color('none') # don't draw spine - - # turn off ticks where there is no spine - if 'left' in spines: - ax.yaxis.set_ticks_position('left') - else: - # no yaxis ticks - ax.yaxis.set_ticks([]) - - if 'bottom' in spines: - ax.xaxis.set_ticks_position('bottom') - else: - # no xaxis ticks - ax.xaxis.set_ticks([]) - -fig = plt.figure() - -x = np.linspace(0, 2*np.pi, 100) -y = 2*np.sin(x) - -ax = fig.add_subplot(2, 2, 1) -ax.plot(x, y) -adjust_spines(ax, ['left']) - -ax = fig.add_subplot(2, 2, 2) -ax.plot(x, y) -adjust_spines(ax, []) - -ax = fig.add_subplot(2, 2, 3) -ax.plot(x, y) -adjust_spines(ax, ['left', 'bottom']) - -ax = fig.add_subplot(2, 2, 4) -ax.plot(x, y) -adjust_spines(ax, ['bottom']) - -plt.show() - -############################################################################# -# -# ------------ -# -# References -# """""""""" -# -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axis.Axis.set_ticks -matplotlib.axis.XAxis.set_ticks_position -matplotlib.axis.YAxis.set_ticks_position -matplotlib.spines -matplotlib.spines.Spine -matplotlib.spines.Spine.set_color -matplotlib.spines.Spine.set_position diff --git a/examples/scales/asinh_demo.py b/examples/scales/asinh_demo.py new file mode 100644 index 000000000000..69e4cea4fa48 --- /dev/null +++ b/examples/scales/asinh_demo.py @@ -0,0 +1,109 @@ +""" +============ +Asinh Demo +============ + +Illustration of the `asinh <.scale.AsinhScale>` axis scaling, +which uses the transformation + +.. math:: + + a \\rightarrow a_0 \\sinh^{-1} (a / a_0) + +For coordinate values close to zero (i.e. much smaller than +the "linear width" :math:`a_0`), this leaves values essentially unchanged: + +.. math:: + + a \\rightarrow a + \\mathcal{O}(a^3) + +but for larger values (i.e. :math:`|a| \\gg a_0`, this is asymptotically + +.. math:: + + a \\rightarrow a_0 \\, \\mathrm{sgn}(a) \\ln |a| + \\mathcal{O}(1) + +As with the `symlog <.scale.SymmetricalLogScale>` scaling, +this allows one to plot quantities +that cover a very wide dynamic range that includes both positive +and negative values. However, ``symlog`` involves a transformation +that has discontinuities in its gradient because it is built +from *separate* linear and logarithmic transformations. +The ``asinh`` scaling uses a transformation that is smooth +for all (finite) values, which is both mathematically cleaner +and reduces visual artifacts associated with an abrupt +transition between linear and logarithmic regions of the plot. + +.. note:: + `.scale.AsinhScale` is experimental, and the API may change. + +See `~.scale.AsinhScale`, `~.scale.SymmetricalLogScale`. +""" + +import numpy as np +import matplotlib.pyplot as plt + +# Prepare sample values for variations on y=x graph: +x = np.linspace(-3, 6, 500) + +############################################################################### +# Compare "symlog" and "asinh" behaviour on sample y=x graph, +# where there is a discontinuous gradient in "symlog" near y=2: +fig1 = plt.figure() +ax0, ax1 = fig1.subplots(1, 2, sharex=True) + +ax0.plot(x, x) +ax0.set_yscale('symlog') +ax0.grid() +ax0.set_title('symlog') + +ax1.plot(x, x) +ax1.set_yscale('asinh') +ax1.grid() +ax1.set_title('asinh') + + +############################################################################### +# Compare "asinh" graphs with different scale parameter "linear_width": +fig2 = plt.figure(constrained_layout=True) +axs = fig2.subplots(1, 3, sharex=True) +for ax, (a0, base) in zip(axs, ((0.2, 2), (1.0, 0), (5.0, 10))): + ax.set_title('linear_width={:.3g}'.format(a0)) + ax.plot(x, x, label='y=x') + ax.plot(x, 10*x, label='y=10x') + ax.plot(x, 100*x, label='y=100x') + ax.set_yscale('asinh', linear_width=a0, base=base) + ax.grid() + ax.legend(loc='best', fontsize='small') + + +############################################################################### +# Compare "symlog" and "asinh" scalings +# on 2D Cauchy-distributed random numbers, +# where one may be able to see more subtle artifacts near y=2 +# due to the gradient-discontinuity in "symlog": +fig3 = plt.figure() +ax = fig3.subplots(1, 1) +r = 3 * np.tan(np.random.uniform(-np.pi / 2.02, np.pi / 2.02, + size=(5000,))) +th = np.random.uniform(0, 2*np.pi, size=r.shape) + +ax.scatter(r * np.cos(th), r * np.sin(th), s=4, alpha=0.5) +ax.set_xscale('asinh') +ax.set_yscale('symlog') +ax.set_xlabel('asinh') +ax.set_ylabel('symlog') +ax.set_title('2D Cauchy random deviates') +ax.set_xlim(-50, 50) +ax.set_ylim(-50, 50) +ax.grid() + +plt.show() + +############################################################################### +# +# .. admonition:: References +# +# - `matplotlib.scale.AsinhScale` +# - `matplotlib.ticker.AsinhLocator` +# - `matplotlib.scale.SymmetricalLogScale` diff --git a/examples/scales/custom_scale.py b/examples/scales/custom_scale.py index ea2c2df00b34..bfeb8b99bd98 100644 --- a/examples/scales/custom_scale.py +++ b/examples/scales/custom_scale.py @@ -6,7 +6,7 @@ Create a custom scale, by implementing the scaling use for latitude data in a Mercator Projection. -Unless you are making special use of the `~.Transform` class, you probably +Unless you are making special use of the `.Transform` class, you probably don't need to use this verbose method, and instead can use `~.scale.FuncScale` and the ``'function'`` option of `~.Axes.set_xscale` and `~.Axes.set_yscale`. See the last example in :doc:`/gallery/scales/scales`. @@ -34,12 +34,12 @@ class MercatorLatitudeScale(mscale.ScaleBase): there is user-defined threshold, above and below which nothing will be plotted. This defaults to +/- 85 degrees. - __ http://en.wikipedia.org/wiki/Mercator_projection + __ https://en.wikipedia.org/wiki/Mercator_projection """ # The scale class must have a member ``name`` that defines the string used - # to select the scale. For example, ``gca().set_yscale("mercator")`` would - # be used to select this scale. + # to select the scale. For example, ``ax.set_yscale("mercator")`` would be + # used to select this scale. name = 'mercator' def __init__(self, axis, *, thresh=np.deg2rad(85), **kwargs): @@ -159,7 +159,7 @@ def inverted(self): s = np.radians(t)/2. plt.plot(t, s, '-', lw=2) - plt.gca().set_yscale('mercator') + plt.yscale('mercator') plt.xlabel('Longitude') plt.ylabel('Latitude') diff --git a/examples/scales/log_bar.py b/examples/scales/log_bar.py index bcbdee2a7f98..239069806c5c 100644 --- a/examples/scales/log_bar.py +++ b/examples/scales/log_bar.py @@ -20,8 +20,7 @@ y = [d[i] for d in data] b = ax.bar(x + i * dimw, y, dimw, bottom=0.001) -ax.set_xticks(x + dimw / 2) -ax.set_xticklabels(map(str, x)) +ax.set_xticks(x + dimw / 2, labels=map(str, x)) ax.set_yscale('log') ax.set_xlabel('x') diff --git a/examples/scales/power_norm.py b/examples/scales/power_norm.py index 60795fb5b524..9ab8454465f5 100644 --- a/examples/scales/power_norm.py +++ b/examples/scales/power_norm.py @@ -38,16 +38,12 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.colors -matplotlib.colors.PowerNorm -matplotlib.axes.Axes.hist2d -matplotlib.pyplot.hist2d +# - `matplotlib.colors` +# - `matplotlib.colors.PowerNorm` +# - `matplotlib.axes.Axes.hist2d` +# - `matplotlib.pyplot.hist2d` diff --git a/examples/scales/scales.py b/examples/scales/scales.py index d47d0281b33c..25ec82608f01 100644 --- a/examples/scales/scales.py +++ b/examples/scales/scales.py @@ -103,20 +103,16 @@ def inverse(a): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.set_yscale -matplotlib.axes.Axes.set_xscale -matplotlib.axis.Axis.set_major_locator -matplotlib.scale.LogitScale -matplotlib.scale.LogScale -matplotlib.scale.LinearScale -matplotlib.scale.SymmetricalLogScale -matplotlib.scale.FuncScale +# - `matplotlib.axes.Axes.set_xscale` +# - `matplotlib.axes.Axes.set_yscale` +# - `matplotlib.axis.Axis.set_major_locator` +# - `matplotlib.scale.LinearScale` +# - `matplotlib.scale.LogScale` +# - `matplotlib.scale.SymmetricalLogScale` +# - `matplotlib.scale.LogitScale` +# - `matplotlib.scale.FuncScale` diff --git a/examples/scales/log_test.py b/examples/scales/semilogx_demo.py similarity index 86% rename from examples/scales/log_test.py rename to examples/scales/semilogx_demo.py index 72c04d61aaae..00c708a03479 100644 --- a/examples/scales/log_test.py +++ b/examples/scales/semilogx_demo.py @@ -3,6 +3,8 @@ Log Axis ======== +.. redirect-from:: /gallery/scales/log_test + This is an example of assigning a log-scale for the x-axis using `~.axes.Axes.semilogx`. """ diff --git a/examples/scales/symlog_demo.py b/examples/scales/symlog_demo.py index e1c433b22b88..6c5f04ade8d6 100644 --- a/examples/scales/symlog_demo.py +++ b/examples/scales/symlog_demo.py @@ -33,3 +33,17 @@ fig.tight_layout() plt.show() + +############################################################################### +# It should be noted that the coordinate transform used by ``symlog`` +# has a discontinuous gradient at the transition between its linear +# and logarithmic regions. The ``asinh`` axis scale is an alternative +# technique that may avoid visual artifacts caused by these discontinuities. + +############################################################################### +# +# .. admonition:: References +# +# - `matplotlib.scale.SymmetricalLogScale` +# - `matplotlib.ticker.SymmetricalLogLocator` +# - `matplotlib.scale.AsinhScale` diff --git a/examples/shapes_and_collections/arrow_guide.py b/examples/shapes_and_collections/arrow_guide.py index 284944e95bb8..24151544ba17 100644 --- a/examples/shapes_and_collections/arrow_guide.py +++ b/examples/shapes_and_collections/arrow_guide.py @@ -23,13 +23,16 @@ 3. Entire patch fixed in data space Below each use case is presented in turn. + +.. redirect-from:: /gallery/text_labels_and_annotations/arrow_simple_demo """ + import matplotlib.patches as mpatches import matplotlib.pyplot as plt x_tail = 0.1 -y_tail = 0.1 +y_tail = 0.5 x_head = 0.9 -y_head = 0.9 +y_head = 0.8 dx = x_head - x_tail dy = y_head - y_tail @@ -54,8 +57,7 @@ arrow = mpatches.FancyArrowPatch((x_tail, y_tail), (x_head, y_head), mutation_scale=100) axs[1].add_patch(arrow) -axs[1].set_xlim(0, 2) -axs[1].set_ylim(0, 2) +axs[1].set(xlim=(0, 2), ylim=(0, 2)) ############################################################################### # Head shape and anchor points fixed in display space @@ -81,28 +83,42 @@ mutation_scale=100, transform=axs[1].transAxes) axs[1].add_patch(arrow) -axs[1].set_xlim(0, 2) -axs[1].set_ylim(0, 2) +axs[1].set(xlim=(0, 2), ylim=(0, 2)) ############################################################################### # Head shape and anchor points fixed in data space # ------------------------------------------------ # -# In this case we use `.patches.Arrow`. +# In this case we use `.patches.Arrow`, or `.patches.FancyArrow` (the latter is +# in orange). # # Note that when the axis limits are changed, the arrow shape and location # change. +# +# `.FancyArrow`'s API is relatively awkward, and requires in particular passing +# ``length_includes_head=True`` so that the arrow *tip* is ``(dx, dy)`` away +# from the arrow start. It is only included in this reference because it is +# the arrow class returned by `.Axes.arrow` (in green). fig, axs = plt.subplots(nrows=2) arrow = mpatches.Arrow(x_tail, y_tail, dx, dy) axs[0].add_patch(arrow) +arrow = mpatches.FancyArrow(x_tail, y_tail - .4, dx, dy, + width=.1, length_includes_head=True, color="C1") +axs[0].add_patch(arrow) +axs[0].arrow(x_tail + 1, y_tail - .4, dx, dy, + width=.1, length_includes_head=True, color="C2") arrow = mpatches.Arrow(x_tail, y_tail, dx, dy) axs[1].add_patch(arrow) -axs[1].set_xlim(0, 2) -axs[1].set_ylim(0, 2) +arrow = mpatches.FancyArrow(x_tail, y_tail - .4, dx, dy, + width=.1, length_includes_head=True, color="C1") +axs[1].add_patch(arrow) +axs[1].arrow(x_tail + 1, y_tail - .4, dx, dy, + width=.1, length_includes_head=True, color="C2") +axs[1].set(xlim=(0, 2), ylim=(0, 2)) ############################################################################### diff --git a/examples/shapes_and_collections/artist_reference.py b/examples/shapes_and_collections/artist_reference.py index d8e267c98d5e..054c1f1e088a 100644 --- a/examples/shapes_and_collections/artist_reference.py +++ b/examples/shapes_and_collections/artist_reference.py @@ -45,7 +45,7 @@ def label(xy, text): label(grid[2], "Wedge") # add a Polygon -polygon = mpatches.RegularPolygon(grid[3], 5, 0.1) +polygon = mpatches.RegularPolygon(grid[3], 5, radius=0.1) patches.append(polygon) label(grid[3], "Polygon") @@ -104,30 +104,26 @@ def label(xy, text): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.path -matplotlib.path.Path -matplotlib.lines -matplotlib.lines.Line2D -matplotlib.patches -matplotlib.patches.Circle -matplotlib.patches.Ellipse -matplotlib.patches.Wedge -matplotlib.patches.Rectangle -matplotlib.patches.Arrow -matplotlib.patches.PathPatch -matplotlib.patches.FancyBboxPatch -matplotlib.patches.RegularPolygon -matplotlib.collections -matplotlib.collections.PatchCollection -matplotlib.cm.ScalarMappable.set_array -matplotlib.axes.Axes.add_collection -matplotlib.axes.Axes.add_line +# - `matplotlib.path` +# - `matplotlib.path.Path` +# - `matplotlib.lines` +# - `matplotlib.lines.Line2D` +# - `matplotlib.patches` +# - `matplotlib.patches.Circle` +# - `matplotlib.patches.Ellipse` +# - `matplotlib.patches.Wedge` +# - `matplotlib.patches.Rectangle` +# - `matplotlib.patches.Arrow` +# - `matplotlib.patches.PathPatch` +# - `matplotlib.patches.FancyBboxPatch` +# - `matplotlib.patches.RegularPolygon` +# - `matplotlib.collections` +# - `matplotlib.collections.PatchCollection` +# - `matplotlib.cm.ScalarMappable.set_array` +# - `matplotlib.axes.Axes.add_collection` +# - `matplotlib.axes.Axes.add_line` diff --git a/examples/shapes_and_collections/collections.py b/examples/shapes_and_collections/collections.py index 8b3a38823a8e..161e2f1d28e5 100644 --- a/examples/shapes_and_collections/collections.py +++ b/examples/shapes_and_collections/collections.py @@ -3,11 +3,10 @@ Line, Poly and RegularPoly Collection with autoscaling ========================================================= -For the first two subplots, we will use spirals. Their -size will be set in plot units, not data units. Their positions -will be set in data units by using the "offsets" and "transOffset" -kwargs of the `~.collections.LineCollection` and -`~.collections.PolyCollection`. +For the first two subplots, we will use spirals. Their size will be set in +plot units, not data units. Their positions will be set in data units by using +the *offsets* and *offset_transform* keyword arguments of the `.LineCollection` +and `.PolyCollection`. The third subplot will make regular polygons, with the same type of scaling and positioning as in the first two. @@ -47,8 +46,8 @@ hspace=0.3, wspace=0.3) -col = collections.LineCollection([spiral], offsets=xyo, - transOffset=ax1.transData) +col = collections.LineCollection( + [spiral], offsets=xyo, offset_transform=ax1.transData) trans = fig.dpi_scale_trans + transforms.Affine2D().scale(1.0/72.0) col.set_transform(trans) # the points to pixels transform # Note: the first argument to the collection initializer @@ -60,7 +59,7 @@ # but it is good enough to generate a plot that you can use # as a starting point. If you know beforehand the range of # x and y that you want to show, it is better to set them -# explicitly, leave out the autolim kwarg (or set it to False), +# explicitly, leave out the *autolim* keyword argument (or set it to False), # and omit the 'ax1.autoscale_view()' call below. # Make a transform for the line segments such that their size is @@ -72,8 +71,8 @@ # The same data as above, but fill the curves. -col = collections.PolyCollection([spiral], offsets=xyo, - transOffset=ax2.transData) +col = collections.PolyCollection( + [spiral], offsets=xyo, offset_transform=ax2.transData) trans = transforms.Affine2D().scale(fig.dpi/72.0) col.set_transform(trans) # the points to pixels transform ax2.add_collection(col, autolim=True) @@ -86,7 +85,7 @@ # 7-sided regular polygons col = collections.RegularPolyCollection( - 7, sizes=np.abs(xx) * 10.0, offsets=xyo, transOffset=ax3.transData) + 7, sizes=np.abs(xx) * 10.0, offsets=xyo, offset_transform=ax3.transData) trans = transforms.Affine2D().scale(fig.dpi / 72.0) col.set_transform(trans) # the points to pixels transform ax3.add_collection(col, autolim=True) @@ -127,20 +126,16 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.figure.Figure -matplotlib.collections -matplotlib.collections.LineCollection -matplotlib.collections.RegularPolyCollection -matplotlib.axes.Axes.add_collection -matplotlib.axes.Axes.autoscale_view -matplotlib.transforms.Affine2D -matplotlib.transforms.Affine2D.scale +# - `matplotlib.figure.Figure` +# - `matplotlib.collections` +# - `matplotlib.collections.LineCollection` +# - `matplotlib.collections.RegularPolyCollection` +# - `matplotlib.axes.Axes.add_collection` +# - `matplotlib.axes.Axes.autoscale_view` +# - `matplotlib.transforms.Affine2D` +# - `matplotlib.transforms.Affine2D.scale` diff --git a/examples/shapes_and_collections/compound_path.py b/examples/shapes_and_collections/compound_path.py index ceff94400686..d721eaf1c392 100644 --- a/examples/shapes_and_collections/compound_path.py +++ b/examples/shapes_and_collections/compound_path.py @@ -23,7 +23,7 @@ path = Path(vertices, codes) -pathpatch = PathPatch(path, facecolor='None', edgecolor='green') +pathpatch = PathPatch(path, facecolor='none', edgecolor='green') fig, ax = plt.subplots() ax.add_patch(pathpatch) @@ -35,18 +35,14 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.path -matplotlib.path.Path -matplotlib.patches -matplotlib.patches.PathPatch -matplotlib.axes.Axes.add_patch -matplotlib.axes.Axes.autoscale_view +# - `matplotlib.path` +# - `matplotlib.path.Path` +# - `matplotlib.patches` +# - `matplotlib.patches.PathPatch` +# - `matplotlib.axes.Axes.add_patch` +# - `matplotlib.axes.Axes.autoscale_view` diff --git a/examples/shapes_and_collections/dolphin.py b/examples/shapes_and_collections/dolphin.py index 5f606a3425c0..325b55a96f7b 100644 --- a/examples/shapes_and_collections/dolphin.py +++ b/examples/shapes_and_collections/dolphin.py @@ -105,21 +105,17 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.path -matplotlib.path.Path -matplotlib.patches -matplotlib.patches.PathPatch -matplotlib.patches.Circle -matplotlib.axes.Axes.add_patch -matplotlib.transforms -matplotlib.transforms.Affine2D -matplotlib.transforms.Affine2D.rotate_deg +# - `matplotlib.path` +# - `matplotlib.path.Path` +# - `matplotlib.patches` +# - `matplotlib.patches.PathPatch` +# - `matplotlib.patches.Circle` +# - `matplotlib.axes.Axes.add_patch` +# - `matplotlib.transforms` +# - `matplotlib.transforms.Affine2D` +# - `matplotlib.transforms.Affine2D.rotate_deg` diff --git a/examples/shapes_and_collections/donut.py b/examples/shapes_and_collections/donut.py index 9afa26c85e6f..d4c308e75fae 100644 --- a/examples/shapes_and_collections/donut.py +++ b/examples/shapes_and_collections/donut.py @@ -64,23 +64,19 @@ def make_circle(r): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.path -matplotlib.path.Path -matplotlib.patches -matplotlib.patches.PathPatch -matplotlib.patches.Circle -matplotlib.axes.Axes.add_patch -matplotlib.axes.Axes.annotate -matplotlib.axes.Axes.set_aspect -matplotlib.axes.Axes.set_xlim -matplotlib.axes.Axes.set_ylim -matplotlib.axes.Axes.set_title +# - `matplotlib.path` +# - `matplotlib.path.Path` +# - `matplotlib.patches` +# - `matplotlib.patches.PathPatch` +# - `matplotlib.patches.Circle` +# - `matplotlib.axes.Axes.add_patch` +# - `matplotlib.axes.Axes.annotate` +# - `matplotlib.axes.Axes.set_aspect` +# - `matplotlib.axes.Axes.set_xlim` +# - `matplotlib.axes.Axes.set_ylim` +# - `matplotlib.axes.Axes.set_title` diff --git a/examples/shapes_and_collections/ellipse_collection.py b/examples/shapes_and_collections/ellipse_collection.py index 9b7a71f55643..7a92eff580ee 100644 --- a/examples/shapes_and_collections/ellipse_collection.py +++ b/examples/shapes_and_collections/ellipse_collection.py @@ -7,6 +7,7 @@ a `~.collections.EllipseCollection` or `~.collections.PathCollection`, the use of an `~.collections.EllipseCollection` allows for much shorter code. """ + import matplotlib.pyplot as plt import numpy as np from matplotlib.collections import EllipseCollection @@ -25,7 +26,7 @@ fig, ax = plt.subplots() ec = EllipseCollection(ww, hh, aa, units='x', offsets=XY, - transOffset=ax.transData) + offset_transform=ax.transData) ec.set_array((X + Y).ravel()) ax.add_collection(ec) ax.autoscale_view() @@ -37,17 +38,13 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.collections -matplotlib.collections.EllipseCollection -matplotlib.axes.Axes.add_collection -matplotlib.axes.Axes.autoscale_view -matplotlib.cm.ScalarMappable.set_array +# - `matplotlib.collections` +# - `matplotlib.collections.EllipseCollection` +# - `matplotlib.axes.Axes.add_collection` +# - `matplotlib.axes.Axes.autoscale_view` +# - `matplotlib.cm.ScalarMappable.set_array` diff --git a/examples/shapes_and_collections/ellipse_demo.py b/examples/shapes_and_collections/ellipse_demo.py index 8b03e845bdbb..8d2615af7717 100644 --- a/examples/shapes_and_collections/ellipse_demo.py +++ b/examples/shapes_and_collections/ellipse_demo.py @@ -42,10 +42,6 @@ # Draw many ellipses with different angles. # -import matplotlib.pyplot as plt -import numpy as np -from matplotlib.patches import Ellipse - angle_step = 45 # degrees angles = np.arange(0, 180, angle_step) @@ -62,18 +58,14 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.patches -matplotlib.patches.Ellipse -matplotlib.axes.Axes.add_artist -matplotlib.artist.Artist.set_clip_box -matplotlib.artist.Artist.set_alpha -matplotlib.patches.Patch.set_facecolor +# - `matplotlib.patches` +# - `matplotlib.patches.Ellipse` +# - `matplotlib.axes.Axes.add_artist` +# - `matplotlib.artist.Artist.set_clip_box` +# - `matplotlib.artist.Artist.set_alpha` +# - `matplotlib.patches.Patch.set_facecolor` diff --git a/examples/shapes_and_collections/fancybox_demo.py b/examples/shapes_and_collections/fancybox_demo.py index 22772a50983d..ea1383d01941 100644 --- a/examples/shapes_and_collections/fancybox_demo.py +++ b/examples/shapes_and_collections/fancybox_demo.py @@ -6,6 +6,8 @@ The following examples show how to plot boxes with different visual properties. """ +import inspect + import matplotlib.pyplot as plt import matplotlib.transforms as mtransforms import matplotlib.patches as mpatch @@ -15,17 +17,26 @@ # First we'll show some sample boxes with fancybox. styles = mpatch.BoxStyle.get_styles() -spacing = 1.2 - -figheight = (spacing * len(styles) + .5) -fig = plt.figure(figsize=(4 / 1.5, figheight / 1.5)) -fontsize = 0.3 * 72 - -for i, stylename in enumerate(sorted(styles)): - fig.text(0.5, (spacing * (len(styles) - i) - 0.5) / figheight, stylename, - ha="center", - size=fontsize, - bbox=dict(boxstyle=stylename, fc="w", ec="k")) +ncol = 2 +nrow = (len(styles) + 1) // ncol +axs = (plt.figure(figsize=(3 * ncol, 1 + nrow)) + .add_gridspec(1 + nrow, ncol, wspace=.5).subplots()) +for ax in axs.flat: + ax.set_axis_off() +for ax in axs[0, :]: + ax.text(.2, .5, "boxstyle", + transform=ax.transAxes, size="large", color="tab:blue", + horizontalalignment="right", verticalalignment="center") + ax.text(.4, .5, "default parameters", + transform=ax.transAxes, + horizontalalignment="left", verticalalignment="center") +for ax, (stylename, stylecls) in zip(axs[1:, :].T.flat, styles.items()): + ax.text(.2, .5, stylename, bbox=dict(boxstyle=stylename, fc="w", ec="k"), + transform=ax.transAxes, size="large", color="tab:blue", + horizontalalignment="right", verticalalignment="center") + ax.text(.4, .5, str(inspect.signature(stylecls))[1:-1].replace(", ", "\n"), + transform=ax.transAxes, + horizontalalignment="left", verticalalignment="center") ############################################################################### @@ -33,7 +44,7 @@ def add_fancy_patch_around(ax, bb, **kwargs): - fancy = FancyBboxPatch((bb.xmin, bb.ymin), bb.width, bb.height, + fancy = FancyBboxPatch(bb.p0, bb.width, bb.height, fc=(1, 0.8, 1, 0.5), ec=(1, 0.5, 1, 0.5), **kwargs) ax.add_patch(fancy) @@ -101,19 +112,15 @@ def draw_control_points_for_patches(ax): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.patches -matplotlib.patches.FancyBboxPatch -matplotlib.patches.BoxStyle -matplotlib.patches.BoxStyle.get_styles -matplotlib.transforms.Bbox -matplotlib.figure.Figure.text -matplotlib.axes.Axes.text +# - `matplotlib.patches` +# - `matplotlib.patches.FancyBboxPatch` +# - `matplotlib.patches.BoxStyle` +# - ``matplotlib.patches.BoxStyle.get_styles`` +# - `matplotlib.transforms.Bbox` +# - `matplotlib.figure.Figure.text` +# - `matplotlib.axes.Axes.text` diff --git a/examples/shapes_and_collections/hatch_demo.py b/examples/shapes_and_collections/hatch_demo.py index c537181abe41..00b3200a2268 100644 --- a/examples/shapes_and_collections/hatch_demo.py +++ b/examples/shapes_and_collections/hatch_demo.py @@ -47,19 +47,14 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.patches -matplotlib.patches.Ellipse -matplotlib.patches.Polygon -matplotlib.axes.Axes.add_patch -matplotlib.patches.Patch.set_hatch -matplotlib.axes.Axes.bar -matplotlib.pyplot.bar +# - `matplotlib.patches` +# - `matplotlib.patches.Ellipse` +# - `matplotlib.patches.Polygon` +# - `matplotlib.axes.Axes.add_patch` +# - `matplotlib.patches.Patch.set_hatch` +# - `matplotlib.axes.Axes.bar` / `matplotlib.pyplot.bar` diff --git a/examples/shapes_and_collections/hatch_style_reference.py b/examples/shapes_and_collections/hatch_style_reference.py index a364d893dcaa..5fa13163ff33 100644 --- a/examples/shapes_and_collections/hatch_style_reference.py +++ b/examples/shapes_and_collections/hatch_style_reference.py @@ -52,16 +52,12 @@ def hatches_plot(ax, h): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.patches -matplotlib.patches.Rectangle -matplotlib.axes.Axes.add_patch -matplotlib.axes.Axes.text +# - `matplotlib.patches` +# - `matplotlib.patches.Rectangle` +# - `matplotlib.axes.Axes.add_patch` +# - `matplotlib.axes.Axes.text` diff --git a/examples/shapes_and_collections/line_collection.py b/examples/shapes_and_collections/line_collection.py index 61e4c5317e59..9adfd8024e5b 100644 --- a/examples/shapes_and_collections/line_collection.py +++ b/examples/shapes_and_collections/line_collection.py @@ -86,19 +86,14 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.collections -matplotlib.collections.LineCollection -matplotlib.cm.ScalarMappable.set_array -matplotlib.axes.Axes.add_collection -matplotlib.figure.Figure.colorbar -matplotlib.pyplot.colorbar -matplotlib.pyplot.sci +# - `matplotlib.collections` +# - `matplotlib.collections.LineCollection` +# - `matplotlib.cm.ScalarMappable.set_array` +# - `matplotlib.axes.Axes.add_collection` +# - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar` +# - `matplotlib.pyplot.sci` diff --git a/examples/shapes_and_collections/marker_path.py b/examples/shapes_and_collections/marker_path.py deleted file mode 100644 index 7d43df365b32..000000000000 --- a/examples/shapes_and_collections/marker_path.py +++ /dev/null @@ -1,41 +0,0 @@ -""" -=========== -Marker Path -=========== - -Using a `~.path.Path` as marker for a `~.axes.Axes.plot`. -""" -import matplotlib.pyplot as plt -import matplotlib.path as mpath -import numpy as np - - -star = mpath.Path.unit_regular_star(6) -circle = mpath.Path.unit_circle() -# concatenate the circle with an internal cutout of the star -verts = np.concatenate([circle.vertices, star.vertices[::-1, ...]]) -codes = np.concatenate([circle.codes, star.codes]) -cut_star = mpath.Path(verts, codes) - - -plt.plot(np.arange(10)**2, '--r', marker=cut_star, markersize=15) - -plt.show() - -############################################################################# -# -# ------------ -# -# References -# """""""""" -# -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.path -matplotlib.path.Path -matplotlib.path.Path.unit_regular_star -matplotlib.path.Path.unit_circle -matplotlib.axes.Axes.plot -matplotlib.pyplot.plot diff --git a/examples/shapes_and_collections/patch_collection.py b/examples/shapes_and_collections/patch_collection.py index 33e137edb6a1..cdd9f4687619 100644 --- a/examples/shapes_and_collections/patch_collection.py +++ b/examples/shapes_and_collections/patch_collection.py @@ -45,7 +45,7 @@ ] for i in range(N): - polygon = Polygon(np.random.rand(N, 2), True) + polygon = Polygon(np.random.rand(N, 2), closed=True) patches.append(polygon) colors = 100 * np.random.rand(len(patches)) @@ -58,20 +58,16 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.patches -matplotlib.patches.Circle -matplotlib.patches.Wedge -matplotlib.patches.Polygon -matplotlib.collections.PatchCollection -matplotlib.collections.Collection.set_array -matplotlib.axes.Axes.add_collection -matplotlib.figure.Figure.colorbar +# - `matplotlib.patches` +# - `matplotlib.patches.Circle` +# - `matplotlib.patches.Wedge` +# - `matplotlib.patches.Polygon` +# - `matplotlib.collections.PatchCollection` +# - `matplotlib.collections.Collection.set_array` +# - `matplotlib.axes.Axes.add_collection` +# - `matplotlib.figure.Figure.colorbar` diff --git a/examples/shapes_and_collections/path_patch.py b/examples/shapes_and_collections/path_patch.py index 8ad132f9cb73..08f1a9457957 100644 --- a/examples/shapes_and_collections/path_patch.py +++ b/examples/shapes_and_collections/path_patch.py @@ -40,17 +40,13 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.path -matplotlib.path.Path -matplotlib.patches -matplotlib.patches.PathPatch -matplotlib.axes.Axes.add_patch +# - `matplotlib.path` +# - `matplotlib.path.Path` +# - `matplotlib.patches` +# - `matplotlib.patches.PathPatch` +# - `matplotlib.axes.Axes.add_patch` diff --git a/examples/shapes_and_collections/quad_bezier.py b/examples/shapes_and_collections/quad_bezier.py index 0aacd26c55f4..0059d627126d 100644 --- a/examples/shapes_and_collections/quad_bezier.py +++ b/examples/shapes_and_collections/quad_bezier.py @@ -27,17 +27,13 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.path -matplotlib.path.Path -matplotlib.patches -matplotlib.patches.PathPatch -matplotlib.axes.Axes.add_patch +# - `matplotlib.path` +# - `matplotlib.path.Path` +# - `matplotlib.patches` +# - `matplotlib.patches.PathPatch` +# - `matplotlib.axes.Axes.add_patch` diff --git a/examples/shapes_and_collections/scatter.py b/examples/shapes_and_collections/scatter.py index 673766a6ea4f..f5ecd8f10139 100644 --- a/examples/shapes_and_collections/scatter.py +++ b/examples/shapes_and_collections/scatter.py @@ -23,13 +23,9 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: -import matplotlib - -matplotlib.axes.Axes.scatter -matplotlib.pyplot.scatter +# - `matplotlib.axes.Axes.scatter` / `matplotlib.pyplot.scatter` diff --git a/examples/showcase/anatomy.py b/examples/showcase/anatomy.py index 96afa7f5d408..f53aa2bb9d9e 100644 --- a/examples/showcase/anatomy.py +++ b/examples/showcase/anatomy.py @@ -6,10 +6,18 @@ This figure shows the name of several matplotlib elements composing a figure """ + import numpy as np import matplotlib.pyplot as plt +from matplotlib.patches import Circle +from matplotlib.patheffects import withStroke from matplotlib.ticker import AutoMinorLocator, MultipleLocator +royal_blue = [0, 20/256, 82/256] + + +# make the figure + np.random.seed(19680801) X = np.linspace(0.5, 3.5, 100) @@ -17,149 +25,96 @@ Y2 = 1+np.cos(1+X/0.75)/2 Y3 = np.random.uniform(Y1, Y2, len(X)) -fig = plt.figure(figsize=(8, 8)) -ax = fig.add_subplot(1, 1, 1, aspect=1) - - -def minor_tick(x, pos): - if not x % 1.0: - return "" - return f"{x:.2f}" +fig = plt.figure(figsize=(7.5, 7.5)) +ax = fig.add_axes([0.2, 0.17, 0.68, 0.7], aspect=1) ax.xaxis.set_major_locator(MultipleLocator(1.000)) ax.xaxis.set_minor_locator(AutoMinorLocator(4)) ax.yaxis.set_major_locator(MultipleLocator(1.000)) ax.yaxis.set_minor_locator(AutoMinorLocator(4)) -# FuncFormatter is created and used automatically -ax.xaxis.set_minor_formatter(minor_tick) +ax.xaxis.set_minor_formatter("{x:.2f}") ax.set_xlim(0, 4) ax.set_ylim(0, 4) -ax.tick_params(which='major', width=1.0) -ax.tick_params(which='major', length=10) -ax.tick_params(which='minor', width=1.0, labelsize=10) -ax.tick_params(which='minor', length=5, labelsize=10, labelcolor='0.25') +ax.tick_params(which='major', width=1.0, length=10, labelsize=14) +ax.tick_params(which='minor', width=1.0, length=5, labelsize=10, + labelcolor='0.25') ax.grid(linestyle="--", linewidth=0.5, color='.25', zorder=-10) -ax.plot(X, Y1, c=(0.25, 0.25, 1.00), lw=2, label="Blue signal", zorder=10) -ax.plot(X, Y2, c=(1.00, 0.25, 0.25), lw=2, label="Red signal") -ax.plot(X, Y3, linewidth=0, - marker='o', markerfacecolor='w', markeredgecolor='k') +ax.plot(X, Y1, c='C0', lw=2.5, label="Blue signal", zorder=10) +ax.plot(X, Y2, c='C1', lw=2.5, label="Orange signal") +ax.plot(X[::3], Y3[::3], linewidth=0, markersize=9, + marker='s', markerfacecolor='none', markeredgecolor='C4', + markeredgewidth=2.5) ax.set_title("Anatomy of a figure", fontsize=20, verticalalignment='bottom') -ax.set_xlabel("X axis label") -ax.set_ylabel("Y axis label") - -ax.legend() - - -def circle(x, y, radius=0.15): - from matplotlib.patches import Circle - from matplotlib.patheffects import withStroke - circle = Circle((x, y), radius, clip_on=False, zorder=10, linewidth=1, - edgecolor='black', facecolor=(0, 0, 0, .0125), - path_effects=[withStroke(linewidth=5, foreground='w')]) - ax.add_artist(circle) - - -def text(x, y, text): - ax.text(x, y, text, backgroundcolor="white", - ha='center', va='top', weight='bold', color='blue') - - -# Minor tick -circle(0.50, -0.10) -text(0.50, -0.32, "Minor tick label") - -# Major tick -circle(-0.03, 4.00) -text(0.03, 3.80, "Major tick") - -# Minor tick -circle(0.00, 3.50) -text(0.00, 3.30, "Minor tick") - -# Major tick label -circle(-0.15, 3.00) -text(-0.15, 2.80, "Major tick label") - -# X Label -circle(1.80, -0.27) -text(1.80, -0.45, "X axis label") - -# Y Label -circle(-0.27, 1.80) -text(-0.27, 1.6, "Y axis label") - -# Title -circle(1.60, 4.13) -text(1.60, 3.93, "Title") - -# Blue plot -circle(1.75, 2.80) -text(1.75, 2.60, "Line\n(line plot)") - -# Red plot -circle(1.20, 0.60) -text(1.20, 0.40, "Line\n(line plot)") - -# Scatter plot -circle(3.20, 1.75) -text(3.20, 1.55, "Markers\n(scatter plot)") - -# Grid -circle(3.00, 3.00) -text(3.00, 2.80, "Grid") - -# Legend -circle(3.70, 3.80) -text(3.70, 3.60, "Legend") - -# Axes -circle(0.5, 0.5) -text(0.5, 0.3, "Axes") - -# Figure -circle(-0.3, 0.65) -text(-0.3, 0.45, "Figure") - -color = 'blue' -ax.annotate('Spines', xy=(4.0, 0.35), xytext=(3.3, 0.5), - weight='bold', color=color, - arrowprops=dict(arrowstyle='->', - connectionstyle="arc3", - color=color)) - -ax.annotate('', xy=(3.15, 0.0), xytext=(3.45, 0.45), - weight='bold', color=color, - arrowprops=dict(arrowstyle='->', - connectionstyle="arc3", - color=color)) - -ax.text(4.0, -0.4, "Made with https://matplotlib.org", - fontsize=10, ha="right", color='.5') - +ax.set_xlabel("x Axis label", fontsize=14) +ax.set_ylabel("y Axis label", fontsize=14) +ax.legend(loc="upper right", fontsize=14) + + +# Annotate the figure + +def annotate(x, y, text, code): + # Circle marker + c = Circle((x, y), radius=0.15, clip_on=False, zorder=10, linewidth=2.5, + edgecolor=royal_blue + [0.6], facecolor='none', + path_effects=[withStroke(linewidth=7, foreground='white')]) + ax.add_artist(c) + + # use path_effects as a background for the texts + # draw the path_effects and the colored text separately so that the + # path_effects cannot clip other texts + for path_effects in [[withStroke(linewidth=7, foreground='white')], []]: + color = 'white' if path_effects else royal_blue + ax.text(x, y-0.2, text, zorder=100, + ha='center', va='top', weight='bold', color=color, + style='italic', fontfamily='Courier New', + path_effects=path_effects) + + color = 'white' if path_effects else 'black' + ax.text(x, y-0.33, code, zorder=100, + ha='center', va='top', weight='normal', color=color, + fontfamily='monospace', fontsize='medium', + path_effects=path_effects) + + +annotate(3.5, -0.13, "Minor tick label", "ax.xaxis.set_minor_formatter") +annotate(-0.03, 1.0, "Major tick", "ax.yaxis.set_major_locator") +annotate(0.00, 3.75, "Minor tick", "ax.yaxis.set_minor_locator") +annotate(-0.15, 3.00, "Major tick label", "ax.yaxis.set_major_formatter") +annotate(1.68, -0.39, "xlabel", "ax.set_xlabel") +annotate(-0.38, 1.67, "ylabel", "ax.set_ylabel") +annotate(1.52, 4.15, "Title", "ax.set_title") +annotate(1.75, 2.80, "Line", "ax.plot") +annotate(2.25, 1.54, "Markers", "ax.scatter") +annotate(3.00, 3.00, "Grid", "ax.grid") +annotate(3.60, 3.58, "Legend", "ax.legend") +annotate(2.5, 0.55, "Axes", "fig.subplots") +annotate(4, 4.5, "Figure", "plt.figure") +annotate(0.65, 0.01, "x Axis", "ax.xaxis") +annotate(0, 0.36, "y Axis", "ax.yaxis") +annotate(4.0, 0.7, "Spine", "ax.spines") + +# frame around figure +fig.patch.set(linewidth=4, edgecolor='0.5') plt.show() ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.pyplot.figure -matplotlib.axes.Axes.text -matplotlib.axis.Axis.set_minor_formatter -matplotlib.axis.Axis.set_major_locator -matplotlib.axis.Axis.set_minor_locator -matplotlib.patches.Circle -matplotlib.patheffects.withStroke -matplotlib.ticker.FuncFormatter +# - `matplotlib.pyplot.figure` +# - `matplotlib.axes.Axes.text` +# - `matplotlib.axis.Axis.set_minor_formatter` +# - `matplotlib.axis.Axis.set_major_locator` +# - `matplotlib.axis.Axis.set_minor_locator` +# - `matplotlib.patches.Circle` +# - `matplotlib.patheffects.withStroke` +# - `matplotlib.ticker.FuncFormatter` diff --git a/examples/showcase/bachelors_degrees_by_gender.py b/examples/showcase/bachelors_degrees_by_gender.py deleted file mode 100644 index 079ba399634c..000000000000 --- a/examples/showcase/bachelors_degrees_by_gender.py +++ /dev/null @@ -1,132 +0,0 @@ -""" -============================ -Bachelor's degrees by gender -============================ - -A graph of multiple time series which demonstrates extensive custom -styling of plot frame, tick lines and labels, and line graph properties. - -Also demonstrates the custom placement of text labels along the right edge -as an alternative to a conventional legend. -""" - -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.cbook import get_sample_data - - -fname = get_sample_data('percent_bachelors_degrees_women_usa.csv', - asfileobj=False) -gender_degree_data = np.genfromtxt(fname, delimiter=',', names=True) - -# You typically want your plot to be ~1.33x wider than tall. This plot -# is a rare exception because of the number of lines being plotted on it. -# Common sizes: (10, 7.5) and (12, 9) -fig, ax = plt.subplots(1, 1, figsize=(12, 14)) - -# These are the colors that will be used in the plot -ax.set_prop_cycle(color=[ - '#1f77b4', '#aec7e8', '#ff7f0e', '#ffbb78', '#2ca02c', '#98df8a', - '#d62728', '#ff9896', '#9467bd', '#c5b0d5', '#8c564b', '#c49c94', - '#e377c2', '#f7b6d2', '#7f7f7f', '#c7c7c7', '#bcbd22', '#dbdb8d', - '#17becf', '#9edae5']) - -# Remove the plot frame lines. They are unnecessary here. -ax.spines[:].set_visible(False) - -# Ensure that the axis ticks only show up on the bottom and left of the plot. -# Ticks on the right and top of the plot are generally unnecessary. -ax.xaxis.tick_bottom() -ax.yaxis.tick_left() - -fig.subplots_adjust(left=.06, right=.75, bottom=.02, top=.94) -# Limit the range of the plot to only where the data is. -# Avoid unnecessary whitespace. -ax.set_xlim(1969.5, 2011.1) -ax.set_ylim(-0.25, 90) - -# Set a fixed location and format for ticks. -ax.set_xticks(range(1970, 2011, 10)) -ax.set_yticks(range(0, 91, 10)) -# Use automatic StrMethodFormatter creation -ax.xaxis.set_major_formatter('{x:.0f}') -ax.yaxis.set_major_formatter('{x:.0f}%') - -# Provide tick lines across the plot to help your viewers trace along -# the axis ticks. Make sure that the lines are light and small so they -# don't obscure the primary data lines. -ax.grid(True, 'major', 'y', ls='--', lw=.5, c='k', alpha=.3) - -# Remove the tick marks; they are unnecessary with the tick lines we just -# plotted. Make sure your axis ticks are large enough to be easily read. -# You don't want your viewers squinting to read your plot. -ax.tick_params(axis='both', which='both', labelsize=14, - bottom=False, top=False, labelbottom=True, - left=False, right=False, labelleft=True) - -# Now that the plot is prepared, it's time to actually plot the data! -# Note that I plotted the majors in order of the highest % in the final year. -majors = ['Health Professions', 'Public Administration', 'Education', - 'Psychology', 'Foreign Languages', 'English', - 'Communications\nand Journalism', 'Art and Performance', 'Biology', - 'Agriculture', 'Social Sciences and History', 'Business', - 'Math and Statistics', 'Architecture', 'Physical Sciences', - 'Computer Science', 'Engineering'] - -y_offsets = {'Foreign Languages': 0.5, 'English': -0.5, - 'Communications\nand Journalism': 0.75, - 'Art and Performance': -0.25, 'Agriculture': 1.25, - 'Social Sciences and History': 0.25, 'Business': -0.75, - 'Math and Statistics': 0.75, 'Architecture': -0.75, - 'Computer Science': 0.75, 'Engineering': -0.25} - -for column in majors: - # Plot each line separately with its own color. - column_rec_name = column.replace('\n', '_').replace(' ', '_') - - line, = ax.plot('Year', column_rec_name, data=gender_degree_data, - lw=2.5) - - # Add a text label to the right end of every line. Most of the code below - # is adding specific offsets y position because some labels overlapped. - y_pos = gender_degree_data[column_rec_name][-1] - 0.5 - - if column in y_offsets: - y_pos += y_offsets[column] - - # Again, make sure that all labels are large enough to be easily read - # by the viewer. - ax.text(2011.5, y_pos, column, fontsize=14, color=line.get_color()) - -# Make the title big enough so it spans the entire plot, but don't make it -# so big that it requires two lines to show. - -# Note that if the title is descriptive enough, it is unnecessary to include -# axis labels; they are self-evident, in this plot's case. -fig.suptitle("Percentage of Bachelor's degrees conferred to women in " - "the U.S.A. by major (1970-2011)", fontsize=18, ha="center") - -# Finally, save the figure as a PNG. -# You can also save it as a PDF, JPEG, etc. -# Just change the file extension in this call. -# fig.savefig('percent-bachelors-degrees-women-usa.png', bbox_inches='tight') -plt.show() - -############################################################################# -# -# ------------ -# -# References -# """""""""" -# -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.pyplot.subplots -matplotlib.axes.Axes.text -matplotlib.axis.Axis.set_major_formatter -matplotlib.axis.XAxis.tick_bottom -matplotlib.axis.YAxis.tick_left -matplotlib.artist.Artist.set_visible -matplotlib.ticker.StrMethodFormatter diff --git a/examples/showcase/firefox.py b/examples/showcase/firefox.py index 713809b8292d..69025d39eba1 100644 --- a/examples/showcase/firefox.py +++ b/examples/showcase/firefox.py @@ -12,8 +12,8 @@ from matplotlib.path import Path import matplotlib.patches as patches -# From: http://raphaeljs.com/icons/#firefox -firefox = "M28.4,22.469c0.479-0.964,0.851-1.991,1.095-3.066c0.953-3.661,0.666-6.854,0.666-6.854l-0.327,2.104c0,0-0.469-3.896-1.044-5.353c-0.881-2.231-1.273-2.214-1.274-2.21c0.542,1.379,0.494,2.169,0.483,2.288c-0.01-0.016-0.019-0.032-0.027-0.047c-0.131-0.324-0.797-1.819-2.225-2.878c-2.502-2.481-5.943-4.014-9.745-4.015c-4.056,0-7.705,1.745-10.238,4.525C5.444,6.5,5.183,5.938,5.159,5.317c0,0-0.002,0.002-0.006,0.005c0-0.011-0.003-0.021-0.003-0.031c0,0-1.61,1.247-1.436,4.612c-0.299,0.574-0.56,1.172-0.777,1.791c-0.375,0.817-0.75,2.004-1.059,3.746c0,0,0.133-0.422,0.399-0.988c-0.064,0.482-0.103,0.971-0.116,1.467c-0.09,0.845-0.118,1.865-0.039,3.088c0,0,0.032-0.406,0.136-1.021c0.834,6.854,6.667,12.165,13.743,12.165l0,0c1.86,0,3.636-0.37,5.256-1.036C24.938,27.771,27.116,25.196,28.4,22.469zM16.002,3.356c2.446,0,4.73,0.68,6.68,1.86c-2.274-0.528-3.433-0.261-3.423-0.248c0.013,0.015,3.384,0.589,3.981,1.411c0,0-1.431,0-2.856,0.41c-0.065,0.019,5.242,0.663,6.327,5.966c0,0-0.582-1.213-1.301-1.42c0.473,1.439,0.351,4.17-0.1,5.528c-0.058,0.174-0.118-0.755-1.004-1.155c0.284,2.037-0.018,5.268-1.432,6.158c-0.109,0.07,0.887-3.189,0.201-1.93c-4.093,6.276-8.959,2.539-10.934,1.208c1.585,0.388,3.267,0.108,4.242-0.559c0.982-0.672,1.564-1.162,2.087-1.047c0.522,0.117,0.87-0.407,0.464-0.872c-0.405-0.466-1.392-1.105-2.725-0.757c-0.94,0.247-2.107,1.287-3.886,0.233c-1.518-0.899-1.507-1.63-1.507-2.095c0-0.366,0.257-0.88,0.734-1.028c0.58,0.062,1.044,0.214,1.537,0.466c0.005-0.135,0.006-0.315-0.001-0.519c0.039-0.077,0.015-0.311-0.047-0.596c-0.036-0.287-0.097-0.582-0.19-0.851c0.01-0.002,0.017-0.007,0.021-0.021c0.076-0.344,2.147-1.544,2.299-1.659c0.153-0.114,0.55-0.378,0.506-1.183c-0.015-0.265-0.058-0.294-2.232-0.286c-0.917,0.003-1.425-0.894-1.589-1.245c0.222-1.231,0.863-2.11,1.919-2.704c0.02-0.011,0.015-0.021-0.008-0.027c0.219-0.127-2.524-0.006-3.76,1.604C9.674,8.045,9.219,7.95,8.71,7.95c-0.638,0-1.139,0.07-1.603,0.187c-0.05,0.013-0.122,0.011-0.208-0.001C6.769,8.04,6.575,7.88,6.365,7.672c0.161-0.18,0.324-0.356,0.495-0.526C9.201,4.804,12.43,3.357,16.002,3.356z" +# From: https://dmitrybaranovskiy.github.io/raphael/icons/#firefox +firefox = "M28.4,22.469c0.479-0.964,0.851-1.991,1.095-3.066c0.953-3.661,0.666-6.854,0.666-6.854l-0.327,2.104c0,0-0.469-3.896-1.044-5.353c-0.881-2.231-1.273-2.214-1.274-2.21c0.542,1.379,0.494,2.169,0.483,2.288c-0.01-0.016-0.019-0.032-0.027-0.047c-0.131-0.324-0.797-1.819-2.225-2.878c-2.502-2.481-5.943-4.014-9.745-4.015c-4.056,0-7.705,1.745-10.238,4.525C5.444,6.5,5.183,5.938,5.159,5.317c0,0-0.002,0.002-0.006,0.005c0-0.011-0.003-0.021-0.003-0.031c0,0-1.61,1.247-1.436,4.612c-0.299,0.574-0.56,1.172-0.777,1.791c-0.375,0.817-0.75,2.004-1.059,3.746c0,0,0.133-0.422,0.399-0.988c-0.064,0.482-0.103,0.971-0.116,1.467c-0.09,0.845-0.118,1.865-0.039,3.088c0,0,0.032-0.406,0.136-1.021c0.834,6.854,6.667,12.165,13.743,12.165l0,0c1.86,0,3.636-0.37,5.256-1.036C24.938,27.771,27.116,25.196,28.4,22.469zM16.002,3.356c2.446,0,4.73,0.68,6.68,1.86c-2.274-0.528-3.433-0.261-3.423-0.248c0.013,0.015,3.384,0.589,3.981,1.411c0,0-1.431,0-2.856,0.41c-0.065,0.019,5.242,0.663,6.327,5.966c0,0-0.582-1.213-1.301-1.42c0.473,1.439,0.351,4.17-0.1,5.528c-0.058,0.174-0.118-0.755-1.004-1.155c0.284,2.037-0.018,5.268-1.432,6.158c-0.109,0.07,0.887-3.189,0.201-1.93c-4.093,6.276-8.959,2.539-10.934,1.208c1.585,0.388,3.267,0.108,4.242-0.559c0.982-0.672,1.564-1.162,2.087-1.047c0.522,0.117,0.87-0.407,0.464-0.872c-0.405-0.466-1.392-1.105-2.725-0.757c-0.94,0.247-2.107,1.287-3.886,0.233c-1.518-0.899-1.507-1.63-1.507-2.095c0-0.366,0.257-0.88,0.734-1.028c0.58,0.062,1.044,0.214,1.537,0.466c0.005-0.135,0.006-0.315-0.001-0.519c0.039-0.077,0.015-0.311-0.047-0.596c-0.036-0.287-0.097-0.582-0.19-0.851c0.01-0.002,0.017-0.007,0.021-0.021c0.076-0.344,2.147-1.544,2.299-1.659c0.153-0.114,0.55-0.378,0.506-1.183c-0.015-0.265-0.058-0.294-2.232-0.286c-0.917,0.003-1.425-0.894-1.589-1.245c0.222-1.231,0.863-2.11,1.919-2.704c0.02-0.011,0.015-0.021-0.008-0.027c0.219-0.127-2.524-0.006-3.76,1.604C9.674,8.045,9.219,7.95,8.71,7.95c-0.638,0-1.139,0.07-1.603,0.187c-0.05,0.013-0.122,0.011-0.208-0.001C6.769,8.04,6.575,7.88,6.365,7.672c0.161-0.18,0.324-0.356,0.495-0.526C9.201,4.804,12.43,3.357,16.002,3.356z" # noqa def svg_parse(path): diff --git a/examples/showcase/integral.py b/examples/showcase/integral.py index e0d07fec3de2..c75ef940b0db 100644 --- a/examples/showcase/integral.py +++ b/examples/showcase/integral.py @@ -46,8 +46,7 @@ def func(x): ax.spines.top.set_visible(False) ax.xaxis.set_ticks_position('bottom') -ax.set_xticks((a, b)) -ax.set_xticklabels(('$a$', '$b$')) +ax.set_xticks([a, b], labels=['$a$', '$b$']) ax.set_yticks([]) plt.show() diff --git a/examples/showcase/stock_prices.py b/examples/showcase/stock_prices.py new file mode 100644 index 000000000000..7f7ffba08f03 --- /dev/null +++ b/examples/showcase/stock_prices.py @@ -0,0 +1,119 @@ +""" +========================== +Stock prices over 32 years +========================== + +.. redirect-from:: /gallery/showcase/bachelors_degrees_by_gender + +A graph of multiple time series that demonstrates custom styling of plot frame, +tick lines, tick labels, and line graph properties. It also uses custom +placement of text labels along the right edge as an alternative to a +conventional legend. + +Note: The third-party mpl style dufte_ produces similar-looking plots with less +code. + +.. _dufte: https://github.com/nschloe/dufte +""" + +import numpy as np +import matplotlib.transforms as mtransforms +import matplotlib.pyplot as plt +from matplotlib.cbook import get_sample_data + + +def convertdate(x): + return np.datetime64(x, 'D') + + +fname = get_sample_data('Stocks.csv', asfileobj=False) +stock_data = np.genfromtxt(fname, encoding='utf-8', delimiter=',', + names=True, dtype=None, converters={0: convertdate}, + skip_header=1) + + +fig, ax = plt.subplots(1, 1, figsize=(6, 8), layout='constrained') + +# These are the colors that will be used in the plot +ax.set_prop_cycle(color=[ + '#1f77b4', '#aec7e8', '#ff7f0e', '#ffbb78', '#2ca02c', '#98df8a', + '#d62728', '#ff9896', '#9467bd', '#c5b0d5', '#8c564b', '#c49c94', + '#e377c2', '#f7b6d2', '#7f7f7f', '#c7c7c7', '#bcbd22', '#dbdb8d', + '#17becf', '#9edae5']) + +stocks_name = ['IBM', 'Apple', 'Microsoft', 'Xerox', 'Amazon', 'Dell', + 'Alphabet', 'Adobe', 'S&P 500', 'NASDAQ'] +stocks_ticker = ['IBM', 'AAPL', 'MSFT', 'XRX', 'AMZN', 'DELL', 'GOOGL', + 'ADBE', 'GSPC', 'IXIC'] + +# Manually adjust the label positions vertically (units are points = 1/72 inch) +y_offsets = {k: 0 for k in stocks_ticker} +y_offsets['IBM'] = 5 +y_offsets['AAPL'] = -5 +y_offsets['AMZN'] = -6 + +for nn, column in enumerate(stocks_ticker): + # Plot each line separately with its own color. + # don't include any data with NaN. + good = np.nonzero(np.isfinite(stock_data[column])) + line, = ax.plot(stock_data['Date'][good], stock_data[column][good], lw=2.5) + + # Add a text label to the right end of every line. Most of the code below + # is adding specific offsets y position because some labels overlapped. + y_pos = stock_data[column][-1] + + # Use an offset transform, in points, for any text that needs to be nudged + # up or down. + offset = y_offsets[column] / 72 + trans = mtransforms.ScaledTranslation(0, offset, fig.dpi_scale_trans) + trans = ax.transData + trans + + # Again, make sure that all labels are large enough to be easily read + # by the viewer. + ax.text(np.datetime64('2022-10-01'), y_pos, stocks_name[nn], + color=line.get_color(), transform=trans) + +ax.set_xlim(np.datetime64('1989-06-01'), np.datetime64('2023-01-01')) + +fig.suptitle("Technology company stocks prices dollars (1990-2022)", + ha="center") + +# Remove the plot frame lines. They are unnecessary here. +ax.spines[:].set_visible(False) + +# Ensure that the axis ticks only show up on the bottom and left of the plot. +# Ticks on the right and top of the plot are generally unnecessary. +ax.xaxis.tick_bottom() +ax.yaxis.tick_left() +ax.set_yscale('log') + +# Provide tick lines across the plot to help your viewers trace along +# the axis ticks. Make sure that the lines are light and small so they +# don't obscure the primary data lines. +ax.grid(True, 'major', 'both', ls='--', lw=.5, c='k', alpha=.3) + +# Remove the tick marks; they are unnecessary with the tick lines we just +# plotted. Make sure your axis ticks are large enough to be easily read. +# You don't want your viewers squinting to read your plot. +ax.tick_params(axis='both', which='both', labelsize='large', + bottom=False, top=False, labelbottom=True, + left=False, right=False, labelleft=True) + +# Finally, save the figure as a PNG. +# You can also save it as a PDF, JPEG, etc. +# Just change the file extension in this call. +# fig.savefig('stock-prices.png', bbox_inches='tight') +plt.show() + +############################################################################# +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.pyplot.subplots` +# - `matplotlib.axes.Axes.text` +# - `matplotlib.axis.XAxis.tick_bottom` +# - `matplotlib.axis.YAxis.tick_left` +# - `matplotlib.artist.Artist.set_visible` diff --git a/examples/specialty_plots/anscombe.py b/examples/specialty_plots/anscombe.py index 93390907f7b3..0d45ca350229 100644 --- a/examples/specialty_plots/anscombe.py +++ b/examples/specialty_plots/anscombe.py @@ -55,15 +55,11 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.axline -matplotlib.axes.Axes.text -matplotlib.axes.Axes.tick_params +# - `matplotlib.axes.Axes.axline` / `matplotlib.pyplot.axline` +# - `matplotlib.axes.Axes.text` / `matplotlib.pyplot.text` +# - `matplotlib.axes.Axes.tick_params` / matplotlib.pyplot.tick_params` diff --git a/examples/specialty_plots/leftventricle_bulleye.py b/examples/specialty_plots/leftventricle_bulleye.py index 3f32dafd2b08..b3e1ecbfd25c 100644 --- a/examples/specialty_plots/leftventricle_bulleye.py +++ b/examples/specialty_plots/leftventricle_bulleye.py @@ -56,6 +56,9 @@ def bullseye_plot(ax, data, seg_bold=None, cmap=None, norm=None): theta = np.linspace(0, 2 * np.pi, 768) r = np.linspace(0.2, 1, 4) + # Remove grid + ax.grid(False) + # Create the bound for the segment 17 for i in range(r.shape[0]): ax.plot(theta, np.repeat(r[i], theta.shape), '-k', lw=linewidth) @@ -171,8 +174,6 @@ def bullseye_plot(ax, data, seg_bold=None, cmap=None, norm=None): norm3 = mpl.colors.BoundaryNorm(bounds, cmap3.N) fig.colorbar(mpl.cm.ScalarMappable(cmap=cmap3, norm=norm3), cax=axl3, - # to use 'extend', you must specify two extra boundaries: - boundaries=[0] + bounds + [18], extend='both', ticks=bounds, # optional spacing='proportional', diff --git a/examples/specialty_plots/mri_with_eeg.py b/examples/specialty_plots/mri_with_eeg.py index 0f622bf42a67..8f269e9e92ec 100644 --- a/examples/specialty_plots/mri_with_eeg.py +++ b/examples/specialty_plots/mri_with_eeg.py @@ -1,6 +1,6 @@ """ ============ -MRI With EEG +MRI with EEG ============ Displays a set of subplots with an MRI image, its intensity @@ -64,12 +64,11 @@ offsets = np.zeros((n_rows, 2), dtype=float) offsets[:, 1] = ticklocs -lines = LineCollection(segs, offsets=offsets, transOffset=None) +lines = LineCollection(segs, offsets=offsets, offset_transform=None) ax2.add_collection(lines) # Set the yticks to use axes coordinates on the y axis -ax2.set_yticks(ticklocs) -ax2.set_yticklabels(['PG3', 'PG5', 'PG7', 'PG9']) +ax2.set_yticks(ticklocs, labels=['PG3', 'PG5', 'PG7', 'PG9']) ax2.set_xlabel('Time (s)') diff --git a/examples/specialty_plots/radar_chart.py b/examples/specialty_plots/radar_chart.py index 42358c766d01..21519137df9a 100644 --- a/examples/specialty_plots/radar_chart.py +++ b/examples/specialty_plots/radar_chart.py @@ -11,7 +11,7 @@ matplotlib.axis to the desired number of vertices, but the orientation of the polygon is not aligned with the radial axes. -.. [1] http://en.wikipedia.org/wiki/Radar_chart +.. [1] https://en.wikipedia.org/wiki/Radar_chart """ import numpy as np @@ -42,11 +42,20 @@ def radar_factory(num_vars, frame='circle'): # calculate evenly-spaced axis angles theta = np.linspace(0, 2*np.pi, num_vars, endpoint=False) + class RadarTransform(PolarAxes.PolarTransform): + + def transform_path_non_affine(self, path): + # Paths with non-unit interpolation steps correspond to gridlines, + # in which case we force interpolation (to defeat PolarTransform's + # autoconversion to circular arcs). + if path._interpolation_steps > 1: + path = path.interpolated(num_vars) + return Path(self.transform(path.vertices), path.codes) + class RadarAxes(PolarAxes): name = 'radar' - # use 1 line segment to connect specified points - RESOLUTION = 1 + PolarTransform = RadarTransform def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -122,7 +131,7 @@ def example_data(): # Organic Carbon fraction 1 (OC) # Organic Carbon fraction 2 (OC2) # Organic Carbon fraction 3 (OC3) - # Pyrolized Organic Carbon (OP) + # Pyrolyzed Organic Carbon (OP) # 2)Inclusion of gas-phase specie carbon monoxide (CO) # 3)Inclusion of gas-phase specie ozone (O3). # 4)Inclusion of both gas-phase species is present... @@ -175,7 +184,7 @@ def example_data(): horizontalalignment='center', verticalalignment='center') for d, color in zip(case_data, colors): ax.plot(theta, d, color=color) - ax.fill(theta, d, facecolor=color, alpha=0.25) + ax.fill(theta, d, facecolor=color, alpha=0.25, label='_nolegend_') ax.set_varlabels(spoke_labels) # add legend relative to top-left plot @@ -192,20 +201,16 @@ def example_data(): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.path -matplotlib.path.Path -matplotlib.spines -matplotlib.spines.Spine -matplotlib.projections -matplotlib.projections.polar -matplotlib.projections.polar.PolarAxes -matplotlib.projections.register_projection +# - `matplotlib.path` +# - `matplotlib.path.Path` +# - `matplotlib.spines` +# - `matplotlib.spines.Spine` +# - `matplotlib.projections` +# - `matplotlib.projections.polar` +# - `matplotlib.projections.polar.PolarAxes` +# - `matplotlib.projections.register_projection` diff --git a/examples/specialty_plots/sankey_basics.py b/examples/specialty_plots/sankey_basics.py index 82d94c0d169b..3bb0cc91cb52 100644 --- a/examples/specialty_plots/sankey_basics.py +++ b/examples/specialty_plots/sankey_basics.py @@ -103,16 +103,12 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.sankey -matplotlib.sankey.Sankey -matplotlib.sankey.Sankey.add -matplotlib.sankey.Sankey.finish +# - `matplotlib.sankey` +# - `matplotlib.sankey.Sankey` +# - `matplotlib.sankey.Sankey.add` +# - `matplotlib.sankey.Sankey.finish` diff --git a/examples/specialty_plots/sankey_links.py b/examples/specialty_plots/sankey_links.py index 61dfc06d41b0..851111e8d693 100644 --- a/examples/specialty_plots/sankey_links.py +++ b/examples/specialty_plots/sankey_links.py @@ -58,16 +58,12 @@ def corner(sankey): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.sankey -matplotlib.sankey.Sankey -matplotlib.sankey.Sankey.add -matplotlib.sankey.Sankey.finish +# - `matplotlib.sankey` +# - `matplotlib.sankey.Sankey` +# - `matplotlib.sankey.Sankey.add` +# - `matplotlib.sankey.Sankey.finish` diff --git a/examples/specialty_plots/sankey_rankine.py b/examples/specialty_plots/sankey_rankine.py index 379d2d65f7ba..6313ba3acdcf 100644 --- a/examples/specialty_plots/sankey_rankine.py +++ b/examples/specialty_plots/sankey_rankine.py @@ -85,16 +85,12 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.sankey -matplotlib.sankey.Sankey -matplotlib.sankey.Sankey.add -matplotlib.sankey.Sankey.finish +# - `matplotlib.sankey` +# - `matplotlib.sankey.Sankey` +# - `matplotlib.sankey.Sankey.add` +# - `matplotlib.sankey.Sankey.finish` diff --git a/examples/specialty_plots/skewt.py b/examples/specialty_plots/skewt.py index e6023fef4b5b..4cf3f1905e5c 100644 --- a/examples/specialty_plots/skewt.py +++ b/examples/specialty_plots/skewt.py @@ -261,18 +261,14 @@ def upper_xlim(self): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.transforms -matplotlib.spines -matplotlib.spines.Spine -matplotlib.spines.Spine.register_axis -matplotlib.projections -matplotlib.projections.register_projection +# - `matplotlib.transforms` +# - `matplotlib.spines` +# - `matplotlib.spines.Spine` +# - `matplotlib.spines.Spine.register_axis` +# - `matplotlib.projections` +# - `matplotlib.projections.register_projection` diff --git a/examples/specialty_plots/topographic_hillshading.py b/examples/specialty_plots/topographic_hillshading.py index 1b97a8a57808..f76aecd094d3 100644 --- a/examples/specialty_plots/topographic_hillshading.py +++ b/examples/specialty_plots/topographic_hillshading.py @@ -13,8 +13,8 @@ In most cases, hillshading is used purely for visual purposes, and *dx*/*dy* can be safely ignored. In that case, you can tweak *vert_exag* (vertical exaggeration) by trial and error to give the desired visual effect. However, -this example demonstrates how to use the *dx* and *dy* kwargs to ensure that -the *vert_exag* parameter is the true vertical exaggeration. +this example demonstrates how to use the *dx* and *dy* keyword arguments to +ensure that the *vert_exag* parameter is the true vertical exaggeration. """ import numpy as np import matplotlib.pyplot as plt @@ -24,7 +24,7 @@ dem = get_sample_data('jacksboro_fault_dem.npz', np_load=True) z = dem['elevation'] -#-- Optional dx and dy for accurate vertical exaggeration --------------------- +# -- Optional dx and dy for accurate vertical exaggeration -------------------- # If you need topographically accurate vertical exaggeration, or you don't want # to guess at what *vert_exag* should be, you'll need to specify the cellsize # of the grid (i.e. the *dx* and *dy* parameters). Otherwise, any *vert_exag* @@ -36,7 +36,7 @@ dx, dy = dem['dx'], dem['dy'] dy = 111200 * dy dx = 111200 * dx * np.cos(np.radians(dem['ymin'])) -#------------------------------------------------------------------------------ +# ----------------------------------------------------------------------------- # Shade from the northwest, with the sun 45 degrees from horizontal ls = LightSource(azdeg=315, altdeg=45) diff --git a/examples/spines/README.txt b/examples/spines/README.txt new file mode 100644 index 000000000000..40bc3952eacd --- /dev/null +++ b/examples/spines/README.txt @@ -0,0 +1,4 @@ +.. _spines_examples: + +Spines +====== diff --git a/examples/ticks_and_spines/centered_spines_with_arrows.py b/examples/spines/centered_spines_with_arrows.py similarity index 100% rename from examples/ticks_and_spines/centered_spines_with_arrows.py rename to examples/spines/centered_spines_with_arrows.py diff --git a/examples/ticks_and_spines/multiple_yaxis_with_spines.py b/examples/spines/multiple_yaxis_with_spines.py similarity index 100% rename from examples/ticks_and_spines/multiple_yaxis_with_spines.py rename to examples/spines/multiple_yaxis_with_spines.py diff --git a/examples/ticks_and_spines/spine_placement_demo.py b/examples/spines/spine_placement_demo.py similarity index 97% rename from examples/ticks_and_spines/spine_placement_demo.py rename to examples/spines/spine_placement_demo.py index e567d8160d9c..d433236657f9 100644 --- a/examples/ticks_and_spines/spine_placement_demo.py +++ b/examples/spines/spine_placement_demo.py @@ -6,7 +6,7 @@ Adjusting the location and appearance of axis spines. Note: If you want to obtain arrow heads at the ends of the axes, also check -out the :doc:`/gallery/ticks_and_spines/centered_spines_with_arrows` example. +out the :doc:`/gallery/spines/centered_spines_with_arrows` example. """ import numpy as np import matplotlib.pyplot as plt diff --git a/examples/ticks_and_spines/spines.py b/examples/spines/spines.py similarity index 64% rename from examples/ticks_and_spines/spines.py rename to examples/spines/spines.py index 08e3700d1387..d21947d33636 100644 --- a/examples/ticks_and_spines/spines.py +++ b/examples/spines/spines.py @@ -5,9 +5,12 @@ This demo compares: -- normal axes, with spines on all four sides; -- an axes with spines only on the left and bottom; -- an axes using custom bounds to limit the extent of the spine. +- normal Axes, with spines on all four sides; +- an Axes with spines only on the left and bottom; +- an Axes using custom bounds to limit the extent of the spine. + +Each `.axes.Axes` has a list of `.Spine` objects, accessible +via the container ``ax.spines``. """ import numpy as np import matplotlib.pyplot as plt @@ -44,3 +47,12 @@ ax2.xaxis.set_ticks_position('bottom') plt.show() + +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.Spines.set_visible` +# - `matplotlib.Spines.set_bounds` +# - `matplotlib.axis.set_ticks_position` diff --git a/examples/ticks_and_spines/spines_bounds.py b/examples/spines/spines_bounds.py similarity index 90% rename from examples/ticks_and_spines/spines_bounds.py rename to examples/spines/spines_bounds.py index 96205a43d38e..5ccf3051e1ea 100644 --- a/examples/ticks_and_spines/spines_bounds.py +++ b/examples/spines/spines_bounds.py @@ -21,8 +21,7 @@ # set ticks and tick labels ax.set_xlim((0, 2*np.pi)) -ax.set_xticks([0, np.pi, 2*np.pi]) -ax.set_xticklabels(['0', r'$\pi$', r'2$\pi$']) +ax.set_xticks([0, np.pi, 2*np.pi], labels=['0', r'$\pi$', r'2$\pi$']) ax.set_ylim((-1.5, 1.5)) ax.set_yticks([-1, 0, 1]) diff --git a/examples/ticks_and_spines/spines_dropped.py b/examples/spines/spines_dropped.py similarity index 100% rename from examples/ticks_and_spines/spines_dropped.py rename to examples/spines/spines_dropped.py diff --git a/examples/statistics/barchart_demo.py b/examples/statistics/barchart_demo.py index e9c2b94c0b95..18819ca35b08 100644 --- a/examples/statistics/barchart_demo.py +++ b/examples/statistics/barchart_demo.py @@ -15,24 +15,16 @@ just make up some data for little Johnny Doe. """ +from collections import namedtuple import numpy as np -import matplotlib import matplotlib.pyplot as plt -from matplotlib.ticker import MaxNLocator -from collections import namedtuple -np.random.seed(42) Student = namedtuple('Student', ['name', 'grade', 'gender']) -Score = namedtuple('Score', ['score', 'percentile']) +Score = namedtuple('Score', ['value', 'unit', 'percentile']) -# GLOBAL CONSTANTS -test_names = ['Pacer Test', 'Flexed Arm\n Hang', 'Mile Run', 'Agility', - 'Push Ups'] -test_units = dict(zip(test_names, ['laps', 'sec', 'min:sec', 'sec', ''])) - -def attach_ordinal(num): +def to_ordinal(num): """Convert an integer to an ordinal string, e.g. 2 -> '2nd'.""" suffixes = {str(i): v for i, v in enumerate(['th', 'st', 'nd', 'rd', 'th', @@ -44,133 +36,76 @@ def attach_ordinal(num): return v + suffixes[v[-1]] -def format_score(score, test): +def format_score(score): """ Create score labels for the right y-axis as the test name followed by the measurement unit (if any), split over two lines. """ - unit = test_units[test] - if unit: - return f'{score}\n{unit}' - else: # If no unit, don't include a newline, so that label stays centered. - return score - + return f'{score.value}\n{score.unit}' if score.unit else str(score.value) -def format_ycursor(y): - y = int(y) - if y < 0 or y >= len(test_names): - return '' - else: - return test_names[y] - -def plot_student_results(student, scores, cohort_size): - fig, ax1 = plt.subplots(figsize=(9, 7)) # Create the figure - fig.subplots_adjust(left=0.115, right=0.88) +def plot_student_results(student, scores_by_test, cohort_size): + fig, ax1 = plt.subplots(figsize=(9, 7), constrained_layout=True) fig.canvas.manager.set_window_title('Eldorado K-8 Fitness Chart') - pos = np.arange(len(test_names)) - - rects = ax1.barh(pos, [scores[k].percentile for k in test_names], - align='center', - height=0.5, - tick_label=test_names) - ax1.set_title(student.name) + ax1.set_xlabel( + 'Percentile Ranking Across {grade} Grade {gender}s\n' + 'Cohort Size: {cohort_size}'.format( + grade=to_ordinal(student.grade), + gender=student.gender.title(), + cohort_size=cohort_size)) + + test_names = list(scores_by_test.keys()) + percentiles = [score.percentile for score in scores_by_test.values()] + + rects = ax1.barh(test_names, percentiles, align='center', height=0.5) + # Partition the percentile values to be able to draw large numbers in + # white within the bar, and small numbers in black outside the bar. + large_percentiles = [to_ordinal(p) if p > 40 else '' for p in percentiles] + small_percentiles = [to_ordinal(p) if p <= 40 else '' for p in percentiles] + ax1.bar_label(rects, small_percentiles, + padding=5, color='black', fontweight='bold') + ax1.bar_label(rects, large_percentiles, + padding=-32, color='white', fontweight='bold') ax1.set_xlim([0, 100]) - ax1.xaxis.set_major_locator(MaxNLocator(11)) + ax1.set_xticks([0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]) ax1.xaxis.grid(True, linestyle='--', which='major', color='grey', alpha=.25) - - # Plot a solid vertical gridline to highlight the median position - ax1.axvline(50, color='grey', alpha=0.25) + ax1.axvline(50, color='grey', alpha=0.25) # median position # Set the right-hand Y-axis ticks and labels ax2 = ax1.twinx() - - # Set the tick locations - ax2.set_yticks(pos) # Set equal limits on both yaxis so that the ticks line up ax2.set_ylim(ax1.get_ylim()) - - # Set the tick labels - ax2.set_yticklabels([format_score(scores[k].score, k) for k in test_names]) + # Set the tick locations and labels + ax2.set_yticks( + np.arange(len(scores_by_test)), + labels=[format_score(score) for score in scores_by_test.values()]) ax2.set_ylabel('Test Scores') - xlabel = ('Percentile Ranking Across {grade} Grade {gender}s\n' - 'Cohort Size: {cohort_size}') - ax1.set_xlabel(xlabel.format(grade=attach_ordinal(student.grade), - gender=student.gender.title(), - cohort_size=cohort_size)) - - rect_labels = [] - # Lastly, write in the ranking inside each bar to aid in interpretation - for rect in rects: - # Rectangle widths are already integer-valued but are floating - # type, so it helps to remove the trailing decimal point and 0 by - # converting width to int type - width = int(rect.get_width()) - - rank_str = attach_ordinal(width) - # The bars aren't wide enough to print the ranking inside - if width < 40: - # Shift the text to the right side of the right edge - xloc = 5 - # Black against white background - clr = 'black' - align = 'left' - else: - # Shift the text to the left side of the right edge - xloc = -5 - # White on magenta - clr = 'white' - align = 'right' - - # Center the text vertically in the bar - yloc = rect.get_y() + rect.get_height() / 2 - label = ax1.annotate( - rank_str, xy=(width, yloc), xytext=(xloc, 0), - textcoords="offset points", - horizontalalignment=align, verticalalignment='center', - color=clr, weight='bold', clip_on=True) - rect_labels.append(label) - - # Make the interactive mouse over give the bar title - ax2.fmt_ydata = format_ycursor - # Return all of the artists created - return {'fig': fig, - 'ax': ax1, - 'ax_right': ax2, - 'bars': rects, - 'perc_labels': rect_labels} - - -student = Student('Johnny Doe', 2, 'boy') -scores = dict(zip( - test_names, - (Score(v, p) for v, p in - zip(['7', '48', '12:52', '17', '14'], - np.round(np.random.uniform(0, 100, len(test_names)), 0))))) -cohort_size = 62 # The number of other 2nd grade boys - -arts = plot_student_results(student, scores, cohort_size) -plt.show() +student = Student(name='Johnny Doe', grade=2, gender='Boy') +scores_by_test = { + 'Pacer Test': Score(7, 'laps', percentile=37), + 'Flexed Arm\n Hang': Score(48, 'sec', percentile=95), + 'Mile Run': Score('12:52', 'min:sec', percentile=73), + 'Agility': Score(17, 'sec', percentile=60), + 'Push Ups': Score(14, '', percentile=16), +} + +plot_student_results(student, scores_by_test, cohort_size=62) +plt.show() ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown -# in this example: - -matplotlib.axes.Axes.bar -matplotlib.pyplot.bar -matplotlib.axes.Axes.annotate -matplotlib.pyplot.annotate -matplotlib.axes.Axes.twinx +# - `matplotlib.axes.Axes.bar` / `matplotlib.pyplot.bar` +# - `matplotlib.axes.Axes.bar_label` / `matplotlib.pyplot.bar_label` +# - `matplotlib.axes.Axes.twinx` / `matplotlib.pyplot.twinx` diff --git a/examples/statistics/boxplot.py b/examples/statistics/boxplot.py index 9c5d7dd29186..fa8500cfc42f 100644 --- a/examples/statistics/boxplot.py +++ b/examples/statistics/boxplot.py @@ -3,16 +3,15 @@ Artist customization in box plots ================================= -This example demonstrates how to use the various kwargs -to fully customize box plots. The first figure demonstrates -how to remove and add individual components (note that the -mean is the only value not shown by default). The second -figure demonstrates how the styles of the artists can -be customized. It also demonstrates how to set the limit -of the whiskers to specific percentiles (lower right axes) +This example demonstrates how to use the various keyword arguments to fully +customize box plots. The first figure demonstrates how to remove and add +individual components (note that the mean is the only value not shown by +default). The second figure demonstrates how the styles of the artists can be +customized. It also demonstrates how to set the limit of the whiskers to +specific percentiles (lower right axes) -A good general reference on boxplots and their history can be found -here: http://vita.had.co.nz/papers/boxplots.pdf +A good general reference on boxplots and their history can be found here: +https://vita.had.co.nz/papers/boxplots.pdf """ @@ -98,13 +97,9 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.boxplot -matplotlib.pyplot.boxplot +# - `matplotlib.axes.Axes.boxplot` / `matplotlib.pyplot.boxplot` diff --git a/examples/statistics/boxplot_color.py b/examples/statistics/boxplot_color.py index c1c00c607590..c26b6672f567 100644 --- a/examples/statistics/boxplot_color.py +++ b/examples/statistics/boxplot_color.py @@ -54,13 +54,9 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.boxplot -matplotlib.pyplot.boxplot +# - `matplotlib.axes.Axes.boxplot` / `matplotlib.pyplot.boxplot` diff --git a/examples/statistics/boxplot_demo.py b/examples/statistics/boxplot_demo.py index d5eb08f9cf1d..252ca25bd58d 100644 --- a/examples/statistics/boxplot_demo.py +++ b/examples/statistics/boxplot_demo.py @@ -8,6 +8,8 @@ The following examples show off how to visualize boxplots with Matplotlib. There are many options to control their appearance and the statistics that they use to summarize the data. + +.. redirect-from:: /gallery/pyplots/boxplot_demo_pyplot """ import matplotlib.pyplot as plt @@ -105,7 +107,7 @@ fig.canvas.manager.set_window_title('A Boxplot Example') fig.subplots_adjust(left=0.075, right=0.95, top=0.9, bottom=0.25) -bp = ax1.boxplot(data, notch=0, sym='+', vert=1, whis=1.5) +bp = ax1.boxplot(data, notch=False, sym='+', vert=True, whis=1.5) plt.setp(bp['boxes'], color='black') plt.setp(bp['whiskers'], color='black') plt.setp(bp['fliers'], color='red', marker='+') @@ -221,7 +223,7 @@ def fake_bootstrapper(n): fig, ax = plt.subplots() pos = np.arange(len(treatments)) + 1 bp = ax.boxplot(treatments, sym='k+', positions=pos, - notch=1, bootstrap=5000, + notch=True, bootstrap=5000, usermedians=medians, conf_intervals=conf_intervals) @@ -231,16 +233,24 @@ def fake_bootstrapper(n): plt.setp(bp['fliers'], markersize=3.0) plt.show() + +############################################################################### +# Here we customize the widths of the caps . + +x = np.linspace(-7, 7, 140) +x = np.hstack([-25, x, 25]) +fig, ax = plt.subplots() + +ax.boxplot([x, x], notch=True, capwidths=[0.01, 0.2]) + +plt.show() + ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.boxplot -matplotlib.pyplot.boxplot -matplotlib.axes.Axes.set +# - `matplotlib.axes.Axes.boxplot` / `matplotlib.pyplot.boxplot` +# - `matplotlib.artist.Artist.set` / `matplotlib.pyplot.setp` diff --git a/examples/statistics/boxplot_vs_violin.py b/examples/statistics/boxplot_vs_violin.py index 5107a45f0dcb..f1162a1ef7a7 100644 --- a/examples/statistics/boxplot_vs_violin.py +++ b/examples/statistics/boxplot_vs_violin.py @@ -45,26 +45,19 @@ # adding horizontal grid lines for ax in axs: ax.yaxis.grid(True) - ax.set_xticks([y + 1 for y in range(len(all_data))]) + ax.set_xticks([y + 1 for y in range(len(all_data))], + labels=['x1', 'x2', 'x3', 'x4']) ax.set_xlabel('Four separate samples') ax.set_ylabel('Observed values') -# add x-tick labels -plt.setp(axs, xticks=[y + 1 for y in range(len(all_data))], - xticklabels=['x1', 'x2', 'x3', 'x4']) plt.show() ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.boxplot -matplotlib.pyplot.boxplot -matplotlib.axes.Axes.violinplot -matplotlib.pyplot.violinplot +# - `matplotlib.axes.Axes.boxplot` / `matplotlib.pyplot.boxplot` +# - `matplotlib.axes.Axes.violinplot` / `matplotlib.pyplot.violinplot` diff --git a/examples/statistics/bxp.py b/examples/statistics/bxp.py index b5e22be0e0d2..374db300b5bd 100644 --- a/examples/statistics/bxp.py +++ b/examples/statistics/bxp.py @@ -105,13 +105,10 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.bxp -matplotlib.cbook.boxplot_stats +# - `matplotlib.axes.Axes.bxp` +# - `matplotlib.cbook.boxplot_stats` diff --git a/examples/statistics/confidence_ellipse.py b/examples/statistics/confidence_ellipse.py index 06d5523f522e..c67da152ad7d 100644 --- a/examples/statistics/confidence_ellipse.py +++ b/examples/statistics/confidence_ellipse.py @@ -34,9 +34,9 @@ # # The radiuses of the ellipse can be controlled by n_std which is the number # of standard deviations. The default value is 3 which makes the ellipse -# enclose 99.4% of the points if the data is normally distributed +# enclose 98.9% of the points if the data is normally distributed # like in these examples (3 standard deviations in 1-D contain 99.7% -# of the data, which is 99.4% of the data in 2-D). +# of the data, which is 98.9% of the data in 2-D). def confidence_ellipse(x, y, ax, n_std=3.0, facecolor='none', **kwargs): @@ -67,19 +67,19 @@ def confidence_ellipse(x, y, ax, n_std=3.0, facecolor='none', **kwargs): cov = np.cov(x, y) pearson = cov[0, 1]/np.sqrt(cov[0, 0] * cov[1, 1]) # Using a special case to obtain the eigenvalues of this - # two-dimensionl dataset. + # two-dimensional dataset. ell_radius_x = np.sqrt(1 + pearson) ell_radius_y = np.sqrt(1 - pearson) ellipse = Ellipse((0, 0), width=ell_radius_x * 2, height=ell_radius_y * 2, facecolor=facecolor, **kwargs) - # Calculating the stdandard deviation of x from + # Calculating the standard deviation of x from # the squareroot of the variance and multiplying # with the given number of standard deviations. scale_x = np.sqrt(cov[0, 0]) * n_std mean_x = np.mean(x) - # calculating the stdandard deviation of y ... + # calculating the standard deviation of y ... scale_y = np.sqrt(cov[1, 1]) * n_std mean_y = np.mean(y) @@ -97,7 +97,7 @@ def confidence_ellipse(x, y, ax, n_std=3.0, facecolor='none', **kwargs): # A helper function to create a correlated dataset # """""""""""""""""""""""""""""""""""""""""""""""" # -# Creates a random two-dimesional dataset with the specified +# Creates a random two-dimensional dataset with the specified # two-dimensional mean (mu) and dimensions (scale). # The correlation can be controlled by the param 'dependency', # a 2x2 matrix. @@ -190,7 +190,7 @@ def get_correlated_dataset(n, dependency, mu, scale): # Using the keyword arguments # """"""""""""""""""""""""""" # -# Use the kwargs specified for matplotlib.patches.Patch in order +# Use the keyword arguments specified for `matplotlib.patches.Patch` in order # to have the ellipse rendered in different ways. fig, ax_kwargs = plt.subplots(figsize=(6, 6)) @@ -210,20 +210,17 @@ def get_correlated_dataset(n, dependency, mu, scale): ax_kwargs.scatter(x, y, s=0.5) ax_kwargs.scatter(mu[0], mu[1], c='red', s=3) -ax_kwargs.set_title('Using kwargs') +ax_kwargs.set_title('Using keyword arguments') fig.subplots_adjust(hspace=0.25) plt.show() ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.transforms.Affine2D -matplotlib.patches.Ellipse +# - `matplotlib.transforms.Affine2D` +# - `matplotlib.patches.Ellipse` diff --git a/examples/statistics/customized_violin.py b/examples/statistics/customized_violin.py index 61c52c2d9a87..4809e5f28d81 100644 --- a/examples/statistics/customized_violin.py +++ b/examples/statistics/customized_violin.py @@ -3,12 +3,11 @@ Violin plot customization ========================= -This example demonstrates how to fully customize violin plots. -The first plot shows the default style by providing only -the data. The second plot first limits what matplotlib draws -with additional kwargs. Then a simplified representation of -a box plot is drawn on top. Lastly, the styles of the artists -of the violins are modified. +This example demonstrates how to fully customize violin plots. The first plot +shows the default style by providing only the data. The second plot first +limits what Matplotlib draws with additional keyword arguments. Then a +simplified representation of a box plot is drawn on top. Lastly, the styles of +the artists of the violins are modified. For more information on violin plots, the scikit-learn docs have a great section: https://scikit-learn.org/stable/modules/density.html @@ -30,8 +29,7 @@ def adjacent_values(vals, q1, q3): def set_axis_style(ax, labels): ax.xaxis.set_tick_params(direction='out') ax.xaxis.set_ticks_position('bottom') - ax.set_xticks(np.arange(1, len(labels) + 1)) - ax.set_xticklabels(labels) + ax.set_xticks(np.arange(1, len(labels) + 1), labels=labels) ax.set_xlim(0.25, len(labels) + 0.75) ax.set_xlabel('Sample name') @@ -77,15 +75,10 @@ def set_axis_style(ax, labels): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.violinplot -matplotlib.pyplot.violinplot -matplotlib.axes.Axes.vlines -matplotlib.pyplot.vlines +# - `matplotlib.axes.Axes.violinplot` / `matplotlib.pyplot.violinplot` +# - `matplotlib.axes.Axes.vlines` / `matplotlib.pyplot.vlines` diff --git a/examples/statistics/errorbar.py b/examples/statistics/errorbar.py index c6c0a8d61704..8d674095b4e4 100644 --- a/examples/statistics/errorbar.py +++ b/examples/statistics/errorbar.py @@ -21,13 +21,9 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.errorbar -matplotlib.pyplot.errorbar +# - `matplotlib.axes.Axes.errorbar` / `matplotlib.pyplot.errorbar` diff --git a/examples/statistics/errorbar_features.py b/examples/statistics/errorbar_features.py index 253d0008dcae..78e80f0aeb88 100644 --- a/examples/statistics/errorbar_features.py +++ b/examples/statistics/errorbar_features.py @@ -48,13 +48,9 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.errorbar -matplotlib.pyplot.errorbar +# - `matplotlib.axes.Axes.errorbar` / `matplotlib.pyplot.errorbar` diff --git a/examples/statistics/errorbar_limits.py b/examples/statistics/errorbar_limits.py index 914fef4cdbbc..5013b82ed977 100644 --- a/examples/statistics/errorbar_limits.py +++ b/examples/statistics/errorbar_limits.py @@ -77,13 +77,9 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.errorbar -matplotlib.pyplot.errorbar +# - `matplotlib.axes.Axes.errorbar` / `matplotlib.pyplot.errorbar` diff --git a/examples/statistics/errorbars_and_boxes.py b/examples/statistics/errorbars_and_boxes.py index 7c647aedaadd..a8bdd7a46384 100644 --- a/examples/statistics/errorbars_and_boxes.py +++ b/examples/statistics/errorbars_and_boxes.py @@ -12,9 +12,9 @@ 1. an `~.axes.Axes` object is passed directly to the function 2. the function operates on the ``Axes`` methods directly, not through the ``pyplot`` interface - 3. plotting kwargs that could be abbreviated are spelled out for - better code readability in the future (for example we use - ``facecolor`` instead of ``fc``) + 3. plotting keyword arguments that could be abbreviated are spelled out for + better code readability in the future (for example we use *facecolor* + instead of *fc*) 4. the artists returned by the ``Axes`` plotting methods are then returned by the function so that, if desired, their styles can be modified later outside of the function (they are not @@ -40,7 +40,7 @@ def make_error_boxes(ax, xdata, ydata, xerror, yerror, facecolor='r', - edgecolor='None', alpha=0.5): + edgecolor='none', alpha=0.5): # Loop over data points; create box from errors at each point errorboxes = [Rectangle((x - xe[0], y - ye[0]), xe.sum(), ye.sum()) @@ -55,7 +55,7 @@ def make_error_boxes(ax, xdata, ydata, xerror, yerror, facecolor='r', # Plot errorbars artists = ax.errorbar(xdata, ydata, xerr=xerror, yerr=yerror, - fmt='None', ecolor='k') + fmt='none', ecolor='k') return artists @@ -70,15 +70,11 @@ def make_error_boxes(ax, xdata, ydata, xerror, yerror, facecolor='r', ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.errorbar -matplotlib.pyplot.errorbar -matplotlib.axes.Axes.add_collection -matplotlib.collections.PatchCollection +# - `matplotlib.axes.Axes.errorbar` / `matplotlib.pyplot.errorbar` +# - `matplotlib.axes.Axes.add_collection` +# - `matplotlib.collections.PatchCollection` diff --git a/examples/statistics/hexbin_demo.py b/examples/statistics/hexbin_demo.py index 3d8e85cf8f00..b97e4f6c4347 100644 --- a/examples/statistics/hexbin_demo.py +++ b/examples/statistics/hexbin_demo.py @@ -1,14 +1,10 @@ """ -=========== -Hexbin Demo -=========== +===================== +Hexagonal binned plot +===================== -Plotting hexbins with Matplotlib. - -Hexbin is an axes method or pyplot function that is essentially -a pcolor of a 2D histogram with hexagonal cells. It can be -much more informative than a scatter plot. In the first plot -below, try substituting 'scatter' for 'hexbin'. +`~.Axes.hexbin` is a 2D histogram plot, in which the bins are hexagons and +the color represents the number of data points within each bin. """ import numpy as np @@ -17,41 +13,31 @@ # Fixing random state for reproducibility np.random.seed(19680801) -n = 100000 +n = 100_000 x = np.random.standard_normal(n) y = 2.0 + 3.0 * x + 4.0 * np.random.standard_normal(n) -xmin = x.min() -xmax = x.max() -ymin = y.min() -ymax = y.max() - -fig, axs = plt.subplots(ncols=2, sharey=True, figsize=(7, 4)) -fig.subplots_adjust(hspace=0.5, left=0.07, right=0.93) -ax = axs[0] -hb = ax.hexbin(x, y, gridsize=50, cmap='inferno') -ax.set(xlim=(xmin, xmax), ylim=(ymin, ymax)) -ax.set_title("Hexagon binning") -cb = fig.colorbar(hb, ax=ax) -cb.set_label('counts') - -ax = axs[1] -hb = ax.hexbin(x, y, gridsize=50, bins='log', cmap='inferno') -ax.set(xlim=(xmin, xmax), ylim=(ymin, ymax)) -ax.set_title("With a log color scale") -cb = fig.colorbar(hb, ax=ax) -cb.set_label('log10(N)') +xlim = x.min(), x.max() +ylim = y.min(), y.max() + +fig, (ax0, ax1) = plt.subplots(ncols=2, sharey=True, figsize=(9, 4)) + +hb = ax0.hexbin(x, y, gridsize=50, cmap='inferno') +ax0.set(xlim=xlim, ylim=ylim) +ax0.set_title("Hexagon binning") +cb = fig.colorbar(hb, ax=ax0, label='counts') + +hb = ax1.hexbin(x, y, gridsize=50, bins='log', cmap='inferno') +ax1.set(xlim=xlim, ylim=ylim) +ax1.set_title("With a log color scale") +cb = fig.colorbar(hb, ax=ax1, label='log10(N)') plt.show() ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.hexbin -matplotlib.pyplot.hexbin +# - `matplotlib.axes.Axes.hexbin` / `matplotlib.pyplot.hexbin` diff --git a/examples/statistics/hist.py b/examples/statistics/hist.py index 5fd409911c89..6c880961c922 100644 --- a/examples/statistics/hist.py +++ b/examples/statistics/hist.py @@ -3,7 +3,7 @@ Histograms ========== -Demonstrates how to plot histograms with matplotlib. +How to plot histograms with Matplotlib. """ import matplotlib.pyplot as plt @@ -11,9 +11,8 @@ from matplotlib import colors from matplotlib.ticker import PercentFormatter -# Fixing random state for reproducibility -np.random.seed(19680801) - +# Create a random number generator with a fixed seed for reproducibility +rng = np.random.default_rng(19680801) ############################################################################### # Generate data and plot a simple histogram @@ -26,15 +25,15 @@ N_points = 100000 n_bins = 20 -# Generate a normal distribution, center at x=0 and y=5 -x = np.random.randn(N_points) -y = .4 * x + np.random.randn(100000) + 5 +# Generate two normal distributions +dist1 = rng.standard_normal(N_points) +dist2 = 0.4 * rng.standard_normal(N_points) + 5 fig, axs = plt.subplots(1, 2, sharey=True, tight_layout=True) -# We can set the number of bins with the `bins` kwarg -axs[0].hist(x, bins=n_bins) -axs[1].hist(y, bins=n_bins) +# We can set the number of bins with the *bins* keyword argument. +axs[0].hist(dist1, bins=n_bins) +axs[1].hist(dist2, bins=n_bins) ############################################################################### @@ -49,7 +48,7 @@ fig, axs = plt.subplots(1, 2, tight_layout=True) # N is the count in each bin, bins is the lower-limit of the bin -N, bins, patches = axs[0].hist(x, bins=n_bins) +N, bins, patches = axs[0].hist(dist1, bins=n_bins) # We'll color code by height, but you could use any scalar fracs = N / N.max() @@ -63,7 +62,7 @@ thispatch.set_facecolor(color) # We can also normalize our inputs by the total number of counts -axs[1].hist(x, bins=n_bins, density=True) +axs[1].hist(dist1, bins=n_bins, density=True) # Now we format the y-axis to display percentage axs[1].yaxis.set_major_formatter(PercentFormatter(xmax=1)) @@ -77,7 +76,7 @@ # corresponding to each axis of the histogram. fig, ax = plt.subplots(tight_layout=True) -hist = ax.hist2d(x, y) +hist = ax.hist2d(dist1, dist2) ############################################################################### @@ -91,27 +90,23 @@ tight_layout=True) # We can increase the number of bins on each axis -axs[0].hist2d(x, y, bins=40) +axs[0].hist2d(dist1, dist2, bins=40) # As well as define normalization of the colors -axs[1].hist2d(x, y, bins=40, norm=colors.LogNorm()) +axs[1].hist2d(dist1, dist2, bins=40, norm=colors.LogNorm()) # We can also define custom numbers of bins for each axis -axs[2].hist2d(x, y, bins=(80, 10), norm=colors.LogNorm()) +axs[2].hist2d(dist1, dist2, bins=(80, 10), norm=colors.LogNorm()) plt.show() ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.hist -matplotlib.pyplot.hist -matplotlib.pyplot.hist2d -matplotlib.ticker.PercentFormatter +# - `matplotlib.axes.Axes.hist` / `matplotlib.pyplot.hist` +# - `matplotlib.pyplot.hist2d` +# - `matplotlib.ticker.PercentFormatter` diff --git a/examples/statistics/histogram_cumulative.py b/examples/statistics/histogram_cumulative.py index f5da13aa49fe..a057cc7b1267 100644 --- a/examples/statistics/histogram_cumulative.py +++ b/examples/statistics/histogram_cumulative.py @@ -7,14 +7,13 @@ step function in order to visualize the empirical cumulative distribution function (CDF) of a sample. We also show the theoretical CDF. -A couple of other options to the ``hist`` function are demonstrated. -Namely, we use the ``normed`` parameter to normalize the histogram and -a couple of different options to the ``cumulative`` parameter. -The ``normed`` parameter takes a boolean value. When ``True``, the bin -heights are scaled such that the total area of the histogram is 1. The -``cumulative`` kwarg is a little more nuanced. Like ``normed``, you -can pass it True or False, but you can also pass it -1 to reverse the -distribution. +A couple of other options to the ``hist`` function are demonstrated. Namely, we +use the *normed* parameter to normalize the histogram and a couple of different +options to the *cumulative* parameter. The *normed* parameter takes a boolean +value. When ``True``, the bin heights are scaled such that the total area of +the histogram is 1. The *cumulative* keyword argument is a little more nuanced. +Like *normed*, you can pass it True or False, but you can also pass it -1 to +reverse the distribution. Since we're showing a normalized and cumulative histogram, these curves are effectively the cumulative distribution functions (CDFs) of the @@ -73,13 +72,9 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.hist -matplotlib.pyplot.hist +# - `matplotlib.axes.Axes.hist` / `matplotlib.pyplot.hist` diff --git a/examples/statistics/histogram_features.py b/examples/statistics/histogram_features.py index 27c4940822d8..31f88384f24c 100644 --- a/examples/statistics/histogram_features.py +++ b/examples/statistics/histogram_features.py @@ -48,16 +48,12 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.hist -matplotlib.pyplot.hist -matplotlib.axes.Axes.set_title -matplotlib.axes.Axes.set_xlabel -matplotlib.axes.Axes.set_ylabel +# - `matplotlib.axes.Axes.hist` / `matplotlib.pyplot.hist` +# - `matplotlib.axes.Axes.set_title` +# - `matplotlib.axes.Axes.set_xlabel` +# - `matplotlib.axes.Axes.set_ylabel` diff --git a/examples/statistics/histogram_histtypes.py b/examples/statistics/histogram_histtypes.py index c22c96329be0..ca5c051d943c 100644 --- a/examples/statistics/histogram_histtypes.py +++ b/examples/statistics/histogram_histtypes.py @@ -51,13 +51,9 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.hist -matplotlib.pyplot.hist +# - `matplotlib.axes.Axes.hist` / `matplotlib.pyplot.hist` diff --git a/examples/statistics/histogram_multihist.py b/examples/statistics/histogram_multihist.py index 65c7b11f337e..c832259c6670 100644 --- a/examples/statistics/histogram_multihist.py +++ b/examples/statistics/histogram_multihist.py @@ -47,13 +47,9 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.hist -matplotlib.pyplot.hist +# - `matplotlib.axes.Axes.hist` / `matplotlib.pyplot.hist` diff --git a/examples/statistics/multiple_histograms_side_by_side.py b/examples/statistics/multiple_histograms_side_by_side.py index eef1a22602e4..6e7f34ac9556 100644 --- a/examples/statistics/multiple_histograms_side_by_side.py +++ b/examples/statistics/multiple_histograms_side_by_side.py @@ -45,8 +45,8 @@ # The bin_edges are the same for all of the histograms bin_edges = np.linspace(hist_range[0], hist_range[1], number_of_bins + 1) -centers = 0.5 * (bin_edges + np.roll(bin_edges, 1))[:-1] heights = np.diff(bin_edges) +centers = bin_edges[:-1] + heights / 2 # Cycle through and plot each histogram fig, ax = plt.subplots() @@ -54,8 +54,7 @@ lefts = x_loc - 0.5 * binned_data ax.barh(centers, binned_data, height=heights, left=lefts) -ax.set_xticks(x_locations) -ax.set_xticklabels(labels) +ax.set_xticks(x_locations, labels) ax.set_ylabel("Data values") ax.set_xlabel("Data sets") @@ -64,13 +63,9 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.barh -matplotlib.pyplot.barh +# - `matplotlib.axes.Axes.barh` / `matplotlib.pyplot.barh` diff --git a/examples/statistics/time_series_histogram.py b/examples/statistics/time_series_histogram.py index c0e95f93bfc6..94f904fab91e 100644 --- a/examples/statistics/time_series_histogram.py +++ b/examples/statistics/time_series_histogram.py @@ -97,14 +97,10 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.pcolormesh -matplotlib.pyplot.pcolormesh -matplotlib.figure.Figure.colorbar +# - `matplotlib.axes.Axes.pcolormesh` / `matplotlib.pyplot.pcolormesh` +# - `matplotlib.figure.Figure.colorbar` diff --git a/examples/statistics/violinplot.py b/examples/statistics/violinplot.py index a2ff6aed61ba..7c9c894f7aec 100644 --- a/examples/statistics/violinplot.py +++ b/examples/statistics/violinplot.py @@ -88,13 +88,9 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.violinplot -matplotlib.pyplot.violinplot +# - `matplotlib.axes.Axes.violinplot` / `matplotlib.pyplot.violinplot` diff --git a/examples/style_sheets/ggplot.py b/examples/style_sheets/ggplot.py index a1563f1bd0d2..52139c348b82 100644 --- a/examples/style_sheets/ggplot.py +++ b/examples/style_sheets/ggplot.py @@ -8,7 +8,7 @@ These settings were shamelessly stolen from [1]_ (with permission). -.. [1] https://web.archive.org/web/20111215111010/http://www.huyng.com/archives/sane-color-scheme-for-matplotlib/691/ +.. [1] https://everyhue.me/posts/sane-color-scheme-for-matplotlib/ .. _ggplot: https://ggplot2.tidyverse.org/ .. _R: https://www.r-project.org/ @@ -23,7 +23,7 @@ np.random.seed(19680801) fig, axs = plt.subplots(ncols=2, nrows=2) -ax1, ax2, ax3, ax4 = axs.ravel() +ax1, ax2, ax3, ax4 = axs.flat # scatter plot (Note: `plt.scatter` doesn't use default colors) x, y = np.random.normal(size=(2, 200)) @@ -45,8 +45,7 @@ ax3.bar(x, y1, width) ax3.bar(x + width, y2, width, color=list(plt.rcParams['axes.prop_cycle'])[2]['color']) -ax3.set_xticks(x + width) -ax3.set_xticklabels(['a', 'b', 'c', 'd', 'e']) +ax3.set_xticks(x + width, labels=['a', 'b', 'c', 'd', 'e']) # circles with colors from default color cycle for i, color in enumerate(plt.rcParams['axes.prop_cycle']): diff --git a/examples/style_sheets/plot_solarizedlight2.py b/examples/style_sheets/plot_solarizedlight2.py index 530e4027cdc6..5fa82b295932 100644 --- a/examples/style_sheets/plot_solarizedlight2.py +++ b/examples/style_sheets/plot_solarizedlight2.py @@ -6,7 +6,7 @@ This shows an example of "Solarized_Light" styling, which tries to replicate the styles of: -- http://ethanschoonover.com/solarized +- https://ethanschoonover.com/solarized/ - https://github.com/jrnold/ggthemes - http://www.pygal.org/en/stable/documentation/builtin_styles.html#light-solarized diff --git a/examples/style_sheets/style_sheets_reference.py b/examples/style_sheets/style_sheets_reference.py index 8bdeb0c62bc7..657c73aadb10 100644 --- a/examples/style_sheets/style_sheets_reference.py +++ b/examples/style_sheets/style_sheets_reference.py @@ -11,6 +11,7 @@ import numpy as np import matplotlib.pyplot as plt +import matplotlib.colors as mcolors # Fixing random state for reproducibility np.random.seed(19680801) @@ -26,15 +27,19 @@ def plot_scatter(ax, prng, nb_samples=100): return ax -def plot_colored_sinusoidal_lines(ax): - """Plot sinusoidal lines with colors following the style color cycle.""" - L = 2 * np.pi - x = np.linspace(0, L) +def plot_colored_lines(ax): + """Plot lines with colors following the style color cycle.""" + t = np.linspace(-10, 10, 100) + + def sigmoid(t, t0): + return 1 / (1 + np.exp(-(t - t0))) + nb_colors = len(plt.rcParams['axes.prop_cycle']) - shift = np.linspace(0, L, nb_colors, endpoint=False) - for s in shift: - ax.plot(x, np.sin(x + s), '-') - ax.set_xlim([x[0], x[-1]]) + shifts = np.linspace(-5, 5, nb_colors) + amplitudes = np.linspace(1, 1.5, nb_colors) + for t0, a in zip(shifts, amplitudes): + ax.plot(t, a * sigmoid(t, t0), '-') + ax.set_xlim(-10, 10) return ax @@ -45,8 +50,7 @@ def plot_bar_graphs(ax, prng, min_value=5, max_value=25, nb_samples=5): width = 0.25 ax.bar(x, ya, width) ax.bar(x + width, yb, width, color='C2') - ax.set_xticks(x + width) - ax.set_xticklabels(['a', 'b', 'c', 'd', 'e']) + ax.set_xticks(x + width, labels=['a', 'b', 'c', 'd', 'e']) return ax @@ -105,40 +109,43 @@ def plot_figure(style_label=""): # across the different figures. prng = np.random.RandomState(96917002) - # Tweak the figure size to be better suited for a row of numerous plots: - # double the width and halve the height. NB: use relative changes because - # some styles may have a figure size different from the default one. - (fig_width, fig_height) = plt.rcParams['figure.figsize'] - fig_size = [fig_width * 2, fig_height / 2] - fig, axs = plt.subplots(ncols=6, nrows=1, num=style_label, - figsize=fig_size, squeeze=True) - axs[0].set_ylabel(style_label) + figsize=(14.8, 2.7), constrained_layout=True) + + # make a suptitle, in the same style for all subfigures, + # except those with dark backgrounds, which get a lighter color: + background_color = mcolors.rgb_to_hsv( + mcolors.to_rgb(plt.rcParams['figure.facecolor']))[2] + if background_color < 0.5: + title_color = [0.8, 0.8, 1] + else: + title_color = np.array([19, 6, 84]) / 256 + fig.suptitle(style_label, x=0.01, ha='left', color=title_color, + fontsize=14, fontfamily='DejaVu Sans', fontweight='normal') plot_scatter(axs[0], prng) plot_image_and_patch(axs[1], prng) plot_bar_graphs(axs[2], prng) plot_colored_circles(axs[3], prng) - plot_colored_sinusoidal_lines(axs[4]) + plot_colored_lines(axs[4]) plot_histograms(axs[5], prng) - fig.tight_layout() - - return fig - if __name__ == "__main__": # Setup a list of all available styles, in alphabetical order but # the `default` and `classic` ones, which will be forced resp. in # first and second position. + # styles with leading underscores are for internal use such as testing + # and plot types gallery. These are excluded here. style_list = ['default', 'classic'] + sorted( - style for style in plt.style.available if style != 'classic') + style for style in plt.style.available + if style != 'classic' and not style.startswith('_')) # Plot a demonstration figure for every available style sheet. for style_label in style_list: with plt.rc_context({"figure.max_open_warning": len(style_list)}): with plt.style.context(style_label): - fig = plot_figure(style_label=style_label) + plot_figure(style_label=style_label) plt.show() diff --git a/examples/subplots_axes_and_figures/align_labels_demo.py b/examples/subplots_axes_and_figures/align_labels_demo.py index 4be8081ee1f0..0e600b790c47 100644 --- a/examples/subplots_axes_and_figures/align_labels_demo.py +++ b/examples/subplots_axes_and_figures/align_labels_demo.py @@ -30,8 +30,7 @@ ax.set_ylabel('YLabel1 %d' % i) ax.set_xlabel('XLabel1 %d' % i) if i == 0: - for tick in ax.get_xticklabels(): - tick.set_rotation(55) + ax.tick_params(axis='x', rotation=55) fig.align_labels() # same as fig.align_xlabels(); fig.align_ylabels() plt.show() diff --git a/examples/subplots_axes_and_figures/auto_subplots_adjust.py b/examples/subplots_axes_and_figures/auto_subplots_adjust.py new file mode 100644 index 000000000000..bd6326b8291f --- /dev/null +++ b/examples/subplots_axes_and_figures/auto_subplots_adjust.py @@ -0,0 +1,86 @@ +""" +=============================================== +Programmatically controlling subplot adjustment +=============================================== + +.. note:: + + This example is primarily intended to show some advanced concepts in + Matplotlib. + + If you are only looking for having enough space for your labels, it is + almost always simpler and good enough to either set the subplot parameters + manually using `.Figure.subplots_adjust`, or use one of the automatic + layout mechanisms + (:doc:`/tutorials/intermediate/constrainedlayout_guide` or + :doc:`/tutorials/intermediate/tight_layout_guide`). + +This example describes a user-defined way to read out Artist sizes and +set the subplot parameters accordingly. Its main purpose is to illustrate +some advanced concepts like reading out text positions, working with +bounding boxes and transforms and using +:ref:`events `. But it can also serve as a starting +point if you want to automate the layouting and need more flexibility than +tight layout and constrained layout. + +Below, we collect the bounding boxes of all y-labels and move the left border +of the subplot to the right so that it leaves enough room for the union of all +the bounding boxes. + +There's one catch with calculating text bounding boxes: +Querying the text bounding boxes (`.Text.get_window_extent`) needs a +renderer (`.RendererBase` instance), to calculate the text size. This renderer +is only available after the figure has been drawn (`.Figure.draw`). + +A solution to this is putting the adjustment logic in a draw callback. +This function is executed after the figure has been drawn. It can now check +if the subplot leaves enough room for the text. If not, the subplot parameters +are updated and second draw is triggered. + +.. redirect-from:: /gallery/pyplots/auto_subplots_adjust +""" + +import matplotlib.pyplot as plt +import matplotlib.transforms as mtransforms + +fig, ax = plt.subplots() +ax.plot(range(10)) +ax.set_yticks([2, 5, 7], labels=['really, really, really', 'long', 'labels']) + + +def on_draw(event): + bboxes = [] + for label in ax.get_yticklabels(): + # Bounding box in pixels + bbox_px = label.get_window_extent() + # Transform to relative figure coordinates. This is the inverse of + # transFigure. + bbox_fig = bbox_px.transformed(fig.transFigure.inverted()) + bboxes.append(bbox_fig) + # the bbox that bounds all the bboxes, again in relative figure coords + bbox = mtransforms.Bbox.union(bboxes) + if fig.subplotpars.left < bbox.width: + # Move the subplot left edge more to the right + fig.subplots_adjust(left=1.1*bbox.width) # pad a little + fig.canvas.draw() + + +fig.canvas.mpl_connect('draw_event', on_draw) + +plt.show() + +############################################################################# +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.artist.Artist.get_window_extent` +# - `matplotlib.transforms.Bbox` +# - `matplotlib.transforms.BboxBase.transformed` +# - `matplotlib.transforms.BboxBase.union` +# - `matplotlib.transforms.Transform.inverted` +# - `matplotlib.figure.Figure.subplots_adjust` +# - `matplotlib.figure.SubplotParams` +# - `matplotlib.backend_bases.FigureCanvasBase.mpl_connect` diff --git a/examples/subplots_axes_and_figures/axes_box_aspect.py b/examples/subplots_axes_and_figures/axes_box_aspect.py index 6acfcb6046f9..019c7e43c787 100644 --- a/examples/subplots_axes_and_figures/axes_box_aspect.py +++ b/examples/subplots_axes_and_figures/axes_box_aspect.py @@ -3,7 +3,7 @@ Axes box aspect =============== -This demo shows how to set the aspect of an axes box directly via +This demo shows how to set the aspect of an Axes box directly via `~.Axes.set_box_aspect`. The box aspect is the ratio between axes height and axes width in physical units, independent of the data limits. This is useful to e.g. produce a square plot, independent of the data it @@ -19,7 +19,6 @@ # # Produce a square axes, no matter what the data limits are. -import matplotlib import numpy as np import matplotlib.pyplot as plt @@ -120,7 +119,7 @@ # ~~~~~~~~~~~~~~~~~~~~~~~~~~ # # When setting the box aspect, one may still set the data aspect as well. -# Here we create an axes with a box twice as long as tall and use an "equal" +# Here we create an Axes with a box twice as long as tall and use an "equal" # data aspect for its contents, i.e. the circle actually stays circular. fig6, ax = plt.subplots() @@ -136,8 +135,8 @@ # Box aspect for many subplots # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # -# It is possible to pass the box aspect to an axes at initialization. The -# following creates a 2 by 3 subplot grid with all square axes. +# It is possible to pass the box aspect to an Axes at initialization. The +# following creates a 2 by 3 subplot grid with all square Axes. fig7, axs = plt.subplots(2, 3, subplot_kw=dict(box_aspect=1), sharex=True, sharey=True, constrained_layout=True) @@ -148,12 +147,9 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown -# in this example: - -matplotlib.axes.Axes.set_box_aspect +# - `matplotlib.axes.Axes.set_box_aspect` diff --git a/examples/subplots_axes_and_figures/axes_margins.py b/examples/subplots_axes_and_figures/axes_margins.py index 41347f715d4d..d532f6138fe8 100644 --- a/examples/subplots_axes_and_figures/axes_margins.py +++ b/examples/subplots_axes_and_figures/axes_margins.py @@ -13,6 +13,7 @@ import numpy as np import matplotlib.pyplot as plt +from matplotlib.patches import Polygon def f(t): @@ -64,7 +65,7 @@ def f(t): for ax, status in zip((ax1, ax2), ('Is', 'Is Not')): cells = ax.pcolor(x, y, x+y, cmap='inferno', shading='auto') # sticky ax.add_patch( - plt.Polygon(poly_coords, color='forestgreen', alpha=0.5) + Polygon(poly_coords, color='forestgreen', alpha=0.5) ) # not sticky ax.margins(x=0.1, y=0.05) ax.set_aspect('equal') @@ -75,18 +76,12 @@ def f(t): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.margins -matplotlib.pyplot.margins -matplotlib.axes.Axes.use_sticky_edges -matplotlib.axes.Axes.pcolor -matplotlib.pyplot.pcolor -matplotlib.pyplot.Polygon +# - `matplotlib.axes.Axes.margins` / `matplotlib.pyplot.margins` +# - `matplotlib.axes.Axes.use_sticky_edges` +# - `matplotlib.axes.Axes.pcolor` / `matplotlib.pyplot.pcolor` +# - `matplotlib.patches.Polygon` diff --git a/examples/subplots_axes_and_figures/axes_zoom_effect.py b/examples/subplots_axes_and_figures/axes_zoom_effect.py index b525cd7ccd41..4b7ff6e91e30 100644 --- a/examples/subplots_axes_and_figures/axes_zoom_effect.py +++ b/examples/subplots_axes_and_figures/axes_zoom_effect.py @@ -5,6 +5,8 @@ """ +import matplotlib.pyplot as plt + from matplotlib.transforms import ( Bbox, TransformedBbox, blended_transform_factory) from mpl_toolkits.axes_grid1.inset_locator import ( @@ -30,7 +32,6 @@ def connect_bbox(bbox1, bbox2, bbox_patch2 = BboxPatch(bbox2, **prop_patches) p = BboxConnectorPatch(bbox1, bbox2, - # loc1a=3, loc2a=2, loc1b=4, loc2b=1, loc1a=loc1a, loc2a=loc2a, loc1b=loc1b, loc2b=loc2b, clip_on=False, **prop_patches) @@ -107,19 +108,14 @@ def zoom_effect02(ax1, ax2, **kwargs): return c1, c2, bbox_patch1, bbox_patch2, p -import matplotlib.pyplot as plt - -plt.figure(figsize=(5, 5)) -ax1 = plt.subplot(221) -ax2 = plt.subplot(212) -ax2.set_xlim(0, 1) -ax2.set_xlim(0, 5) -zoom_effect01(ax1, ax2, 0.2, 0.8) - +axs = plt.figure().subplot_mosaic([ + ["zoom1", "zoom2"], + ["main", "main"], +]) -ax1 = plt.subplot(222) -ax1.set_xlim(2, 3) -ax2.set_xlim(0, 5) -zoom_effect02(ax1, ax2) +axs["main"].set(xlim=(0, 5)) +zoom_effect01(axs["zoom1"], axs["main"], 0.2, 0.8) +axs["zoom2"].set(xlim=(2, 3)) +zoom_effect02(axs["zoom2"], axs["main"]) plt.show() diff --git a/examples/subplots_axes_and_figures/colorbar_placement.py b/examples/subplots_axes_and_figures/colorbar_placement.py index 6beed5035856..66e267ce6d43 100644 --- a/examples/subplots_axes_and_figures/colorbar_placement.py +++ b/examples/subplots_axes_and_figures/colorbar_placement.py @@ -23,7 +23,6 @@ pcm = ax.pcolormesh(np.random.random((20, 20)) * (col + 1), cmap=cmaps[col]) fig.colorbar(pcm, ax=ax) -plt.show() ###################################################################### # The first column has the same type of data in both rows, so it may @@ -38,7 +37,6 @@ pcm = ax.pcolormesh(np.random.random((20, 20)) * (col + 1), cmap=cmaps[col]) fig.colorbar(pcm, ax=axs[:, col], shrink=0.6) -plt.show() ###################################################################### # Relatively complicated colorbar layouts are possible using this @@ -53,7 +51,6 @@ fig.colorbar(pcm, ax=[axs[0, 2]], location='bottom') fig.colorbar(pcm, ax=axs[1:, :], location='right', shrink=0.6) fig.colorbar(pcm, ax=[axs[2, 1]], location='left') -plt.show() ###################################################################### # Colorbars with fixed-aspect-ratio axes @@ -75,11 +72,10 @@ ax.set_aspect(1/2) if row == 1: fig.colorbar(pcm, ax=ax, shrink=0.6) -plt.show() ###################################################################### # One way around this issue is to use an `.Axes.inset_axes` to locate the -# axes in axes co-ordinates. Note that if you zoom in on the axes, and +# axes in axes coordinates. Note that if you zoom in on the axes, and # change the shape of the axes, the colorbar will also change position. fig, axs = plt.subplots(2, 2, constrained_layout=True) @@ -94,6 +90,7 @@ else: ax.set_aspect(1/2) if row == 1: - cax = ax.inset_axes([1.04, 0.2, 0.05, 0.6], transform=ax.transAxes) + cax = ax.inset_axes([1.04, 0.2, 0.05, 0.6]) fig.colorbar(pcm, ax=ax, cax=cax) + plt.show() diff --git a/examples/subplots_axes_and_figures/custom_figure_class.py b/examples/subplots_axes_and_figures/custom_figure_class.py index cf2363976550..efaa3f3621f1 100644 --- a/examples/subplots_axes_and_figures/custom_figure_class.py +++ b/examples/subplots_axes_and_figures/custom_figure_class.py @@ -41,15 +41,11 @@ def __init__(self, *args, watermark=None, **kwargs): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.pyplot.figure -matplotlib.figure.Figure -matplotlib.figure.Figure.text +# - `matplotlib.pyplot.figure` +# - `matplotlib.figure.Figure` +# - `matplotlib.figure.Figure.text` diff --git a/examples/subplots_axes_and_figures/demo_constrained_layout.py b/examples/subplots_axes_and_figures/demo_constrained_layout.py index e7aca95777d7..26109cbe3129 100644 --- a/examples/subplots_axes_and_figures/demo_constrained_layout.py +++ b/examples/subplots_axes_and_figures/demo_constrained_layout.py @@ -62,13 +62,10 @@ def example_plot(ax): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.gridspec.GridSpec -matplotlib.gridspec.GridSpecFromSubplotSpec +# - `matplotlib.gridspec.GridSpec` +# - `matplotlib.gridspec.GridSpecFromSubplotSpec` diff --git a/examples/subplots_axes_and_figures/demo_tight_layout.py b/examples/subplots_axes_and_figures/demo_tight_layout.py index 75eb8a44aa40..e3f22618ede5 100644 --- a/examples/subplots_axes_and_figures/demo_tight_layout.py +++ b/examples/subplots_axes_and_figures/demo_tight_layout.py @@ -3,9 +3,8 @@ Resizing axes with tight layout =============================== -`~.figure.Figure.tight_layout` attempts to resize subplots in -a figure so that there are no overlaps between axes objects and labels -on the axes. +`~.Figure.tight_layout` attempts to resize subplots in a figure so that there +are no overlaps between axes objects and labels on the axes. See :doc:`/tutorials/intermediate/tight_layout_guide` for more details and :doc:`/tutorials/intermediate/constrainedlayout_guide` for an alternative. @@ -31,7 +30,7 @@ def example_plot(ax): fig, ax = plt.subplots() example_plot(ax) -plt.tight_layout() +fig.tight_layout() ############################################################################### @@ -40,61 +39,53 @@ def example_plot(ax): example_plot(ax2) example_plot(ax3) example_plot(ax4) -plt.tight_layout() +fig.tight_layout() ############################################################################### fig, (ax1, ax2) = plt.subplots(nrows=2, ncols=1) example_plot(ax1) example_plot(ax2) -plt.tight_layout() +fig.tight_layout() ############################################################################### fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2) example_plot(ax1) example_plot(ax2) -plt.tight_layout() +fig.tight_layout() ############################################################################### fig, axs = plt.subplots(nrows=3, ncols=3) for ax in axs.flat: example_plot(ax) -plt.tight_layout() +fig.tight_layout() ############################################################################### -fig = plt.figure() - +plt.figure() ax1 = plt.subplot(221) ax2 = plt.subplot(223) ax3 = plt.subplot(122) - example_plot(ax1) example_plot(ax2) example_plot(ax3) - plt.tight_layout() ############################################################################### -fig = plt.figure() - +plt.figure() ax1 = plt.subplot2grid((3, 3), (0, 0)) ax2 = plt.subplot2grid((3, 3), (0, 1), colspan=2) ax3 = plt.subplot2grid((3, 3), (1, 0), colspan=2, rowspan=2) ax4 = plt.subplot2grid((3, 3), (1, 2), rowspan=2) - example_plot(ax1) example_plot(ax2) example_plot(ax3) example_plot(ax4) - plt.tight_layout() -plt.show() - ############################################################################### fig = plt.figure() @@ -103,20 +94,16 @@ def example_plot(ax): ax1 = fig.add_subplot(gs1[0]) ax2 = fig.add_subplot(gs1[1]) ax3 = fig.add_subplot(gs1[2]) - example_plot(ax1) example_plot(ax2) example_plot(ax3) - gs1.tight_layout(fig, rect=[None, None, 0.45, None]) gs2 = fig.add_gridspec(2, 1) ax4 = fig.add_subplot(gs2[0]) ax5 = fig.add_subplot(gs2[1]) - example_plot(ax4) example_plot(ax5) - with warnings.catch_warnings(): # gs2.tight_layout cannot handle the subplots from the first gridspec # (gs1), so it will raise a warning. We are going to match the gridspecs @@ -135,16 +122,13 @@ def example_plot(ax): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.pyplot.tight_layout -matplotlib.figure.Figure.tight_layout -matplotlib.figure.Figure.add_gridspec -matplotlib.figure.Figure.add_subplot -matplotlib.pyplot.subplot2grid +# - `matplotlib.figure.Figure.tight_layout` / +# `matplotlib.pyplot.tight_layout` +# - `matplotlib.figure.Figure.add_gridspec` +# - `matplotlib.figure.Figure.add_subplot` +# - `matplotlib.pyplot.subplot2grid` diff --git a/examples/subplots_axes_and_figures/figure_size_units.py b/examples/subplots_axes_and_figures/figure_size_units.py index f3154267ac50..94de72c1554c 100644 --- a/examples/subplots_axes_and_figures/figure_size_units.py +++ b/examples/subplots_axes_and_figures/figure_size_units.py @@ -56,30 +56,26 @@ # 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. ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib - -matplotlib.pyplot.figure -matplotlib.pyplot.subplots -matplotlib.pyplot.subplot_mosaic +# - `matplotlib.pyplot.figure` +# - `matplotlib.pyplot.subplots` +# - `matplotlib.pyplot.subplot_mosaic` diff --git a/examples/subplots_axes_and_figures/ganged_plots.py b/examples/subplots_axes_and_figures/ganged_plots.py index 4014dabdf046..1456bcd8d3e4 100644 --- a/examples/subplots_axes_and_figures/ganged_plots.py +++ b/examples/subplots_axes_and_figures/ganged_plots.py @@ -21,7 +21,7 @@ s3 = s1 * s2 fig, axs = plt.subplots(3, 1, sharex=True) -# Remove horizontal space between axes +# Remove vertical space between axes fig.subplots_adjust(hspace=0) # Plot each graph, and manually set the y tick values diff --git a/examples/subplots_axes_and_figures/geo_demo.py b/examples/subplots_axes_and_figures/geo_demo.py index 562f9d710438..27b6f27ae6e4 100644 --- a/examples/subplots_axes_and_figures/geo_demo.py +++ b/examples/subplots_axes_and_figures/geo_demo.py @@ -6,7 +6,7 @@ This shows 4 possible geographic projections. Cartopy_ supports more projections. -.. _Cartopy: http://scitools.org.uk/cartopy +.. _Cartopy: https://scitools.org.uk/cartopy/ """ import matplotlib.pyplot as plt diff --git a/examples/subplots_axes_and_figures/gridspec_and_subplots.py b/examples/subplots_axes_and_figures/gridspec_and_subplots.py index fbb7ebd013a0..133fd37b5d71 100644 --- a/examples/subplots_axes_and_figures/gridspec_and_subplots.py +++ b/examples/subplots_axes_and_figures/gridspec_and_subplots.py @@ -8,7 +8,10 @@ and then remove the covered axes and fill the gap with a new bigger axes. Here we create a layout with the bottom two axes in the last column combined. -See also :doc:`/tutorials/intermediate/gridspec`. +To start with this layout (rather than removing the overlapping axes) use +`~.pyplot.subplot_mosaic`. + +See also :doc:`/tutorials/intermediate/arranging_axes`. """ import matplotlib.pyplot as plt diff --git a/examples/subplots_axes_and_figures/gridspec_nested.py b/examples/subplots_axes_and_figures/gridspec_nested.py index e47106e5d47c..2a73fc4b3c1c 100644 --- a/examples/subplots_axes_and_figures/gridspec_nested.py +++ b/examples/subplots_axes_and_figures/gridspec_nested.py @@ -7,7 +7,7 @@ set the position for a nested grid of subplots. Note that the same functionality can be achieved more directly with -`~.figure.FigureBase.subfigures`; see +`~.FigureBase.subfigures`; see :doc:`/gallery/subplots_axes_and_figures/subfigures`. """ diff --git a/examples/subplots_axes_and_figures/multiple_figs_demo.py b/examples/subplots_axes_and_figures/multiple_figs_demo.py index 2d30768902c7..ee667e95565f 100644 --- a/examples/subplots_axes_and_figures/multiple_figs_demo.py +++ b/examples/subplots_axes_and_figures/multiple_figs_demo.py @@ -10,10 +10,11 @@ .. note:: - We discourage working with multiple figures in pyplot because managing - the *current figure* is cumbersome and error-prone. Instead, we recommend - to use the object-oriented approach and call methods on Figure and Axes - instances. + We discourage working with multiple figures through the implicit pyplot + interface because managing the *current figure* is cumbersome and + error-prone. Instead, we recommend using the explicit approach and call + methods on Figure and Axes instances. See :ref:`api_interfaces` for an + explanation of the trade-offs between the implicit and explicit interfaces. """ import matplotlib.pyplot as plt diff --git a/examples/subplots_axes_and_figures/secondary_axis.py b/examples/subplots_axes_and_figures/secondary_axis.py index 4fc3de442d3b..8122a211f899 100644 --- a/examples/subplots_axes_and_figures/secondary_axis.py +++ b/examples/subplots_axes_and_figures/secondary_axis.py @@ -8,7 +8,7 @@ axes with only one axis visible via `.axes.Axes.secondary_xaxis` and `.axes.Axes.secondary_yaxis`. This secondary axis can have a different scale than the main axis by providing both a forward and an inverse conversion -function in a tuple to the ``functions`` kwarg: +function in a tuple to the *functions* keyword argument: """ import matplotlib.pyplot as plt @@ -87,9 +87,9 @@ def one_over(x): # nominal plot limits. # # In the specific case of the numpy linear interpolation, `numpy.interp`, -# this condition can be arbitrarily enforced by providing optional kwargs -# *left*, *right* such that values outside the data range are mapped -# well outside the plot limits. +# this condition can be arbitrarily enforced by providing optional keyword +# arguments *left*, *right* such that values outside the data range are +# mapped well outside the plot limits. fig, ax = plt.subplots(constrained_layout=True) xdata = np.arange(1, 11, 0.4) @@ -183,14 +183,10 @@ def anomaly_to_celsius(x): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib - -matplotlib.axes.Axes.secondary_xaxis -matplotlib.axes.Axes.secondary_yaxis +# - `matplotlib.axes.Axes.secondary_xaxis` +# - `matplotlib.axes.Axes.secondary_yaxis` diff --git a/examples/subplots_axes_and_figures/shared_axis_demo.py b/examples/subplots_axes_and_figures/shared_axis_demo.py index f09e5c87759b..55c2819cebcf 100644 --- a/examples/subplots_axes_and_figures/shared_axis_demo.py +++ b/examples/subplots_axes_and_figures/shared_axis_demo.py @@ -4,14 +4,14 @@ =========== You can share the x or y axis limits for one axis with another by -passing an axes instance as a *sharex* or *sharey* keyword argument. +passing an `~.axes.Axes` instance as a *sharex* or *sharey* keyword argument. Changing the axis limits on one axes will be reflected automatically in the other, and vice-versa, so when you navigate with the toolbar -the axes will follow each other on their shared axes. Ditto for +the Axes will follow each other on their shared axis. Ditto for changes in the axis scaling (e.g., log vs. linear). However, it is possible to have differences in tick labeling, e.g., you can selectively -turn off the tick labels on one axes. +turn off the tick labels on one Axes. The example below shows how to customize the tick labels on the various axes. Shared axes share the tick locator, tick formatter, @@ -20,13 +20,13 @@ because you may want to make the tick labels smaller on the upper axes, e.g., in the example below. -If you want to turn off the ticklabels for a given axes (e.g., on +If you want to turn off the ticklabels for a given Axes (e.g., on subplot(211) or subplot(212), you cannot do the standard trick:: setp(ax2, xticklabels=[]) because this changes the tick Formatter, which is shared among all -axes. But you can alter the visibility of the labels, which is a +Axes. But you can alter the visibility of the labels, which is a property:: setp(ax2.get_xticklabels(), visible=False) @@ -42,13 +42,13 @@ ax1 = plt.subplot(311) plt.plot(t, s1) -plt.setp(ax1.get_xticklabels(), fontsize=6) +plt.tick_params('x', labelsize=6) # share x only ax2 = plt.subplot(312, sharex=ax1) plt.plot(t, s2) # make these tick labels invisible -plt.setp(ax2.get_xticklabels(), visible=False) +plt.tick_params('x', labelbottom=False) # share x and y ax3 = plt.subplot(313, sharex=ax1, sharey=ax1) diff --git a/examples/subplots_axes_and_figures/subplot.py b/examples/subplots_axes_and_figures/subplot.py index 5502d7c23f2b..c368746a8856 100644 --- a/examples/subplots_axes_and_figures/subplot.py +++ b/examples/subplots_axes_and_figures/subplot.py @@ -4,18 +4,25 @@ ================= Simple demo with multiple subplots. + +For more options, see :doc:`/gallery/subplots_axes_and_figures/subplots_demo`. + +.. redirect-from:: /gallery/subplots_axes_and_figures/subplot_demo """ + import numpy as np import matplotlib.pyplot as plt -############################################################################### - +# Create some fake data. x1 = np.linspace(0.0, 5.0) -x2 = np.linspace(0.0, 2.0) - y1 = np.cos(2 * np.pi * x1) * np.exp(-x1) +x2 = np.linspace(0.0, 2.0) y2 = np.cos(2 * np.pi * x2) +############################################################################### +# `~.pyplot.subplots()` is the recommended method to generate simple subplot +# arrangements: + fig, (ax1, ax2) = plt.subplots(2, 1) fig.suptitle('A tale of 2 subplots') @@ -28,22 +35,8 @@ plt.show() -############################################################################# -# -# -# Alternative Method For Creating Multiple Plots -# """""""""""""""""""""""""""""""""""""""""""""" -# -# Subplots can also be generated using `~.pyplot.subplot()` \ -# as in the following example: -# - - -x1 = np.linspace(0.0, 5.0) -x2 = np.linspace(0.0, 2.0) - -y1 = np.cos(2 * np.pi * x1) * np.exp(-x1) -y2 = np.cos(2 * np.pi * x2) +############################################################################### +# Subplots can also be generated one at a time using `~.pyplot.subplot()`: plt.subplot(2, 1, 1) plt.plot(x1, y1, 'o-') diff --git a/examples/subplots_axes_and_figures/subplot_demo.py b/examples/subplots_axes_and_figures/subplot_demo.py deleted file mode 100644 index 836476829a65..000000000000 --- a/examples/subplots_axes_and_figures/subplot_demo.py +++ /dev/null @@ -1,28 +0,0 @@ -""" -================== -Basic Subplot Demo -================== - -Demo with two subplots. -For more options, see -:doc:`/gallery/subplots_axes_and_figures/subplots_demo` -""" -import numpy as np -import matplotlib.pyplot as plt - -# Data for plotting -x1 = np.linspace(0.0, 5.0) -x2 = np.linspace(0.0, 2.0) -y1 = np.cos(2 * np.pi * x1) * np.exp(-x1) -y2 = np.cos(2 * np.pi * x2) - -# Create two subplots sharing y axis -fig, (ax1, ax2) = plt.subplots(2, sharey=True) - -ax1.plot(x1, y1, 'ko-') -ax1.set(title='A tale of 2 subplots', ylabel='Damped oscillation') - -ax2.plot(x2, y2, 'r.-') -ax2.set(xlabel='time (s)', ylabel='Undamped') - -plt.show() diff --git a/examples/subplots_axes_and_figures/subplot_toolbar.py b/examples/subplots_axes_and_figures/subplot_toolbar.py deleted file mode 100644 index e65b1f7091f0..000000000000 --- a/examples/subplots_axes_and_figures/subplot_toolbar.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -=============== -Subplot Toolbar -=============== - -Matplotlib has a toolbar available for adjusting subplot spacing. -""" -import matplotlib.pyplot as plt -import numpy as np - - -# Fixing random state for reproducibility -np.random.seed(19680801) - -fig, axs = plt.subplots(2, 2) - -axs[0, 0].imshow(np.random.random((100, 100))) - -axs[0, 1].imshow(np.random.random((100, 100))) - -axs[1, 0].imshow(np.random.random((100, 100))) - -axs[1, 1].imshow(np.random.random((100, 100))) - -plt.subplot_tool() -plt.show() diff --git a/examples/subplots_axes_and_figures/subplots_adjust.py b/examples/subplots_axes_and_figures/subplots_adjust.py index fecc41f6f5ff..3eb6d709a546 100644 --- a/examples/subplots_axes_and_figures/subplots_adjust.py +++ b/examples/subplots_axes_and_figures/subplots_adjust.py @@ -1,10 +1,16 @@ """ -=============== -Subplots Adjust -=============== +============================= +Subplots spacings and margins +============================= -Adjusting the spacing of margins and subplots using -`~matplotlib.pyplot.subplots_adjust`. +Adjusting the spacing of margins and subplots using `.pyplot.subplots_adjust`. + +.. note:: + There is also a tool window to adjust the margins and spacings of displayed + figures interactively. It can be opened via the toolbar or by calling + `.pyplot.subplot_tool`. + +.. redirect-from:: /gallery/subplots_axes_and_figures/subplot_toolbar """ import matplotlib.pyplot as plt @@ -14,11 +20,12 @@ np.random.seed(19680801) plt.subplot(211) -plt.imshow(np.random.random((100, 100)), cmap=plt.cm.BuPu_r) +plt.imshow(np.random.random((100, 100))) plt.subplot(212) -plt.imshow(np.random.random((100, 100)), cmap=plt.cm.BuPu_r) +plt.imshow(np.random.random((100, 100))) plt.subplots_adjust(bottom=0.1, right=0.8, top=0.9) cax = plt.axes([0.85, 0.1, 0.075, 0.8]) plt.colorbar(cax=cax) + plt.show() diff --git a/examples/subplots_axes_and_figures/subplots_demo.py b/examples/subplots_axes_and_figures/subplots_demo.py index bbc8afa469fa..41b14dadd620 100644 --- a/examples/subplots_axes_and_figures/subplots_demo.py +++ b/examples/subplots_axes_and_figures/subplots_demo.py @@ -176,7 +176,7 @@ ax3.plot(x + 1, -y, 'tab:green') ax4.plot(x + 2, -y**2, 'tab:red') -for ax in axs.flat: +for ax in fig.get_axes(): ax.label_outer() ############################################################################### diff --git a/examples/subplots_axes_and_figures/two_scales.py b/examples/subplots_axes_and_figures/two_scales.py index 4873a6f3dd85..307f6c7053be 100644 --- a/examples/subplots_axes_and_figures/two_scales.py +++ b/examples/subplots_axes_and_figures/two_scales.py @@ -41,15 +41,11 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.twinx -matplotlib.axes.Axes.twiny -matplotlib.axes.Axes.tick_params +# - `matplotlib.axes.Axes.twinx` / `matplotlib.pyplot.twinx` +# - `matplotlib.axes.Axes.twiny` / `matplotlib.pyplot.twiny` +# - `matplotlib.axes.Axes.tick_params` / `matplotlib.pyplot.tick_params` diff --git a/examples/subplots_axes_and_figures/zoom_inset_axes.py b/examples/subplots_axes_and_figures/zoom_inset_axes.py index e4b630aa9356..85f3f78ec6b4 100644 --- a/examples/subplots_axes_and_figures/zoom_inset_axes.py +++ b/examples/subplots_axes_and_figures/zoom_inset_axes.py @@ -33,8 +33,8 @@ def get_demo_image(): x1, x2, y1, y2 = -1.5, -0.9, -2.5, -1.9 axins.set_xlim(x1, x2) axins.set_ylim(y1, y2) -axins.set_xticklabels('') -axins.set_yticklabels('') +axins.set_xticklabels([]) +axins.set_yticklabels([]) ax.indicate_inset_zoom(axins, edgecolor="black") @@ -42,14 +42,11 @@ def get_demo_image(): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions and methods is shown in this example: - -import matplotlib -matplotlib.axes.Axes.inset_axes -matplotlib.axes.Axes.indicate_inset_zoom -matplotlib.axes.Axes.imshow +# - `matplotlib.axes.Axes.inset_axes` +# - `matplotlib.axes.Axes.indicate_inset_zoom` +# - `matplotlib.axes.Axes.imshow` diff --git a/examples/text_labels_and_annotations/accented_text.py b/examples/text_labels_and_annotations/accented_text.py index eb45fe8f276d..9e9ad65e9f15 100644 --- a/examples/text_labels_and_annotations/accented_text.py +++ b/examples/text_labels_and_annotations/accented_text.py @@ -1,9 +1,9 @@ r""" ================================= -Using accented text in matplotlib +Using accented text in Matplotlib ================================= -Matplotlib supports accented characters via TeX mathtext or unicode. +Matplotlib supports accented characters via TeX mathtext or Unicode. Using mathtext, the following accents are provided: \\hat, \\breve, \\grave, \\bar, \\acute, \\tilde, \\vec, \\dot, \\ddot. All of them have the same @@ -24,7 +24,8 @@ ax.text(4, 0.5, r"$F=m\ddot{x}$") fig.tight_layout() -# Unicode demo +############################################################################# +# You can also use Unicode characters directly in strings. fig, ax = plt.subplots() ax.set_title("GISCARD CHAHUTÉ À L'ASSEMBLÉE") ax.set_xlabel("LE COUP DE DÉ DE DE GAULLE") diff --git a/examples/pyplots/align_ylabels.py b/examples/text_labels_and_annotations/align_ylabels.py similarity index 80% rename from examples/pyplots/align_ylabels.py rename to examples/text_labels_and_annotations/align_ylabels.py index 114145f14676..8e47909cc6c1 100644 --- a/examples/pyplots/align_ylabels.py +++ b/examples/text_labels_and_annotations/align_ylabels.py @@ -6,6 +6,7 @@ Two methods are shown here, one using a short call to `.Figure.align_ylabels` and the second a manual way to align the labels. +.. redirect-from:: /gallery/pyplots/align_ylabels """ import numpy as np import matplotlib.pyplot as plt @@ -72,19 +73,14 @@ def make_plot(axs): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.figure.Figure.align_ylabels -matplotlib.axis.Axis.set_label_coords -matplotlib.axes.Axes.plot -matplotlib.pyplot.plot -matplotlib.axes.Axes.set_title -matplotlib.axes.Axes.set_ylabel -matplotlib.axes.Axes.set_ylim +# - `matplotlib.figure.Figure.align_ylabels` +# - `matplotlib.axis.Axis.set_label_coords` +# - `matplotlib.axes.Axes.plot` / `matplotlib.pyplot.plot` +# - `matplotlib.axes.Axes.set_title` +# - `matplotlib.axes.Axes.set_ylabel` +# - `matplotlib.axes.Axes.set_ylim` diff --git a/examples/text_labels_and_annotations/angle_annotation.py b/examples/text_labels_and_annotations/angle_annotation.py index d22501232ce2..8272dd1f3e06 100644 --- a/examples/text_labels_and_annotations/angle_annotation.py +++ b/examples/text_labels_and_annotations/angle_annotation.py @@ -58,7 +58,6 @@ import numpy as np -import matplotlib import matplotlib.pyplot as plt from matplotlib.patches import Arc from matplotlib.transforms import IdentityTransform, TransformedBbox, Bbox @@ -135,8 +134,7 @@ def get_size(self): if self.unit == "points": factor = self.ax.figure.dpi / 72. elif self.unit[:4] == "axes": - b = TransformedBbox(Bbox.from_bounds(0, 0, 1, 1), - self.ax.transAxes) + b = TransformedBbox(Bbox.unit(), self.ax.transAxes) dic = {"max": max(b.width, b.height), "min": min(b.width, b.height), "width": b.width, "height": b.height} @@ -314,18 +312,14 @@ def plot_angle(ax, pos, angle, length=0.95, acol="C0", **kwargs): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown -# in this example: - -matplotlib.patches.Arc -matplotlib.axes.Axes.annotate -matplotlib.pyplot.annotate -matplotlib.text.Annotation -matplotlib.transforms.IdentityTransform -matplotlib.transforms.TransformedBbox -matplotlib.transforms.Bbox +# - `matplotlib.patches.Arc` +# - `matplotlib.axes.Axes.annotate` / `matplotlib.pyplot.annotate` +# - `matplotlib.text.Annotation` +# - `matplotlib.transforms.IdentityTransform` +# - `matplotlib.transforms.TransformedBbox` +# - `matplotlib.transforms.Bbox` diff --git a/examples/text_labels_and_annotations/angles_on_bracket_arrows.py b/examples/text_labels_and_annotations/angles_on_bracket_arrows.py new file mode 100644 index 000000000000..35a6cad22130 --- /dev/null +++ b/examples/text_labels_and_annotations/angles_on_bracket_arrows.py @@ -0,0 +1,64 @@ +""" +=================================== +Angle annotations on bracket arrows +=================================== + +This example shows how to add angle annotations to bracket arrow styles +created using `.FancyArrowPatch`. *angleA* and *angleB* are measured from a +vertical line as positive (to the left) or negative (to the right). Blue +`.FancyArrowPatch` arrows indicate the directions of *angleA* and *angleB* +from the vertical and axes text annotate the angle sizes. +""" + +import matplotlib.pyplot as plt +import numpy as np +from matplotlib.patches import FancyArrowPatch + + +def get_point_of_rotated_vertical(origin, line_length, degrees): + """Return xy coordinates of the vertical line end rotated by degrees.""" + rad = np.deg2rad(-degrees) + return [origin[0] + line_length * np.sin(rad), + origin[1] + line_length * np.cos(rad)] + + +fig, ax = plt.subplots(figsize=(8, 7)) +ax.set(xlim=(0, 6), ylim=(-1, 4)) +ax.set_title("Orientation of the bracket arrows relative to angleA and angleB") + +for i, style in enumerate(["]-[", "|-|"]): + for j, angle in enumerate([-40, 60]): + y = 2*i + j + arrow_centers = ((1, y), (5, y)) + vlines = ((1, y + 0.5), (5, y + 0.5)) + anglesAB = (angle, -angle) + bracketstyle = f"{style}, angleA={anglesAB[0]}, angleB={anglesAB[1]}" + bracket = FancyArrowPatch(*arrow_centers, arrowstyle=bracketstyle, + mutation_scale=42) + ax.add_patch(bracket) + ax.text(3, y + 0.05, bracketstyle, ha="center", va="bottom") + ax.vlines([i[0] for i in vlines], [y, y], [i[1] for i in vlines], + linestyles="--", color="C0") + # Get the top coordinates for the drawn patches at A and B + patch_tops = [get_point_of_rotated_vertical(center, 0.5, angle) + for center, angle in zip(arrow_centers, anglesAB)] + # Define the connection directions for the annotation arrows + connection_dirs = (1, -1) if angle > 0 else (-1, 1) + # Add arrows and annotation text + arrowstyle = "Simple, tail_width=0.5, head_width=4, head_length=8" + for vline, dir, patch_top, angle in zip(vlines, connection_dirs, + patch_tops, anglesAB): + kw = dict(connectionstyle=f"arc3,rad={dir * 0.5}", + arrowstyle=arrowstyle, color="C0") + ax.add_patch(FancyArrowPatch(vline, patch_top, **kw)) + ax.text(vline[0] - dir * 0.15, y + 0.3, f'{angle}°', ha="center", + va="center") + +############################################################################# +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.patches.ArrowStyle` diff --git a/examples/pyplots/annotate_transform.py b/examples/text_labels_and_annotations/annotate_transform.py similarity index 79% rename from examples/pyplots/annotate_transform.py rename to examples/text_labels_and_annotations/annotate_transform.py index 5816a05a0455..1145f7fdb9a2 100644 --- a/examples/pyplots/annotate_transform.py +++ b/examples/text_labels_and_annotations/annotate_transform.py @@ -6,6 +6,8 @@ This example shows how to use different coordinate systems for annotations. For a complete overview of the annotation capabilities, also see the :doc:`annotation tutorial`. + +.. redirect-from:: /gallery/pyplots/annotate_transform """ import numpy as np @@ -43,15 +45,10 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.transforms.Transform.transform -matplotlib.axes.Axes.annotate -matplotlib.pyplot.annotate +# - `matplotlib.transforms.Transform.transform` +# - `matplotlib.axes.Axes.annotate` / `matplotlib.pyplot.annotate` diff --git a/examples/pyplots/annotation_basic.py b/examples/text_labels_and_annotations/annotation_basic.py similarity index 74% rename from examples/pyplots/annotation_basic.py rename to examples/text_labels_and_annotations/annotation_basic.py index 1b2e6ec1a09c..d9ea9748197f 100644 --- a/examples/pyplots/annotation_basic.py +++ b/examples/text_labels_and_annotations/annotation_basic.py @@ -8,6 +8,8 @@ For a complete overview of the annotation capabilities, also see the :doc:`annotation tutorial`. + +.. redirect-from:: /gallery/pyplots/annotation_basic """ import numpy as np import matplotlib.pyplot as plt @@ -26,14 +28,9 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.annotate -matplotlib.pyplot.annotate +# - `matplotlib.axes.Axes.annotate` / `matplotlib.pyplot.annotate` diff --git a/examples/text_labels_and_annotations/annotation_demo.py b/examples/text_labels_and_annotations/annotation_demo.py index f04460b7698b..2008a8744675 100644 --- a/examples/text_labels_and_annotations/annotation_demo.py +++ b/examples/text_labels_and_annotations/annotation_demo.py @@ -107,7 +107,7 @@ # In the example below, the *xy* point is in native coordinates (*xycoords* # defaults to 'data'). For a polar axes, this is in (theta, radius) space. # The text in the example is placed in the fractional figure coordinate system. -# Text keyword args like horizontal and vertical alignment are respected. +# Text keyword arguments like horizontal and vertical alignment are respected. fig, ax = plt.subplots(subplot_kw=dict(projection='polar'), figsize=(3, 3)) r = np.arange(0, 1, 0.001) @@ -125,6 +125,7 @@ horizontalalignment='left', verticalalignment='bottom') +############################################################################# # You can also use polar notation on a cartesian axes. Here the native # coordinate system ('data') is cartesian, so you need to specify the # xycoords and textcoords as 'polar' if you want to use (theta, radius). @@ -230,6 +231,7 @@ ax.set(xlim=(-1, 5), ylim=(-4, 3)) +############################################################################# # We'll create another figure so that it doesn't get too cluttered fig, ax = plt.subplots() @@ -247,7 +249,6 @@ xy=(2., -1), xycoords='data', xytext=(-100, 60), textcoords='offset points', size=20, - # bbox=dict(boxstyle="round", fc="0.8"), arrowprops=dict(arrowstyle="fancy", fc="0.6", ec="none", patchB=el, @@ -256,7 +257,6 @@ xy=(2., -1), xycoords='data', xytext=(100, 60), textcoords='offset points', size=20, - # bbox=dict(boxstyle="round", fc="0.8"), arrowprops=dict(arrowstyle="simple", fc="0.6", ec="none", patchB=el, @@ -265,7 +265,6 @@ xy=(2., -1), xycoords='data', xytext=(-100, -100), textcoords='offset points', size=20, - # bbox=dict(boxstyle="round", fc="0.8"), arrowprops=dict(arrowstyle="wedge,tail_width=0.7", fc="0.6", ec="none", patchB=el, @@ -338,11 +337,8 @@ # It is also possible to generate draggable annotations an1 = ax1.annotate('Drag me 1', xy=(.5, .7), xycoords='data', - #xytext=(.5, .7), textcoords='data', ha="center", va="center", - bbox=bbox_args, - #arrowprops=arrow_args - ) + bbox=bbox_args) an2 = ax1.annotate('Drag me 2', xy=(.5, .5), xycoords=an1, xytext=(.5, .3), textcoords='axes fraction', diff --git a/examples/pyplots/annotation_polar.py b/examples/text_labels_and_annotations/annotation_polar.py similarity index 77% rename from examples/pyplots/annotation_polar.py rename to examples/text_labels_and_annotations/annotation_polar.py index 42d747fc60a6..a7f8b764d914 100644 --- a/examples/pyplots/annotation_polar.py +++ b/examples/text_labels_and_annotations/annotation_polar.py @@ -7,6 +7,8 @@ For a complete overview of the annotation capabilities, also see the :doc:`annotation tutorial`. + +.. redirect-from:: /gallery/pyplots/annotation_polar """ import numpy as np import matplotlib.pyplot as plt @@ -32,15 +34,10 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.projections.polar -matplotlib.axes.Axes.annotate -matplotlib.pyplot.annotate +# - `matplotlib.projections.polar` +# - `matplotlib.axes.Axes.annotate` / `matplotlib.pyplot.annotate` diff --git a/examples/text_labels_and_annotations/arrow_demo.py b/examples/text_labels_and_annotations/arrow_demo.py index 9e678249d1a1..cf9e86d12c3b 100644 --- a/examples/text_labels_and_annotations/arrow_demo.py +++ b/examples/text_labels_and_annotations/arrow_demo.py @@ -3,145 +3,74 @@ Arrow Demo ========== -Arrow drawing example for the new fancy_arrow facilities. - -Code contributed by: Rob Knight - -usage: - - python arrow_demo.py realistic|full|sample|extreme +Three ways of drawing arrows to encode arrow "strength" (e.g., transition +probabilities in a Markov model) using arrow length, width, or alpha (opacity). +""" +import itertools -""" import matplotlib.pyplot as plt import numpy as np -rates_to_bases = {'r1': 'AT', 'r2': 'TA', 'r3': 'GA', 'r4': 'AG', 'r5': 'CA', - 'r6': 'AC', 'r7': 'GT', 'r8': 'TG', 'r9': 'CT', 'r10': 'TC', - 'r11': 'GC', 'r12': 'CG'} -numbered_bases_to_rates = {v: k for k, v in rates_to_bases.items()} -lettered_bases_to_rates = {v: 'r' + v for k, v in rates_to_bases.items()} - -def make_arrow_plot(data, size=4, display='length', shape='right', - max_arrow_width=0.03, arrow_sep=0.02, alpha=0.5, - normalize_data=False, ec=None, labelcolor=None, - head_starts_at_zero=True, - rate_labels=lettered_bases_to_rates, - **kwargs): +def make_arrow_graph(ax, data, size=4, display='length', shape='right', + max_arrow_width=0.03, arrow_sep=0.02, alpha=0.5, + normalize_data=False, ec=None, labelcolor=None, + **kwargs): """ Makes an arrow plot. Parameters ---------- + ax + The axes where the graph is drawn. data Dict with probabilities for the bases and pair transitions. size - Size of the graph in inches. + Size of the plot, in inches. display : {'length', 'width', 'alpha'} The arrow property to change. shape : {'full', 'left', 'right'} For full or half arrows. max_arrow_width : float - Maximum width of an arrow, data coordinates. + Maximum width of an arrow, in data coordinates. arrow_sep : float - Separation between arrows in a pair, data coordinates. + Separation between arrows in a pair, in data coordinates. alpha : float Maximum opacity of arrows. **kwargs - Can be anything allowed by a Arrow object, e.g. *linewidth* or - *edgecolor*. + `.FancyArrow` properties, e.g. *linewidth* or *edgecolor*. """ - plt.xlim(-0.5, 1.5) - plt.ylim(-0.5, 1.5) - plt.gcf().set_size_inches(size, size) - plt.xticks([]) - plt.yticks([]) + ax.set(xlim=(-0.25, 1.25), ylim=(-0.25, 1.25), xticks=[], yticks=[], + title=f'flux encoded as arrow {display}') max_text_size = size * 12 min_text_size = size - label_text_size = size * 2.5 - text_params = {'ha': 'center', 'va': 'center', 'family': 'sans-serif', - 'fontweight': 'bold'} - r2 = np.sqrt(2) - - deltas = { - 'AT': (1, 0), - 'TA': (-1, 0), - 'GA': (0, 1), - 'AG': (0, -1), - 'CA': (-1 / r2, 1 / r2), - 'AC': (1 / r2, -1 / r2), - 'GT': (1 / r2, 1 / r2), - 'TG': (-1 / r2, -1 / r2), - 'CT': (0, 1), - 'TC': (0, -1), - 'GC': (1, 0), - 'CG': (-1, 0)} - - colors = { - 'AT': 'r', - 'TA': 'k', - 'GA': 'g', - 'AG': 'r', - 'CA': 'b', - 'AC': 'r', - 'GT': 'g', - 'TG': 'k', - 'CT': 'b', - 'TC': 'k', - 'GC': 'g', - 'CG': 'b'} - - label_positions = { - 'AT': 'center', - 'TA': 'center', - 'GA': 'center', - 'AG': 'center', - 'CA': 'left', - 'AC': 'left', - 'GT': 'left', - 'TG': 'left', - 'CT': 'center', - 'TC': 'center', - 'GC': 'center', - 'CG': 'center'} - - def do_fontsize(k): - return float(np.clip(max_text_size * np.sqrt(data[k]), - min_text_size, max_text_size)) - - plt.text(0, 1, '$A_3$', color='r', size=do_fontsize('A'), **text_params) - plt.text(1, 1, '$T_3$', color='k', size=do_fontsize('T'), **text_params) - plt.text(0, 0, '$G_3$', color='g', size=do_fontsize('G'), **text_params) - plt.text(1, 0, '$C_3$', color='b', size=do_fontsize('C'), **text_params) + label_text_size = size * 4 + + bases = 'ATGC' + coords = { + 'A': np.array([0, 1]), + 'T': np.array([1, 1]), + 'G': np.array([0, 0]), + 'C': np.array([1, 0]), + } + colors = {'A': 'r', 'T': 'k', 'G': 'g', 'C': 'b'} + + for base in bases: + fontsize = np.clip(max_text_size * data[base]**(1/2), + min_text_size, max_text_size) + ax.text(*coords[base], f'${base}_3$', + color=colors[base], size=fontsize, + horizontalalignment='center', verticalalignment='center', + weight='bold') arrow_h_offset = 0.25 # data coordinates, empirically determined max_arrow_length = 1 - 2 * arrow_h_offset max_head_width = 2.5 * max_arrow_width max_head_length = 2 * max_arrow_width - arrow_params = {'length_includes_head': True, 'shape': shape, - 'head_starts_at_zero': head_starts_at_zero} sf = 0.6 # max arrow size represents this in data coords - d = (r2 / 2 + arrow_h_offset - 0.5) / r2 # distance for diags - r2v = arrow_sep / r2 # offset for diags - - # tuple of x, y for start position - positions = { - 'AT': (arrow_h_offset, 1 + arrow_sep), - 'TA': (1 - arrow_h_offset, 1 - arrow_sep), - 'GA': (-arrow_sep, arrow_h_offset), - 'AG': (arrow_sep, 1 - arrow_h_offset), - 'CA': (1 - d - r2v, d - r2v), - 'AC': (d + r2v, 1 - d + r2v), - 'GT': (d - r2v, d + r2v), - 'TG': (1 - d + r2v, 1 - d - r2v), - 'CT': (1 - arrow_sep, arrow_h_offset), - 'TC': (1 + arrow_sep, 1 - arrow_h_offset), - 'GC': (arrow_h_offset, arrow_sep), - 'CG': (1 - arrow_h_offset, -arrow_sep)} - if normalize_data: # find maximum value for rates, i.e. where keys are 2 chars long max_val = max((v for k, v in data.items() if len(k) == 2), default=0) @@ -149,7 +78,8 @@ def do_fontsize(k): for k, v in data.items(): data[k] = v / max_val * sf - def draw_arrow(pair, alpha=alpha, ec=ec, labelcolor=labelcolor): + # iterate over strings 'AT', 'TA', 'AG', 'GA', etc. + for pair in map(''.join, itertools.permutations(bases, 2)): # set the length of the arrow if display == 'length': length = (max_head_length @@ -159,7 +89,6 @@ def draw_arrow(pair, alpha=alpha, ec=ec, labelcolor=labelcolor): # set the transparency of the arrow if display == 'alpha': alpha = min(data[pair] / sf, alpha) - # set the width of the arrow if display == 'width': scale = data[pair] / sf @@ -171,137 +100,60 @@ def draw_arrow(pair, alpha=alpha, ec=ec, labelcolor=labelcolor): head_width = max_head_width head_length = max_head_length - fc = colors[pair] - ec = ec or fc - - x_scale, y_scale = deltas[pair] - x_pos, y_pos = positions[pair] - plt.arrow(x_pos, y_pos, x_scale * length, y_scale * length, - fc=fc, ec=ec, alpha=alpha, width=width, - head_width=head_width, head_length=head_length, - **arrow_params) - - # figure out coordinates for text + fc = colors[pair[0]] + + cp0 = coords[pair[0]] + cp1 = coords[pair[1]] + # unit vector in arrow direction + delta = cos, sin = (cp1 - cp0) / np.hypot(*(cp1 - cp0)) + x_pos, y_pos = ( + (cp0 + cp1) / 2 # midpoint + - delta * length / 2 # half the arrow length + + np.array([-sin, cos]) * arrow_sep # shift outwards by arrow_sep + ) + ax.arrow( + x_pos, y_pos, cos * length, sin * length, + fc=fc, ec=ec or fc, alpha=alpha, width=width, + head_width=head_width, head_length=head_length, shape=shape, + length_includes_head=True, + **kwargs + ) + + # figure out coordinates for text: # if drawing relative to base: x and y are same as for arrow # dx and dy are one arrow width left and up - # need to rotate based on direction of arrow, use x_scale and y_scale - # as sin x and cos x? - sx, cx = y_scale, x_scale - - where = label_positions[pair] - if where == 'left': - orig_position = 3 * np.array([[max_arrow_width, max_arrow_width]]) - elif where == 'absolute': - orig_position = np.array([[max_arrow_length / 2.0, - 3 * max_arrow_width]]) - elif where == 'right': - orig_position = np.array([[length - 3 * max_arrow_width, - 3 * max_arrow_width]]) - elif where == 'center': - orig_position = np.array([[length / 2.0, 3 * max_arrow_width]]) - else: - raise ValueError("Got unknown position parameter %s" % where) - - M = np.array([[cx, sx], [-sx, cx]]) - coords = np.dot(orig_position, M) + [[x_pos, y_pos]] - x, y = np.ravel(coords) - orig_label = rate_labels[pair] - label = r'$%s_{_{\mathrm{%s}}}$' % (orig_label[0], orig_label[1:]) - - plt.text(x, y, label, size=label_text_size, ha='center', va='center', - color=labelcolor or fc) - - for p in sorted(positions): - draw_arrow(p) - - -# test data -all_on_max = dict([(i, 1) for i in 'TCAG'] + - [(i + j, 0.6) for i in 'TCAG' for j in 'TCAG']) - -realistic_data = { - 'A': 0.4, - 'T': 0.3, - 'G': 0.5, - 'C': 0.2, - 'AT': 0.4, - 'AC': 0.3, - 'AG': 0.2, - 'TA': 0.2, - 'TC': 0.3, - 'TG': 0.4, - 'CT': 0.2, - 'CG': 0.3, - 'CA': 0.2, - 'GA': 0.1, - 'GT': 0.4, - 'GC': 0.1} - -extreme_data = { - 'A': 0.75, - 'T': 0.10, - 'G': 0.10, - 'C': 0.05, - 'AT': 0.6, - 'AC': 0.3, - 'AG': 0.1, - 'TA': 0.02, - 'TC': 0.3, - 'TG': 0.01, - 'CT': 0.2, - 'CG': 0.5, - 'CA': 0.2, - 'GA': 0.1, - 'GT': 0.4, - 'GC': 0.2} - -sample_data = { - 'A': 0.2137, - 'T': 0.3541, - 'G': 0.1946, - 'C': 0.2376, - 'AT': 0.0228, - 'AC': 0.0684, - 'AG': 0.2056, - 'TA': 0.0315, - 'TC': 0.0629, - 'TG': 0.0315, - 'CT': 0.1355, - 'CG': 0.0401, - 'CA': 0.0703, - 'GA': 0.1824, - 'GT': 0.0387, - 'GC': 0.1106} + orig_positions = { + 'base': [3 * max_arrow_width, 3 * max_arrow_width], + 'center': [length / 2, 3 * max_arrow_width], + 'tip': [length - 3 * max_arrow_width, 3 * max_arrow_width], + } + # for diagonal arrows, put the label at the arrow base + # for vertical or horizontal arrows, center the label + where = 'base' if (cp0 != cp1).all() else 'center' + # rotate based on direction of arrow (cos, sin) + M = [[cos, -sin], [sin, cos]] + x, y = np.dot(M, orig_positions[where]) + [x_pos, y_pos] + label = r'$r_{_{\mathrm{%s}}}$' % (pair,) + ax.text(x, y, label, size=label_text_size, ha='center', va='center', + color=labelcolor or fc) if __name__ == '__main__': - from sys import argv - d = None - if len(argv) > 1: - if argv[1] == 'full': - d = all_on_max - scaled = False - elif argv[1] == 'extreme': - d = extreme_data - scaled = False - elif argv[1] == 'realistic': - d = realistic_data - scaled = False - elif argv[1] == 'sample': - d = sample_data - scaled = True - if d is None: - d = all_on_max - scaled = False - if len(argv) > 2: - display = argv[2] - else: - display = 'length' + data = { # test data + 'A': 0.4, 'T': 0.3, 'G': 0.6, 'C': 0.2, + 'AT': 0.4, 'AC': 0.3, 'AG': 0.2, + 'TA': 0.2, 'TC': 0.3, 'TG': 0.4, + 'CT': 0.2, 'CG': 0.3, 'CA': 0.2, + 'GA': 0.1, 'GT': 0.4, 'GC': 0.1, + } size = 4 - plt.figure(figsize=(size, size)) + fig = plt.figure(figsize=(3 * size, size), constrained_layout=True) + axs = fig.subplot_mosaic([["length", "width", "alpha"]]) - make_arrow_plot(d, display=display, linewidth=0.001, edgecolor=None, - normalize_data=scaled, head_starts_at_zero=True, size=size) + for display, ax in axs.items(): + make_arrow_graph( + ax, data, display=display, linewidth=0.001, edgecolor=None, + normalize_data=True, size=size) plt.show() diff --git a/examples/text_labels_and_annotations/arrow_simple_demo.py b/examples/text_labels_and_annotations/arrow_simple_demo.py deleted file mode 100644 index c8a07ee204d3..000000000000 --- a/examples/text_labels_and_annotations/arrow_simple_demo.py +++ /dev/null @@ -1,11 +0,0 @@ -""" -================= -Arrow Simple Demo -================= - -""" -import matplotlib.pyplot as plt - -ax = plt.axes() -ax.arrow(0, 0, 0.5, 0.5, head_width=0.05, head_length=0.1, fc='k', ec='k') -plt.show() diff --git a/examples/text_labels_and_annotations/custom_legends.py b/examples/text_labels_and_annotations/custom_legends.py index e9f840fe768f..7f4ca07398f4 100644 --- a/examples/text_labels_and_annotations/custom_legends.py +++ b/examples/text_labels_and_annotations/custom_legends.py @@ -19,7 +19,8 @@ and call ``ax.legend()``, you will get the following: """ # sphinx_gallery_thumbnail_number = 2 -from matplotlib import rcParams, cycler +import matplotlib as mpl +from matplotlib import cycler import matplotlib.pyplot as plt import numpy as np @@ -29,7 +30,7 @@ N = 10 data = (np.geomspace(1, 10, 100) + np.random.randn(N, 100)).T cmap = plt.cm.coolwarm -rcParams['axes.prop_cycle'] = cycler(color=cmap(np.linspace(0, 1, N))) +mpl.rcParams['axes.prop_cycle'] = cycler(color=cmap(np.linspace(0, 1, N))) fig, ax = plt.subplots() lines = ax.plot(data) diff --git a/examples/text_labels_and_annotations/date.py b/examples/text_labels_and_annotations/date.py index c8287fffcdc6..5ce94a5d876c 100644 --- a/examples/text_labels_and_annotations/date.py +++ b/examples/text_labels_and_annotations/date.py @@ -3,66 +3,61 @@ Date tick labels ================ -Show how to make date plots in Matplotlib using date tick locators and -formatters. See :doc:`/gallery/ticks_and_spines/major_minor_demo` for more -information on controlling major and minor ticks. - Matplotlib date plotting is done by converting date instances into days since an epoch (by default 1970-01-01T00:00:00). The :mod:`matplotlib.dates` module provides the converter functions `.date2num` -and `.num2date`, which convert `datetime.datetime` and `numpy.datetime64` +and `.num2date` that convert `datetime.datetime` and `numpy.datetime64` objects to and from Matplotlib's internal representation. These data -types are registered with with the unit conversion mechanism described in +types are registered with the unit conversion mechanism described in :mod:`matplotlib.units`, so the conversion happens automatically for the user. The registration process also sets the default tick ``locator`` and ``formatter`` for the axis to be `~.matplotlib.dates.AutoDateLocator` and -`~.matplotlib.dates.AutoDateFormatter`. These can be changed manually with -`.Axis.set_major_locator` and `.Axis.set_major_formatter`; see for example -:doc:`/gallery/ticks_and_spines/date_demo_convert`. - -An alternative formatter is the `~.matplotlib.dates.ConciseDateFormatter` -as described at :doc:`/gallery/ticks_and_spines/date_concise_formatter`, -which often removes the need to rotate the tick labels. +`~.matplotlib.dates.AutoDateFormatter`. + +An alternative formatter is the `~.dates.ConciseDateFormatter`, +used in the second ``Axes`` below (see +:doc:`/gallery/ticks/date_concise_formatter`), which often removes the need to +rotate the tick labels. The last ``Axes`` formats the dates manually, using +`~.dates.DateFormatter` to format the dates using the format strings documented +at `datetime.date.strftime`. """ -import numpy as np import matplotlib.pyplot as plt import matplotlib.dates as mdates import matplotlib.cbook as cbook -# Load a numpy structured array from yahoo csv data with fields date, open, -# close, volume, adj_close from the mpl-data/example directory. This array -# stores the date as an np.datetime64 with a day unit ('D') in the 'date' -# column. +# Load a numpy record array from yahoo csv data with fields date, open, high, +# low, close, volume, adj_close from the mpl-data/sample_data directory. The +# record array stores the date as an np.datetime64 with a day unit ('D') in +# the date column. data = cbook.get_sample_data('goog.npz', np_load=True)['price_data'] -fig, ax = plt.subplots() -ax.plot('date', 'adj_close', data=data) - -# Major ticks every 6 months. -fmt_half_year = mdates.MonthLocator(interval=6) -ax.xaxis.set_major_locator(fmt_half_year) - -# Minor ticks every month. -fmt_month = mdates.MonthLocator() -ax.xaxis.set_minor_locator(fmt_month) - +fig, axs = plt.subplots(3, 1, figsize=(6.4, 7), constrained_layout=True) +# common to all three: +for ax in axs: + ax.plot('date', 'adj_close', data=data) + # Major ticks every half year, minor ticks every month, + ax.xaxis.set_major_locator(mdates.MonthLocator(bymonth=(1, 7))) + ax.xaxis.set_minor_locator(mdates.MonthLocator()) + ax.grid(True) + ax.set_ylabel(r'Price [\$]') + +# different formats: +ax = axs[0] +ax.set_title('DefaultFormatter', loc='left', y=0.85, x=0.02, fontsize='medium') + +ax = axs[1] +ax.set_title('ConciseFormatter', loc='left', y=0.85, x=0.02, fontsize='medium') +ax.xaxis.set_major_formatter( + mdates.ConciseDateFormatter(ax.xaxis.get_major_locator())) + +ax = axs[2] +ax.set_title('Manual DateFormatter', loc='left', y=0.85, x=0.02, + fontsize='medium') # Text in the x axis will be displayed in 'YYYY-mm' format. -ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m')) - -# Round to nearest years. -datemin = np.datetime64(data['date'][0], 'Y') -datemax = np.datetime64(data['date'][-1], 'Y') + np.timedelta64(1, 'Y') -ax.set_xlim(datemin, datemax) - -# Format the coords message box, i.e. the numbers displayed as the cursor moves -# across the axes within the interactive GUI. -ax.format_xdata = mdates.DateFormatter('%Y-%m') -ax.format_ydata = lambda x: f'${x:.2f}' # Format the price. -ax.grid(True) - -# Rotates and right aligns the x labels, and moves the bottom of the -# axes up to make room for them. -fig.autofmt_xdate() +ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%b')) +# Rotates and right-aligns the x labels so they don't crowd each other. +for label in ax.get_xticklabels(which='major'): + label.set(rotation=30, horizontalalignment='right') plt.show() diff --git a/examples/text_labels_and_annotations/date_index_formatter.py b/examples/text_labels_and_annotations/date_index_formatter.py deleted file mode 100644 index 55cc81b75131..000000000000 --- a/examples/text_labels_and_annotations/date_index_formatter.py +++ /dev/null @@ -1,60 +0,0 @@ -""" -===================================== -Custom tick formatter for time series -===================================== - -When plotting time series, e.g., financial time series, one often wants -to leave out days on which there is no data, i.e. weekends. The example -below shows how to use an 'index formatter' to achieve the desired plot -""" -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.cbook as cbook - -# Load a numpy record array from yahoo csv data with fields date, open, close, -# volume, adj_close from the mpl-data/example directory. The record array -# stores the date as an np.datetime64 with a day unit ('D') in the date column. -r = (cbook.get_sample_data('goog.npz', np_load=True)['price_data'] - .view(np.recarray)) -r = r[-30:] # get the last 30 days - -# first we'll do it the default way, with gaps on weekends -fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(8, 4)) -ax1.plot(r.date, r.adj_close, 'o-') -ax1.set_title("Default") -fig.autofmt_xdate() - -# next we'll write a custom formatter -N = len(r) -ind = np.arange(N) # the evenly spaced plot indices - - -def format_date(x, pos=None): - thisind = np.clip(int(x + 0.5), 0, N - 1) - return r.date[thisind].item().strftime('%Y-%m-%d') - - -ax2.plot(ind, r.adj_close, 'o-') -# Use automatic FuncFormatter creation -ax2.xaxis.set_major_formatter(format_date) -ax2.set_title("Custom tick formatter") -fig.autofmt_xdate() - -plt.show() - - -############################################################################# -# -# ------------ -# -# References -# """""""""" -# -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.pyplot.subplots -matplotlib.axis.Axis.set_major_formatter -matplotlib.cbook.get_sample_data -matplotlib.ticker.FuncFormatter diff --git a/examples/text_labels_and_annotations/demo_annotation_box.py b/examples/text_labels_and_annotations/demo_annotation_box.py index 51cf88d07b07..44dff1b87d31 100644 --- a/examples/text_labels_and_annotations/demo_annotation_box.py +++ b/examples/text_labels_and_annotations/demo_annotation_box.py @@ -1,13 +1,12 @@ """ =================== -Demo Annotation Box +AnnotationBbox demo =================== -The AnnotationBbox Artist creates an annotation using an OffsetBox. This -example demonstrates three different OffsetBoxes: TextArea, DrawingArea and -OffsetImage. AnnotationBbox gives more fine-grained control than using the axes -method annotate. - +`.AnnotationBbox` creates an annotation using an `.OffsetBox`, and +provides more fine-grained control than `.Axes.annotate`. This example +demonstrates the use of AnnotationBbox together with three different +OffsetBoxes: `.TextArea`, `.DrawingArea`, and `.OffsetImage`. """ import matplotlib.pyplot as plt @@ -104,20 +103,16 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods and classes is shown in this -# example: - -Circle -TextArea -DrawingArea -OffsetImage -AnnotationBbox -get_sample_data -plt.subplots -plt.imread -plt.show +# - `matplotlib.patches.Circle` +# - `matplotlib.offsetbox.TextArea` +# - `matplotlib.offsetbox.DrawingArea` +# - `matplotlib.offsetbox.OffsetImage` +# - `matplotlib.offsetbox.AnnotationBbox` +# - `matplotlib.cbook.get_sample_data` +# - `matplotlib.pyplot.subplots` +# - `matplotlib.pyplot.imread` diff --git a/examples/text_labels_and_annotations/demo_text_path.py b/examples/text_labels_and_annotations/demo_text_path.py index 460670d79dae..3d23e047fd90 100644 --- a/examples/text_labels_and_annotations/demo_text_path.py +++ b/examples/text_labels_and_annotations/demo_text_path.py @@ -46,8 +46,6 @@ def draw(self, renderer=None): if __name__ == "__main__": - usetex = plt.rcParams["text.usetex"] - fig, (ax1, ax2) = plt.subplots(2) # EXAMPLE 1 @@ -68,30 +66,28 @@ def draw(self, renderer=None): ax1.add_artist(ao) # another text - from matplotlib.patches import PathPatch - if usetex: - r = r"\mbox{textpath supports mathtext \& \TeX}" - else: - r = r"textpath supports mathtext & TeX" - - text_path = TextPath((0, 0), r, size=20, usetex=usetex) - - p1 = PathPatch(text_path, ec="w", lw=3, fc="w", alpha=0.9, - transform=IdentityTransform()) - p2 = PathPatch(text_path, ec="none", fc="k", - transform=IdentityTransform()) - - offsetbox2 = AuxTransformBox(IdentityTransform()) - offsetbox2.add_artist(p1) - offsetbox2.add_artist(p2) - - ab = AnnotationBbox(offsetbox2, (0.95, 0.05), - xycoords='axes fraction', - boxcoords="offset points", - box_alignment=(1., 0.), - frameon=False - ) - ax1.add_artist(ab) + for usetex, ypos, string in [ + (False, 0.25, r"textpath supports mathtext"), + (True, 0.05, r"textpath supports \TeX"), + ]: + text_path = TextPath((0, 0), string, size=20, usetex=usetex) + + p1 = PathPatch(text_path, ec="w", lw=3, fc="w", alpha=0.9, + transform=IdentityTransform()) + p2 = PathPatch(text_path, ec="none", fc="k", + transform=IdentityTransform()) + + offsetbox2 = AuxTransformBox(IdentityTransform()) + offsetbox2.add_artist(p1) + offsetbox2.add_artist(p2) + + ab = AnnotationBbox(offsetbox2, (0.95, ypos), + xycoords='axes fraction', + boxcoords="offset points", + box_alignment=(1., 0.), + frameon=False, + ) + ax1.add_artist(ab) ax1.imshow([[0, 1, 2], [1, 2, 3]], cmap=plt.cm.gist_gray_r, interpolation="bilinear", aspect="auto") @@ -100,32 +96,34 @@ def draw(self, renderer=None): arr = np.arange(256).reshape(1, 256) / 256 - if usetex: - s = (r"$\displaystyle\left[\sum_{n=1}^\infty" - r"\frac{-e^{i\pi}}{2^n}\right]$!") - else: - s = r"$\left[\sum_{n=1}^\infty\frac{-e^{i\pi}}{2^n}\right]$!" - text_path = TextPath((0, 0), s, size=40, usetex=usetex) - text_patch = PathClippedImagePatch(text_path, arr, ec="none", - transform=IdentityTransform()) - - shadow1 = Shadow(text_patch, 1, -1, fc="none", ec="0.6", lw=3) - shadow2 = Shadow(text_patch, 1, -1, fc="0.3", ec="none") - - # make offset box - offsetbox = AuxTransformBox(IdentityTransform()) - offsetbox.add_artist(shadow1) - offsetbox.add_artist(shadow2) - offsetbox.add_artist(text_patch) - - # place the anchored offset box using AnnotationBbox - ab = AnnotationBbox(offsetbox, (0.5, 0.5), - xycoords='data', - boxcoords="offset points", - box_alignment=(0.5, 0.5), - ) - - ax2.add_artist(ab) + for usetex, xpos, string in [ + (False, 0.25, + r"$\left[\sum_{n=1}^\infty\frac{-e^{i\pi}}{2^n}\right]$!"), + (True, 0.75, + r"$\displaystyle\left[\sum_{n=1}^\infty" + r"\frac{-e^{i\pi}}{2^n}\right]$!"), + ]: + text_path = TextPath((0, 0), string, size=40, usetex=usetex) + text_patch = PathClippedImagePatch(text_path, arr, ec="none", + transform=IdentityTransform()) + + shadow1 = Shadow(text_patch, 1, -1, fc="none", ec="0.6", lw=3) + shadow2 = Shadow(text_patch, 1, -1, fc="0.3", ec="none") + + # make offset box + offsetbox = AuxTransformBox(IdentityTransform()) + offsetbox.add_artist(shadow1) + offsetbox.add_artist(shadow2) + offsetbox.add_artist(text_patch) + + # place the anchored offset box using AnnotationBbox + ab = AnnotationBbox(offsetbox, (xpos, 0.5), + xycoords='data', + boxcoords="offset points", + box_alignment=(0.5, 0.5), + ) + + ax2.add_artist(ab) ax2.set_xlim(0, 1) ax2.set_ylim(0, 1) diff --git a/examples/text_labels_and_annotations/demo_text_rotation_mode.py b/examples/text_labels_and_annotations/demo_text_rotation_mode.py index bbe1639706ed..690dca6ad4ef 100644 --- a/examples/text_labels_and_annotations/demo_text_rotation_mode.py +++ b/examples/text_labels_and_annotations/demo_text_rotation_mode.py @@ -17,73 +17,70 @@ the bounding box of the rotated text. - ``rotation_mode='anchor'`` aligns the unrotated text and then rotates the text around the point of alignment. - """ + import matplotlib.pyplot as plt -from mpl_toolkits.axes_grid1.axes_grid import ImageGrid -def test_rotation_mode(fig, mode, subplot_location): +def test_rotation_mode(fig, mode): ha_list = ["left", "center", "right"] va_list = ["top", "center", "baseline", "bottom"] - grid = ImageGrid(fig, subplot_location, - nrows_ncols=(len(va_list), len(ha_list)), - share_all=True, aspect=True, cbar_mode=None) + axs = fig.subplots(len(va_list), len(ha_list), sharex=True, sharey=True, + subplot_kw=dict(aspect=1), + gridspec_kw=dict(hspace=0, wspace=0)) # labels and title - for ha, ax in zip(ha_list, grid.axes_row[-1]): - ax.axis["bottom"].label.set_text(ha) - for va, ax in zip(va_list, grid.axes_column[0]): - ax.axis["left"].label.set_text(va) - grid.axes_row[0][1].set_title(f"rotation_mode='{mode}'", size="large") + for ha, ax in zip(ha_list, axs[-1, :]): + ax.set_xlabel(ha) + for va, ax in zip(va_list, axs[:, 0]): + ax.set_ylabel(va) + axs[0, 1].set_title(f"rotation_mode='{mode}'", size="large") - if mode == "default": - kw = dict() - else: - kw = dict( - bbox=dict(boxstyle="square,pad=0.", ec="none", fc="C1", alpha=0.3)) + kw = ( + {} if mode == "default" else + {"bbox": dict(boxstyle="square,pad=0.", ec="none", fc="C1", alpha=0.3)} + ) + + texts = {} # use a different text alignment in each axes - texts = [] - for (va, ha), ax in zip([(x, y) for x in va_list for y in ha_list], grid): - # prepare axes layout - for axis in ax.axis.values(): - axis.toggle(ticks=False, ticklabels=False) - ax.axvline(0.5, color="skyblue", zorder=0) - ax.axhline(0.5, color="skyblue", zorder=0) - ax.plot(0.5, 0.5, color="C0", marker="o", zorder=1) - - # add text with rotation and alignment settings - tx = ax.text(0.5, 0.5, "Tpg", - size="x-large", rotation=40, - horizontalalignment=ha, verticalalignment=va, - rotation_mode=mode, **kw) - texts.append(tx) + for i, va in enumerate(va_list): + for j, ha in enumerate(ha_list): + ax = axs[i, j] + # prepare axes layout + ax.set(xticks=[], yticks=[]) + ax.axvline(0.5, color="skyblue", zorder=0) + ax.axhline(0.5, color="skyblue", zorder=0) + ax.plot(0.5, 0.5, color="C0", marker="o", zorder=1) + # add text with rotation and alignment settings + tx = ax.text(0.5, 0.5, "Tpg", + size="x-large", rotation=40, + horizontalalignment=ha, verticalalignment=va, + rotation_mode=mode, **kw) + texts[ax] = tx if mode == "default": # highlight bbox fig.canvas.draw() - for ax, tx in zip(grid, texts): - bb = tx.get_window_extent().transformed(ax.transData.inverted()) + for ax, text in texts.items(): + bb = text.get_window_extent().transformed(ax.transData.inverted()) rect = plt.Rectangle((bb.x0, bb.y0), bb.width, bb.height, facecolor="C1", alpha=0.3, zorder=2) ax.add_patch(rect) -fig = plt.figure(figsize=(8, 6)) -test_rotation_mode(fig, "default", 121) -test_rotation_mode(fig, "anchor", 122) +fig = plt.figure(figsize=(8, 5)) +subfigs = fig.subfigures(1, 2) +test_rotation_mode(subfigs[0], "default") +test_rotation_mode(subfigs[1], "anchor") plt.show() ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following method is shown in this example: - -import matplotlib -matplotlib.axes.Axes.text +# - `matplotlib.axes.Axes.text` / `matplotlib.pyplot.text` diff --git a/examples/text_labels_and_annotations/fancyarrow_demo.py b/examples/text_labels_and_annotations/fancyarrow_demo.py index d7ee56c33566..12bf39ee57b6 100644 --- a/examples/text_labels_and_annotations/fancyarrow_demo.py +++ b/examples/text_labels_and_annotations/fancyarrow_demo.py @@ -4,69 +4,60 @@ ================================ Overview of the arrow styles available in `~.Axes.annotate`. - """ + +import inspect +import re +import itertools + import matplotlib.patches as mpatches import matplotlib.pyplot as plt styles = mpatches.ArrowStyle.get_styles() - ncol = 2 nrow = (len(styles) + 1) // ncol -figheight = (nrow + 0.5) -fig = plt.figure(figsize=(4 * ncol / 1.5, figheight / 1.5)) -fontsize = 0.2 * 70 - - -ax = fig.add_axes([0, 0, 1, 1], frameon=False, aspect=1.) - -ax.set_xlim(0, 4 * ncol) -ax.set_ylim(0, figheight) - - -def to_texstring(s): - s = s.replace("<", r"$<$") - s = s.replace(">", r"$>$") - s = s.replace("|", r"$|$") - return s - - -for i, (stylename, styleclass) in enumerate(sorted(styles.items())): - x = 3.2 + (i // nrow) * 4 - y = (figheight - 0.7 - i % nrow) # /figheight - p = mpatches.Circle((x, y), 0.2) - ax.add_patch(p) - - ax.annotate(to_texstring(stylename), (x, y), - (x - 1.2, y), - ha="right", va="center", - size=fontsize, - arrowprops=dict(arrowstyle=stylename, - patchB=p, - shrinkA=5, - shrinkB=5, - fc="k", ec="k", - connectionstyle="arc3,rad=-0.05", - ), +axs = (plt.figure(figsize=(4 * ncol, 1 + nrow)) + .add_gridspec(1 + nrow, ncol, + wspace=.7, left=.1, right=.9, bottom=0, top=1).subplots()) +for ax in axs.flat: + ax.set_axis_off() +for ax in axs[0, :]: + ax.text(0, .5, "arrowstyle", + transform=ax.transAxes, size="large", color="tab:blue", + horizontalalignment="center", verticalalignment="center") + ax.text(.35, .5, "default parameters", + transform=ax.transAxes, + horizontalalignment="left", verticalalignment="center") +for ax, (stylename, stylecls) in zip(axs[1:, :].T.flat, styles.items()): + l, = ax.plot(.25, .5, "ok", transform=ax.transAxes) + ax.annotate(stylename, (.25, .5), (-0.1, .5), + xycoords="axes fraction", textcoords="axes fraction", + size="large", color="tab:blue", + horizontalalignment="center", verticalalignment="center", + arrowprops=dict( + arrowstyle=stylename, connectionstyle="arc3,rad=-0.05", + color="k", shrinkA=5, shrinkB=5, patchB=l, + ), bbox=dict(boxstyle="square", fc="w")) - -ax.xaxis.set_visible(False) -ax.yaxis.set_visible(False) + # wrap at every nth comma (n = 1 or 2, depending on text length) + s = str(inspect.signature(stylecls))[1:-1] + n = 2 if s.count(',') > 3 else 1 + ax.text(.35, .5, + re.sub(', ', lambda m, c=itertools.count(1): m.group() + if next(c) % n else '\n', s), + transform=ax.transAxes, + horizontalalignment="left", verticalalignment="center") plt.show() ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.patches -matplotlib.patches.ArrowStyle -matplotlib.patches.ArrowStyle.get_styles -matplotlib.axes.Axes.annotate +# - `matplotlib.patches` +# - `matplotlib.patches.ArrowStyle` +# - ``matplotlib.patches.ArrowStyle.get_styles`` +# - `matplotlib.axes.Axes.annotate` diff --git a/examples/text_labels_and_annotations/figlegend_demo.py b/examples/text_labels_and_annotations/figlegend_demo.py index 674b7627f09f..6fc31235f634 100644 --- a/examples/text_labels_and_annotations/figlegend_demo.py +++ b/examples/text_labels_and_annotations/figlegend_demo.py @@ -23,8 +23,8 @@ l3, = axs[1].plot(x, y3, color='tab:green') l4, = axs[1].plot(x, y4, color='tab:red', marker='^') -fig.legend((l1, l2), ('Line 1', 'Line 2'), 'upper left') -fig.legend((l3, l4), ('Line 3', 'Line 4'), 'upper right') +fig.legend((l1, l2), ('Line 1', 'Line 2'), loc='upper left') +fig.legend((l3, l4), ('Line 3', 'Line 4'), loc='upper right') plt.tight_layout() plt.show() diff --git a/examples/text_labels_and_annotations/font_family_rc.py b/examples/text_labels_and_annotations/font_family_rc.py new file mode 100644 index 000000000000..d840a8575920 --- /dev/null +++ b/examples/text_labels_and_annotations/font_family_rc.py @@ -0,0 +1,73 @@ +""" +=========================== +Configuring the font family +=========================== + +You can explicitly set which font family is picked up, either by specifying +family names of fonts installed on user's system, or generic-families +(e.g., 'serif', 'sans-serif', 'monospace', 'fantasy' or 'cursive'), +or a combination of both. +(see :doc:`font tutorial `) + +In the example below, we are overriding the default sans-serif generic family +to include a specific (Tahoma) font. (Note that the best way to achieve this +would simply be to prepend 'Tahoma' in 'font.family') + +The default family is set with the font.family rcparam, +e.g. :: + + rcParams['font.family'] = 'sans-serif' + +and for the font.family you set a list of font styles to try to find +in order:: + + rcParams['font.sans-serif'] = ['Tahoma', 'DejaVu Sans', + 'Lucida Grande', 'Verdana'] + +.. redirect-from:: /gallery/font_family_rc_sgskip + + + +The font font.family defaults are OS dependent and can be viewed with +""" +import matplotlib.pyplot as plt + +print(plt.rcParams["font.sans-serif"][0]) +print(plt.rcParams["font.monospace"][0]) + + +################################################################# +# Choose default sans-serif font + +def print_text(text): + fig, ax = plt.subplots(figsize=(6, 1), facecolor="#eefade") + ax.text(0.5, 0.5, text, ha='center', va='center', size=40) + ax.axis("off") + plt.show() + + +plt.rcParams["font.family"] = "sans-serif" +print_text("Hello World! 01") + + +################################################################# +# Choose sans-serif font and specify to it to "Nimbus Sans" + +plt.rcParams["font.family"] = "sans-serif" +plt.rcParams["font.sans-serif"] = ["Nimbus Sans"] +print_text("Hello World! 02") + + +################################################################## +# Choose default monospace font + +plt.rcParams["font.family"] = "monospace" +print_text("Hello World! 03") + + +################################################################### +# Choose monospace font and specify to it to "FreeMono" + +plt.rcParams["font.family"] = "monospace" +plt.rcParams["font.monospace"] = ["FreeMono"] +print_text("Hello World! 04") diff --git a/examples/text_labels_and_annotations/font_family_rc_sgskip.py b/examples/text_labels_and_annotations/font_family_rc_sgskip.py deleted file mode 100644 index 1eada97fb7e2..000000000000 --- a/examples/text_labels_and_annotations/font_family_rc_sgskip.py +++ /dev/null @@ -1,31 +0,0 @@ -""" -=========================== -Configuring the font family -=========================== - -You can explicitly set which font family is picked up for a given font -style (e.g., 'serif', 'sans-serif', or 'monospace'). - -In the example below, we only allow one font family (Tahoma) for the -sans-serif font style. The default family is set with the font.family rcparam, -e.g. :: - - rcParams['font.family'] = 'sans-serif' - -and for the font.family you set a list of font styles to try to find -in order:: - - rcParams['font.sans-serif'] = ['Tahoma', 'DejaVu Sans', - 'Lucida Grande', 'Verdana'] -""" - -from matplotlib import rcParams -rcParams['font.family'] = 'sans-serif' -rcParams['font.sans-serif'] = ['Tahoma'] -import matplotlib.pyplot as plt - -fig, ax = plt.subplots() -ax.plot([1, 2, 3], label='test') - -ax.legend() -plt.show() diff --git a/examples/text_labels_and_annotations/font_file.py b/examples/text_labels_and_annotations/font_file.py index 6f3071b87b61..7d808a122231 100644 --- a/examples/text_labels_and_annotations/font_file.py +++ b/examples/text_labels_and_annotations/font_file.py @@ -12,7 +12,7 @@ Matplotlib. For a more flexible solution, see -:doc:`/gallery/text_labels_and_annotations/font_family_rc_sgskip` and +:doc:`/gallery/text_labels_and_annotations/font_family_rc` and :doc:`/gallery/text_labels_and_annotations/fonts_demo`. """ @@ -32,13 +32,9 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.set_title +# - `matplotlib.axes.Axes.set_title` diff --git a/examples/text_labels_and_annotations/fonts_demo.py b/examples/text_labels_and_annotations/fonts_demo.py index 1aa38dcbcf38..112c6b0b4ce9 100644 --- a/examples/text_labels_and_annotations/fonts_demo.py +++ b/examples/text_labels_and_annotations/fonts_demo.py @@ -5,95 +5,66 @@ Set font properties using setters. -See :doc:`fonts_demo_kw` to achieve the same effect using kwargs. +See :doc:`fonts_demo_kw` to achieve the same effect using keyword arguments. """ from matplotlib.font_manager import FontProperties import matplotlib.pyplot as plt -font0 = FontProperties() +fig = plt.figure() alignment = {'horizontalalignment': 'center', 'verticalalignment': 'baseline'} -# Show family options - -families = ['serif', 'sans-serif', 'cursive', 'fantasy', 'monospace'] - -font1 = font0.copy() -font1.set_size('large') - -t = plt.figtext(0.1, 0.9, 'family', fontproperties=font1, **alignment) - yp = [0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2] +heading_font = FontProperties(size='large') +# Show family options +fig.text(0.1, 0.9, 'family', fontproperties=heading_font, **alignment) +families = ['serif', 'sans-serif', 'cursive', 'fantasy', 'monospace'] for k, family in enumerate(families): - font = font0.copy() + font = FontProperties() font.set_family(family) - t = plt.figtext(0.1, yp[k], family, fontproperties=font, **alignment) + fig.text(0.1, yp[k], family, fontproperties=font, **alignment) # Show style options - styles = ['normal', 'italic', 'oblique'] - -t = plt.figtext(0.3, 0.9, 'style', fontproperties=font1, **alignment) - +fig.text(0.3, 0.9, 'style', fontproperties=heading_font, **alignment) for k, style in enumerate(styles): - font = font0.copy() + font = FontProperties() font.set_family('sans-serif') font.set_style(style) - t = plt.figtext(0.3, yp[k], style, fontproperties=font, **alignment) + fig.text(0.3, yp[k], style, fontproperties=font, **alignment) # Show variant options - variants = ['normal', 'small-caps'] - -t = plt.figtext(0.5, 0.9, 'variant', fontproperties=font1, **alignment) - +fig.text(0.5, 0.9, 'variant', fontproperties=heading_font, **alignment) for k, variant in enumerate(variants): - font = font0.copy() + font = FontProperties() font.set_family('serif') font.set_variant(variant) - t = plt.figtext(0.5, yp[k], variant, fontproperties=font, **alignment) + fig.text(0.5, yp[k], variant, fontproperties=font, **alignment) # Show weight options - weights = ['light', 'normal', 'medium', 'semibold', 'bold', 'heavy', 'black'] - -t = plt.figtext(0.7, 0.9, 'weight', fontproperties=font1, **alignment) - +fig.text(0.7, 0.9, 'weight', fontproperties=heading_font, **alignment) for k, weight in enumerate(weights): - font = font0.copy() + font = FontProperties() font.set_weight(weight) - t = plt.figtext(0.7, yp[k], weight, fontproperties=font, **alignment) + fig.text(0.7, yp[k], weight, fontproperties=font, **alignment) # Show size options - -sizes = ['xx-small', 'x-small', 'small', 'medium', 'large', - 'x-large', 'xx-large'] - -t = plt.figtext(0.9, 0.9, 'size', fontproperties=font1, **alignment) - +sizes = [ + 'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large'] +fig.text(0.9, 0.9, 'size', fontproperties=heading_font, **alignment) for k, size in enumerate(sizes): - font = font0.copy() + font = FontProperties() font.set_size(size) - t = plt.figtext(0.9, yp[k], size, fontproperties=font, **alignment) + fig.text(0.9, yp[k], size, fontproperties=font, **alignment) # Show bold italic - -font = font0.copy() -font.set_style('italic') -font.set_weight('bold') -font.set_size('x-small') -t = plt.figtext(0.3, 0.1, 'bold italic', fontproperties=font, **alignment) - -font = font0.copy() -font.set_style('italic') -font.set_weight('bold') -font.set_size('medium') -t = plt.figtext(0.3, 0.2, 'bold italic', fontproperties=font, **alignment) - -font = font0.copy() -font.set_style('italic') -font.set_weight('bold') -font.set_size('x-large') -t = plt.figtext(-0.4, 0.3, 'bold italic', fontproperties=font, **alignment) +font = FontProperties(style='italic', weight='bold', size='x-small') +fig.text(0.3, 0.1, 'bold italic', fontproperties=font, **alignment) +font = FontProperties(style='italic', weight='bold', size='medium') +fig.text(0.3, 0.2, 'bold italic', fontproperties=font, **alignment) +font = FontProperties(style='italic', weight='bold', size='x-large') +fig.text(0.3, 0.3, 'bold italic', fontproperties=font, **alignment) plt.show() diff --git a/examples/text_labels_and_annotations/fonts_demo_kw.py b/examples/text_labels_and_annotations/fonts_demo_kw.py index afbdf1504431..ca19222f878d 100644 --- a/examples/text_labels_and_annotations/fonts_demo_kw.py +++ b/examples/text_labels_and_annotations/fonts_demo_kw.py @@ -1,76 +1,56 @@ """ -=================== -Fonts demo (kwargs) -=================== +============================== +Fonts demo (keyword arguments) +============================== -Set font properties using kwargs. +Set font properties using keyword arguments. See :doc:`fonts_demo` to achieve the same effect using setters. """ import matplotlib.pyplot as plt +fig = plt.figure() alignment = {'horizontalalignment': 'center', 'verticalalignment': 'baseline'} +yp = [0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2] # Show family options - +fig.text(0.1, 0.9, 'family', size='large', **alignment) families = ['serif', 'sans-serif', 'cursive', 'fantasy', 'monospace'] - -t = plt.figtext(0.1, 0.9, 'family', size='large', **alignment) - -yp = [0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2] - for k, family in enumerate(families): - t = plt.figtext(0.1, yp[k], family, family=family, **alignment) + fig.text(0.1, yp[k], family, family=family, **alignment) # Show style options - +fig.text(0.3, 0.9, 'style', **alignment) styles = ['normal', 'italic', 'oblique'] - -t = plt.figtext(0.3, 0.9, 'style', **alignment) - for k, style in enumerate(styles): - t = plt.figtext(0.3, yp[k], style, family='sans-serif', style=style, - **alignment) + fig.text(0.3, yp[k], style, family='sans-serif', style=style, **alignment) # Show variant options - +fig.text(0.5, 0.9, 'variant', **alignment) variants = ['normal', 'small-caps'] - -t = plt.figtext(0.5, 0.9, 'variant', **alignment) - for k, variant in enumerate(variants): - t = plt.figtext(0.5, yp[k], variant, family='serif', variant=variant, - **alignment) + fig.text(0.5, yp[k], variant, family='serif', variant=variant, **alignment) # Show weight options - +fig.text(0.7, 0.9, 'weight', **alignment) weights = ['light', 'normal', 'medium', 'semibold', 'bold', 'heavy', 'black'] - -t = plt.figtext(0.7, 0.9, 'weight', **alignment) - for k, weight in enumerate(weights): - t = plt.figtext(0.7, yp[k], weight, weight=weight, **alignment) + fig.text(0.7, yp[k], weight, weight=weight, **alignment) # Show size options - -sizes = ['xx-small', 'x-small', 'small', 'medium', 'large', - 'x-large', 'xx-large'] - -t = plt.figtext(0.9, 0.9, 'size', **alignment) - +fig.text(0.9, 0.9, 'size', **alignment) +sizes = [ + 'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large'] for k, size in enumerate(sizes): - t = plt.figtext(0.9, yp[k], size, size=size, **alignment) + fig.text(0.9, yp[k], size, size=size, **alignment) # Show bold italic -t = plt.figtext(0.3, 0.1, 'bold italic', style='italic', - weight='bold', size='x-small', - **alignment) -t = plt.figtext(0.3, 0.2, 'bold italic', - style='italic', weight='bold', size='medium', - **alignment) -t = plt.figtext(0.3, 0.3, 'bold italic', - style='italic', weight='bold', size='x-large', - **alignment) +fig.text(0.3, 0.1, 'bold italic', + style='italic', weight='bold', size='x-small', **alignment) +fig.text(0.3, 0.2, 'bold italic', + style='italic', weight='bold', size='medium', **alignment) +fig.text(0.3, 0.3, 'bold italic', + style='italic', weight='bold', size='x-large', **alignment) plt.show() diff --git a/examples/text_labels_and_annotations/label_subplots.py b/examples/text_labels_and_annotations/label_subplots.py new file mode 100644 index 000000000000..974b775a9a97 --- /dev/null +++ b/examples/text_labels_and_annotations/label_subplots.py @@ -0,0 +1,70 @@ +""" +================== +Labelling subplots +================== + +Labelling subplots is relatively straightforward, and varies, +so Matplotlib does not have a general method for doing this. + +Simplest is putting the label inside the axes. Note, here +we use `.pyplot.subplot_mosaic`, and use the subplot labels +as keys for the subplots, which is a nice convenience. However, +the same method works with `.pyplot.subplots` or keys that are +different than what you want to label the subplot with. +""" + +import matplotlib.pyplot as plt +import matplotlib.transforms as mtransforms + +fig, axs = plt.subplot_mosaic([['a)', 'c)'], ['b)', 'c)'], ['d)', 'd)']], + constrained_layout=True) + +for label, ax in axs.items(): + # label physical distance in and down: + trans = mtransforms.ScaledTranslation(10/72, -5/72, fig.dpi_scale_trans) + ax.text(0.0, 1.0, label, transform=ax.transAxes + trans, + fontsize='medium', verticalalignment='top', fontfamily='serif', + bbox=dict(facecolor='0.7', edgecolor='none', pad=3.0)) + +plt.show() + +############################################################################## +# We may prefer the labels outside the axes, but still aligned +# with each other, in which case we use a slightly different transform: + +fig, axs = plt.subplot_mosaic([['a)', 'c)'], ['b)', 'c)'], ['d)', 'd)']], + constrained_layout=True) + +for label, ax in axs.items(): + # label physical distance to the left and up: + trans = mtransforms.ScaledTranslation(-20/72, 7/72, fig.dpi_scale_trans) + ax.text(0.0, 1.0, label, transform=ax.transAxes + trans, + fontsize='medium', va='bottom', fontfamily='serif') + +plt.show() + +############################################################################## +# If we want it aligned with the title, either incorporate in the title or +# use the *loc* keyword argument: + +fig, axs = plt.subplot_mosaic([['a)', 'c)'], ['b)', 'c)'], ['d)', 'd)']], + constrained_layout=True) + +for label, ax in axs.items(): + ax.set_title('Normal Title', fontstyle='italic') + ax.set_title(label, fontfamily='serif', loc='left', fontsize='medium') + +plt.show() + +############################################################################# +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.figure.Figure.subplot_mosaic` / +# `matplotlib.pyplot.subplot_mosaic` +# - `matplotlib.axes.Axes.set_title` +# - `matplotlib.axes.Axes.text` +# - `matplotlib.transforms.ScaledTranslation` diff --git a/examples/text_labels_and_annotations/legend.py b/examples/text_labels_and_annotations/legend.py index 7e6162a51c25..33d8af3204e4 100644 --- a/examples/text_labels_and_annotations/legend.py +++ b/examples/text_labels_and_annotations/legend.py @@ -30,16 +30,10 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.plot -matplotlib.pyplot.plot -matplotlib.axes.Axes.legend -matplotlib.pyplot.legend +# - `matplotlib.axes.Axes.plot` / `matplotlib.pyplot.plot` +# - `matplotlib.axes.Axes.legend` / `matplotlib.pyplot.legend` diff --git a/examples/text_labels_and_annotations/line_with_text.py b/examples/text_labels_and_annotations/line_with_text.py index ffacab587f92..51f1a27d98ec 100644 --- a/examples/text_labels_and_annotations/line_with_text.py +++ b/examples/text_labels_and_annotations/line_with_text.py @@ -55,7 +55,6 @@ def draw(self, renderer): fig, ax = plt.subplots() x, y = np.random.rand(2, 20) line = MyLine(x, y, mfc='red', ms=12, label='line label') -#line.text.set_text('line label') line.text.set_color('red') line.text.set_fontsize(16) @@ -65,27 +64,23 @@ def draw(self, renderer): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.lines -matplotlib.lines.Line2D -matplotlib.lines.Line2D.set_data -matplotlib.artist -matplotlib.artist.Artist -matplotlib.artist.Artist.draw -matplotlib.artist.Artist.set_transform -matplotlib.text -matplotlib.text.Text -matplotlib.text.Text.set_color -matplotlib.text.Text.set_fontsize -matplotlib.text.Text.set_position -matplotlib.axes.Axes.add_line -matplotlib.transforms -matplotlib.transforms.Affine2D +# - `matplotlib.lines` +# - `matplotlib.lines.Line2D` +# - `matplotlib.lines.Line2D.set_data` +# - `matplotlib.artist` +# - `matplotlib.artist.Artist` +# - `matplotlib.artist.Artist.draw` +# - `matplotlib.artist.Artist.set_transform` +# - `matplotlib.text` +# - `matplotlib.text.Text` +# - `matplotlib.text.Text.set_color` +# - `matplotlib.text.Text.set_fontsize` +# - `matplotlib.text.Text.set_position` +# - `matplotlib.axes.Axes.add_line` +# - `matplotlib.transforms` +# - `matplotlib.transforms.Affine2D` diff --git a/examples/text_labels_and_annotations/mathtext_asarray.py b/examples/text_labels_and_annotations/mathtext_asarray.py index b40ba3ce18e4..224634046c68 100644 --- a/examples/text_labels_and_annotations/mathtext_asarray.py +++ b/examples/text_labels_and_annotations/mathtext_asarray.py @@ -21,10 +21,11 @@ def text_to_rgba(s, *, dpi, **kwargs): # (If desired, one can also directly save the image to the filesystem.) fig = Figure(facecolor="none") fig.text(0, 0, s, **kwargs) - buf = BytesIO() - fig.savefig(buf, dpi=dpi, format="png", bbox_inches="tight", pad_inches=0) - buf.seek(0) - rgba = plt.imread(buf) + with BytesIO() as buf: + fig.savefig(buf, dpi=dpi, format="png", bbox_inches="tight", + pad_inches=0) + buf.seek(0) + rgba = plt.imread(buf) return rgba @@ -47,16 +48,12 @@ def text_to_rgba(s, *, dpi, **kwargs): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.figure.Figure.figimage -matplotlib.figure.Figure.text -matplotlib.transforms.IdentityTransform -matplotlib.image.imread +# - `matplotlib.figure.Figure.figimage` +# - `matplotlib.figure.Figure.text` +# - `matplotlib.transforms.IdentityTransform` +# - `matplotlib.image.imread` diff --git a/examples/text_labels_and_annotations/mathtext_examples.py b/examples/text_labels_and_annotations/mathtext_examples.py index 6d8be49599cf..838b68bb2ca2 100644 --- a/examples/text_labels_and_annotations/mathtext_examples.py +++ b/examples/text_labels_and_annotations/mathtext_examples.py @@ -5,121 +5,116 @@ Selected features of Matplotlib's math rendering engine. """ -import matplotlib.pyplot as plt +import re import subprocess import sys -import re -# Selection of features following "Writing mathematical expressions" tutorial -mathtext_titles = { - 0: "Header demo", - 1: "Subscripts and superscripts", - 2: "Fractions, binomials and stacked numbers", - 3: "Radicals", - 4: "Fonts", - 5: "Accents", - 6: "Greek, Hebrew", - 7: "Delimiters, functions and Symbols"} -n_lines = len(mathtext_titles) +import matplotlib.pyplot as plt -# Randomly picked examples -mathext_demos = { - 0: r"$W^{3\beta}_{\delta_1 \rho_1 \sigma_2} = " - r"U^{3\beta}_{\delta_1 \rho_1} + \frac{1}{8 \pi 2} " - r"\int^{\alpha_2}_{\alpha_2} d \alpha^\prime_2 \left[\frac{ " - r"U^{2\beta}_{\delta_1 \rho_1} - \alpha^\prime_2U^{1\beta}_" - r"{\rho_1 \sigma_2} }{U^{0\beta}_{\rho_1 \sigma_2}}\right]$", - 1: r"$\alpha_i > \beta_i,\ " - r"\alpha_{i+1}^j = {\rm sin}(2\pi f_j t_i) e^{-5 t_i/\tau},\ " - r"\ldots$", +# Selection of features following "Writing mathematical expressions" tutorial, +# with randomly picked examples. +mathtext_demos = { + "Header demo": + r"$W^{3\beta}_{\delta_1 \rho_1 \sigma_2} = " + r"U^{3\beta}_{\delta_1 \rho_1} + \frac{1}{8 \pi 2} " + r"\int^{\alpha_2}_{\alpha_2} d \alpha^\prime_2 \left[\frac{ " + r"U^{2\beta}_{\delta_1 \rho_1} - \alpha^\prime_2U^{1\beta}_" + r"{\rho_1 \sigma_2} }{U^{0\beta}_{\rho_1 \sigma_2}}\right]$", - 2: r"$\frac{3}{4},\ \binom{3}{4},\ \genfrac{}{}{0}{}{3}{4},\ " - r"\left(\frac{5 - \frac{1}{x}}{4}\right),\ \ldots$", + "Subscripts and superscripts": + r"$\alpha_i > \beta_i,\ " + r"\alpha_{i+1}^j = {\rm sin}(2\pi f_j t_i) e^{-5 t_i/\tau},\ " + r"\ldots$", - 3: r"$\sqrt{2},\ \sqrt[3]{x},\ \ldots$", + "Fractions, binomials and stacked numbers": + r"$\frac{3}{4},\ \binom{3}{4},\ \genfrac{}{}{0}{}{3}{4},\ " + r"\left(\frac{5 - \frac{1}{x}}{4}\right),\ \ldots$", - 4: r"$\mathrm{Roman}\ , \ \mathit{Italic}\ , \ \mathtt{Typewriter} \ " - r"\mathrm{or}\ \mathcal{CALLIGRAPHY}$", + "Radicals": + r"$\sqrt{2},\ \sqrt[3]{x},\ \ldots$", - 5: r"$\acute a,\ \bar a,\ \breve a,\ \dot a,\ \ddot a, \ \grave a, \ " - r"\hat a,\ \tilde a,\ \vec a,\ \widehat{xyz},\ \widetilde{xyz},\ " - r"\ldots$", + "Fonts": + r"$\mathrm{Roman}\ , \ \mathit{Italic}\ , \ \mathtt{Typewriter} \ " + r"\mathrm{or}\ \mathcal{CALLIGRAPHY}$", - 6: r"$\alpha,\ \beta,\ \chi,\ \delta,\ \lambda,\ \mu,\ " - r"\Delta,\ \Gamma,\ \Omega,\ \Phi,\ \Pi,\ \Upsilon,\ \nabla,\ " - r"\aleph,\ \beth,\ \daleth,\ \gimel,\ \ldots$", + "Accents": + r"$\acute a,\ \bar a,\ \breve a,\ \dot a,\ \ddot a, \ \grave a, \ " + r"\hat a,\ \tilde a,\ \vec a,\ \widehat{xyz},\ \widetilde{xyz},\ " + r"\ldots$", - 7: r"$\coprod,\ \int,\ \oint,\ \prod,\ \sum,\ " - r"\log,\ \sin,\ \approx,\ \oplus,\ \star,\ \varpropto,\ " - r"\infty,\ \partial,\ \Re,\ \leftrightsquigarrow, \ \ldots$"} + "Greek, Hebrew": + r"$\alpha,\ \beta,\ \chi,\ \delta,\ \lambda,\ \mu,\ " + r"\Delta,\ \Gamma,\ \Omega,\ \Phi,\ \Pi,\ \Upsilon,\ \nabla,\ " + r"\aleph,\ \beth,\ \daleth,\ \gimel,\ \ldots$", + + "Delimiters, functions and Symbols": + r"$\coprod,\ \int,\ \oint,\ \prod,\ \sum,\ " + r"\log,\ \sin,\ \approx,\ \oplus,\ \star,\ \varpropto,\ " + r"\infty,\ \partial,\ \Re,\ \leftrightsquigarrow, \ \ldots$", +} +n_lines = len(mathtext_demos) def doall(): # Colors used in Matplotlib online documentation. - mpl_blue_rvb = (191. / 255., 209. / 256., 212. / 255.) - mpl_orange_rvb = (202. / 255., 121. / 256., 0. / 255.) - mpl_grey_rvb = (51. / 255., 51. / 255., 51. / 255.) + mpl_grey_rgb = (51 / 255, 51 / 255, 51 / 255) # Creating figure and axis. - plt.figure(figsize=(6, 7)) - plt.axes([0.01, 0.01, 0.98, 0.90], facecolor="white", frameon=True) - plt.gca().set_xlim(0., 1.) - plt.gca().set_ylim(0., 1.) - plt.gca().set_title("Matplotlib's math rendering engine", - color=mpl_grey_rvb, fontsize=14, weight='bold') - plt.gca().set_xticklabels("", visible=False) - plt.gca().set_yticklabels("", visible=False) + fig = plt.figure(figsize=(7, 7)) + ax = fig.add_axes([0.01, 0.01, 0.98, 0.90], + facecolor="white", frameon=True) + ax.set_xlim(0, 1) + ax.set_ylim(0, 1) + ax.set_title("Matplotlib's math rendering engine", + color=mpl_grey_rgb, fontsize=14, weight='bold') + ax.set_xticks([]) + ax.set_yticks([]) # Gap between lines in axes coords line_axesfrac = 1 / n_lines - # Plotting header demonstration formula - full_demo = mathext_demos[0] - plt.annotate(full_demo, - xy=(0.5, 1. - 0.59 * line_axesfrac), - color=mpl_orange_rvb, ha='center', fontsize=20) + # Plot header demonstration formula. + full_demo = mathtext_demos['Header demo'] + ax.annotate(full_demo, + xy=(0.5, 1. - 0.59 * line_axesfrac), + color='tab:orange', ha='center', fontsize=20) + + # Plot feature demonstration formulae. + for i_line, (title, demo) in enumerate(mathtext_demos.items()): + print(i_line, demo) + if i_line == 0: + continue - # Plotting features demonstration formulae - for i_line in range(1, n_lines): baseline = 1 - i_line * line_axesfrac baseline_next = baseline - line_axesfrac - title = mathtext_titles[i_line] + ":" - fill_color = ['white', mpl_blue_rvb][i_line % 2] - plt.fill_between([0., 1.], [baseline, baseline], - [baseline_next, baseline_next], - color=fill_color, alpha=0.5) - plt.annotate(title, - xy=(0.07, baseline - 0.3 * line_axesfrac), - color=mpl_grey_rvb, weight='bold') - demo = mathext_demos[i_line] - plt.annotate(demo, - xy=(0.05, baseline - 0.75 * line_axesfrac), - color=mpl_grey_rvb, fontsize=16) - - for i in range(n_lines): - s = mathext_demos[i] - print(i, s) + fill_color = ['white', 'tab:blue'][i_line % 2] + ax.axhspan(baseline, baseline_next, color=fill_color, alpha=0.2) + ax.annotate(f'{title}:', + xy=(0.06, baseline - 0.3 * line_axesfrac), + color=mpl_grey_rgb, weight='bold') + ax.annotate(demo, + xy=(0.04, baseline - 0.75 * line_axesfrac), + color=mpl_grey_rgb, fontsize=16) + plt.show() if '--latex' in sys.argv: # Run: python mathtext_examples.py --latex # Need amsmath and amssymb packages. - fd = open("mathtext_examples.ltx", "w") - fd.write("\\documentclass{article}\n") - fd.write("\\usepackage{amsmath, amssymb}\n") - fd.write("\\begin{document}\n") - fd.write("\\begin{enumerate}\n") - - for i in range(n_lines): - s = mathext_demos[i] - s = re.sub(r"(? np.timedelta64(1, 'D')) +for gap in r[['date', 'adj_close']][np.stack((gaps, gaps + 1)).T]: + ax1.plot(gap.date, gap.adj_close, 'w--', lw=2) +ax1.legend(handles=[ml.Line2D([], [], ls='--', label='Gaps in daily data')]) + +ax1.set_title("Plot y at x Coordinates") +ax1.xaxis.set_major_locator(DayLocator()) +ax1.xaxis.set_major_formatter(DateFormatter('%a')) + + +# Next we'll write a custom index formatter. Below we will plot +# the data against an index that goes from 0, 1, ... len(data). Instead of +# formatting the tick marks as integers, we format as times. +def format_date(x, _): + try: + # convert datetime64 to datetime, and use datetime's strftime: + return r.date[round(x)].item().strftime('%a') + except IndexError: + pass + +# Create an index plot (x defaults to range(len(y)) if omitted) +ax2.plot(r.adj_close, 'o-') + +ax2.set_title("Plot y at Index Coordinates Using Custom Formatter") +ax2.xaxis.set_major_formatter(format_date) # internally creates FuncFormatter + +############################################################################# +# Instead of passing a function into `.Axis.set_major_formatter` you can use +# any other callable, e.g. an instance of a class that implements __call__: + + +class MyFormatter(Formatter): + def __init__(self, dates, fmt='%a'): + self.dates = dates + self.fmt = fmt + + def __call__(self, x, pos=0): + """Return the label for time x at position pos.""" + try: + return self.dates[round(x)].item().strftime(self.fmt) + except IndexError: + pass + + +ax2.xaxis.set_major_formatter(MyFormatter(r.date, '%a')) diff --git a/examples/ticks_and_spines/date_precision_and_epochs.py b/examples/ticks/date_precision_and_epochs.py similarity index 92% rename from examples/ticks_and_spines/date_precision_and_epochs.py rename to examples/ticks/date_precision_and_epochs.py index 9526adba55e7..c50bbd234331 100644 --- a/examples/ticks_and_spines/date_precision_and_epochs.py +++ b/examples/ticks/date_precision_and_epochs.py @@ -20,7 +20,6 @@ import datetime import numpy as np -import matplotlib import matplotlib.pyplot as plt import matplotlib.dates as mdates @@ -67,7 +66,7 @@ def _reset_epoch_for_tutorial(): ############################################################################# # If a user wants to use modern dates at microsecond precision, they -# can change the epoch using `~.set_epoch`. However, the epoch has to be +# can change the epoch using `.set_epoch`. However, the epoch has to be # set before any date operations to prevent confusion between different # epochs. Trying to change the epoch later will raise a `RuntimeError`. @@ -132,7 +131,7 @@ def _reset_epoch_for_tutorial(): fig, ax = plt.subplots(constrained_layout=True) ax.plot(xold, y) ax.set_title('Epoch: ' + mdates.get_epoch()) -plt.setp(ax.xaxis.get_majorticklabels(), rotation=40) +ax.xaxis.set_tick_params(rotation=40) plt.show() ############################################################################# @@ -141,20 +140,18 @@ def _reset_epoch_for_tutorial(): fig, ax = plt.subplots(constrained_layout=True) ax.plot(x, y) ax.set_title('Epoch: ' + mdates.get_epoch()) -plt.setp(ax.xaxis.get_majorticklabels(), rotation=40) +ax.xaxis.set_tick_params(rotation=40) plt.show() _reset_epoch_for_tutorial() # Don't do this. Just for this tutorial. ############################################################################# -# ------------ # -# References -# """""""""" +# .. admonition:: References # -# The use of the following functions, methods and classes is shown -# in this example: - -matplotlib.dates.num2date -matplotlib.dates.date2num -matplotlib.dates.set_epoch +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.dates.num2date` +# - `matplotlib.dates.date2num` +# - `matplotlib.dates.set_epoch` diff --git a/examples/pyplots/dollar_ticks.py b/examples/ticks/dollar_ticks.py similarity index 60% rename from examples/pyplots/dollar_ticks.py rename to examples/ticks/dollar_ticks.py index fb7032c052a6..694e613db08d 100644 --- a/examples/pyplots/dollar_ticks.py +++ b/examples/ticks/dollar_ticks.py @@ -4,6 +4,8 @@ ============ Use a `~.ticker.FormatStrFormatter` to prepend dollar signs on y axis labels. + +.. redirect-from:: /gallery/pyplots/dollar_ticks """ import numpy as np import matplotlib.pyplot as plt @@ -25,17 +27,13 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.pyplot.subplots -matplotlib.axis.Axis.set_major_formatter -matplotlib.axis.Axis.set_tick_params -matplotlib.axis.Tick -matplotlib.ticker.StrMethodFormatter +# - `matplotlib.pyplot.subplots` +# - `matplotlib.axis.Axis.set_major_formatter` +# - `matplotlib.axis.Axis.set_tick_params` +# - `matplotlib.axis.Tick` +# - `matplotlib.ticker.StrMethodFormatter` diff --git a/examples/ticks/fig_axes_customize_simple.py b/examples/ticks/fig_axes_customize_simple.py new file mode 100644 index 000000000000..74742f718939 --- /dev/null +++ b/examples/ticks/fig_axes_customize_simple.py @@ -0,0 +1,45 @@ +""" +========================= +Fig Axes Customize Simple +========================= + +Customize the background, labels and ticks of a simple plot. + +.. redirect-from:: /gallery/pyplots/fig_axes_customize_simple +""" + +import matplotlib.pyplot as plt + +############################################################################### +# `.pyplot.figure` creates a `matplotlib.figure.Figure` instance. + +fig = plt.figure() +rect = fig.patch # a rectangle instance +rect.set_facecolor('lightgoldenrodyellow') + +ax1 = fig.add_axes([0.1, 0.3, 0.4, 0.4]) +rect = ax1.patch +rect.set_facecolor('lightslategray') + +ax1.tick_params(axis='x', labelcolor='tab:red', labelrotation=45, labelsize=16) +ax1.tick_params(axis='y', color='tab:green', size=25, width=3) + +plt.show() + +############################################################################# +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.axis.Axis.get_ticklabels` +# - `matplotlib.axis.Axis.get_ticklines` +# - `matplotlib.text.Text.set_rotation` +# - `matplotlib.text.Text.set_fontsize` +# - `matplotlib.text.Text.set_color` +# - `matplotlib.lines.Line2D` +# - `matplotlib.lines.Line2D.set_markeredgecolor` +# - `matplotlib.lines.Line2D.set_markersize` +# - `matplotlib.lines.Line2D.set_markeredgewidth` +# - `matplotlib.patches.Patch.set_facecolor` diff --git a/examples/ticks_and_spines/major_minor_demo.py b/examples/ticks/major_minor_demo.py similarity index 86% rename from examples/ticks_and_spines/major_minor_demo.py rename to examples/ticks/major_minor_demo.py index 34bc773a839a..26d9a2ce5844 100644 --- a/examples/ticks_and_spines/major_minor_demo.py +++ b/examples/ticks/major_minor_demo.py @@ -82,19 +82,15 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.pyplot.subplots -matplotlib.axis.Axis.set_major_formatter -matplotlib.axis.Axis.set_major_locator -matplotlib.axis.Axis.set_minor_locator -matplotlib.ticker.AutoMinorLocator -matplotlib.ticker.MultipleLocator -matplotlib.ticker.StrMethodFormatter +# - `matplotlib.pyplot.subplots` +# - `matplotlib.axis.Axis.set_major_formatter` +# - `matplotlib.axis.Axis.set_major_locator` +# - `matplotlib.axis.Axis.set_minor_locator` +# - `matplotlib.ticker.AutoMinorLocator` +# - `matplotlib.ticker.MultipleLocator` +# - `matplotlib.ticker.StrMethodFormatter` diff --git a/examples/ticks_and_spines/scalarformatter.py b/examples/ticks/scalarformatter.py similarity index 100% rename from examples/ticks_and_spines/scalarformatter.py rename to examples/ticks/scalarformatter.py diff --git a/examples/ticks_and_spines/tick-formatters.py b/examples/ticks/tick-formatters.py similarity index 75% rename from examples/ticks_and_spines/tick-formatters.py rename to examples/ticks/tick-formatters.py index 3693b51a4b1a..ac118cbfce03 100644 --- a/examples/ticks_and_spines/tick-formatters.py +++ b/examples/ticks/tick-formatters.py @@ -7,6 +7,7 @@ is formatted as a string. This example illustrates the usage and effect of the most common formatters. + """ import matplotlib.pyplot as plt @@ -34,12 +35,13 @@ def setup(ax, title): fontsize=14, fontname='Monospace', color='tab:blue') +############################################################################# # Tick formatters can be set in one of two ways, either by passing a ``str`` # or function to `~.Axis.set_major_formatter` or `~.Axis.set_minor_formatter`, # or by creating an instance of one of the various `~.ticker.Formatter` classes # and providing that to `~.Axis.set_major_formatter` or # `~.Axis.set_minor_formatter`. - +# # The first two examples directly pass a ``str`` or function. fig0, axs0 = plt.subplots(2, 1, figsize=(8, 2)) @@ -53,14 +55,15 @@ def setup(ax, title): # A function can also be used directly as a formatter. The function must take # two arguments: ``x`` for the tick value and ``pos`` for the tick position, -# and must return a ``str`` This creates a FuncFormatter automatically. +# and must return a ``str``. This creates a FuncFormatter automatically. setup(axs0[1], title="lambda x, pos: str(x-5)") axs0[1].xaxis.set_major_formatter(lambda x, pos: str(x-5)) fig0.tight_layout() -# The remaining examples use Formatter objects. +############################################################################# +# The remaining examples use `.Formatter` objects. fig1, axs1 = plt.subplots(7, 1, figsize=(8, 6)) fig1.suptitle('Formatter Object Formatting') @@ -80,7 +83,7 @@ def major_formatter(x, pos): return f'[{x:.2f}]' -setup(axs1[2], title='FuncFormatter("[{:.2f}]".format') +setup(axs1[2], title='FuncFormatter("[{:.2f}]".format)') axs1[2].xaxis.set_major_formatter(major_formatter) # Fixed formatter @@ -110,29 +113,25 @@ def major_formatter(x, pos): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.pyplot.subplots -matplotlib.axes.Axes.text -matplotlib.axis.Axis.set_major_formatter -matplotlib.axis.Axis.set_major_locator -matplotlib.axis.Axis.set_minor_locator -matplotlib.axis.XAxis.set_ticks_position -matplotlib.axis.YAxis.set_ticks_position -matplotlib.ticker.FixedFormatter -matplotlib.ticker.FixedLocator -matplotlib.ticker.FormatStrFormatter -matplotlib.ticker.FuncFormatter -matplotlib.ticker.MultipleLocator -matplotlib.ticker.NullFormatter -matplotlib.ticker.NullLocator -matplotlib.ticker.PercentFormatter -matplotlib.ticker.ScalarFormatter -matplotlib.ticker.StrMethodFormatter +# - `matplotlib.pyplot.subplots` +# - `matplotlib.axes.Axes.text` +# - `matplotlib.axis.Axis.set_major_formatter` +# - `matplotlib.axis.Axis.set_major_locator` +# - `matplotlib.axis.Axis.set_minor_locator` +# - `matplotlib.axis.XAxis.set_ticks_position` +# - `matplotlib.axis.YAxis.set_ticks_position` +# - `matplotlib.ticker.FixedFormatter` +# - `matplotlib.ticker.FixedLocator` +# - `matplotlib.ticker.FormatStrFormatter` +# - `matplotlib.ticker.FuncFormatter` +# - `matplotlib.ticker.MultipleLocator` +# - `matplotlib.ticker.NullFormatter` +# - `matplotlib.ticker.NullLocator` +# - `matplotlib.ticker.PercentFormatter` +# - `matplotlib.ticker.ScalarFormatter` +# - `matplotlib.ticker.StrMethodFormatter` diff --git a/examples/ticks_and_spines/tick-locators.py b/examples/ticks/tick-locators.py similarity index 100% rename from examples/ticks_and_spines/tick-locators.py rename to examples/ticks/tick-locators.py diff --git a/examples/ticks_and_spines/tick_label_right.py b/examples/ticks/tick_label_right.py similarity index 64% rename from examples/ticks_and_spines/tick_label_right.py rename to examples/ticks/tick_label_right.py index f49492e93bf1..79eccd8777e7 100644 --- a/examples/ticks_and_spines/tick_label_right.py +++ b/examples/ticks/tick_label_right.py @@ -3,10 +3,9 @@ Set default y-axis tick labels on the right ============================================ -We can use :rc:`ytick.labelright` (default False) and :rc:`ytick.right` -(default False) and :rc:`ytick.labelleft` (default True) and :rc:`ytick.left` -(default True) to control where on the axes ticks and their labels appear. -These properties can also be set in the ``.matplotlib/matplotlibrc``. +We can use :rc:`ytick.labelright`, :rc:`ytick.right`, :rc:`ytick.labelleft`, +and :rc:`ytick.left` to control where on the axes ticks and their labels +appear. These properties can also be set in ``.matplotlib/matplotlibrc``. """ import matplotlib.pyplot as plt diff --git a/examples/ticks_and_spines/tick_labels_from_values.py b/examples/ticks/tick_labels_from_values.py similarity index 76% rename from examples/ticks_and_spines/tick_labels_from_values.py rename to examples/ticks/tick_labels_from_values.py index 355692d89890..e03a2be87198 100644 --- a/examples/ticks_and_spines/tick_labels_from_values.py +++ b/examples/ticks/tick_labels_from_values.py @@ -41,17 +41,13 @@ def format_fn(tick_val, tick_pos): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.pyplot.subplots -matplotlib.axis.Axis.set_major_formatter -matplotlib.axis.Axis.set_major_locator -matplotlib.ticker.FuncFormatter -matplotlib.ticker.MaxNLocator +# - `matplotlib.pyplot.subplots` +# - `matplotlib.axis.Axis.set_major_formatter` +# - `matplotlib.axis.Axis.set_major_locator` +# - `matplotlib.ticker.FuncFormatter` +# - `matplotlib.ticker.MaxNLocator` diff --git a/examples/ticks/tick_xlabel_top.py b/examples/ticks/tick_xlabel_top.py new file mode 100644 index 000000000000..de2ca569851a --- /dev/null +++ b/examples/ticks/tick_xlabel_top.py @@ -0,0 +1,32 @@ +""" +================================== +Move x-axis tick labels to the top +================================== + +`~.axes.Axes.tick_params` can be used to configure the ticks. *top* and +*labeltop* control the visibility tick lines and labels at the top x-axis. +To move x-axis ticks from bottom to top, we have to activate the top ticks +and deactivate the bottom ticks:: + + ax.tick_params(top=True, labeltop=True, bottom=False, labelbottom=False) + +.. note:: + + If the change should be made for all future plots and not only the current + Axes, you can adapt the respective config parameters + + - :rc:`xtick.top` + - :rc:`xtick.labeltop` + - :rc:`xtick.bottom` + - :rc:`xtick.labelbottom` + +""" + +import matplotlib.pyplot as plt + +fig, ax = plt.subplots() +ax.plot(range(10)) +ax.tick_params(top=True, labeltop=True, bottom=False, labelbottom=False) +ax.set_title('x-ticks moved to the top') + +plt.show() diff --git a/examples/ticks_and_spines/ticklabels_rotation.py b/examples/ticks/ticklabels_rotation.py similarity index 100% rename from examples/ticks_and_spines/ticklabels_rotation.py rename to examples/ticks/ticklabels_rotation.py diff --git a/examples/ticks/ticks_too_many.py b/examples/ticks/ticks_too_many.py new file mode 100644 index 000000000000..c1f05bf4c17e --- /dev/null +++ b/examples/ticks/ticks_too_many.py @@ -0,0 +1,76 @@ +""" +===================== +Fixing too many ticks +===================== + +One common cause for unexpected tick behavior is passing a list of strings +instead of numbers or datetime objects. This can easily happen without notice +when reading in a comma-delimited text file. Matplotlib treats lists of strings +as *categorical* variables +(:doc:`/gallery/lines_bars_and_markers/categorical_variables`), and by default +puts one tick per category, and plots them in the order in which they are +supplied. If this is not desired, the solution is to convert the strings to +a numeric type as in the following examples. + +""" + +############################################################################ +# Example 1: Strings can lead to an unexpected order of number ticks +# ------------------------------------------------------------------ + +import matplotlib.pyplot as plt +import numpy as np + +fig, ax = plt.subplots(1, 2, constrained_layout=True, figsize=(6, 2.5)) +x = ['1', '5', '2', '3'] +y = [1, 4, 2, 3] +ax[0].plot(x, y, 'd') +ax[0].tick_params(axis='x', color='r', labelcolor='r') +ax[0].set_xlabel('Categories') +ax[0].set_title('Ticks seem out of order / misplaced') + +# convert to numbers: +x = np.asarray(x, dtype='float') +ax[1].plot(x, y, 'd') +ax[1].set_xlabel('Floats') +ax[1].set_title('Ticks as expected') + +############################################################################ +# Example 2: Strings can lead to very many ticks +# ---------------------------------------------- +# If *x* has 100 elements, all strings, then we would have 100 (unreadable) +# ticks, and again the solution is to convert the strings to floats: + +fig, ax = plt.subplots(1, 2, figsize=(6, 2.5)) +x = [f'{xx}' for xx in np.arange(100)] +y = np.arange(100) +ax[0].plot(x, y) +ax[0].tick_params(axis='x', color='r', labelcolor='r') +ax[0].set_title('Too many ticks') +ax[0].set_xlabel('Categories') + +ax[1].plot(np.asarray(x, float), y) +ax[1].set_title('x converted to numbers') +ax[1].set_xlabel('Floats') + +############################################################################ +# Example 3: Strings can lead to an unexpected order of datetime ticks +# -------------------------------------------------------------------- +# A common case is when dates are read from a CSV file, they need to be +# converted from strings to datetime objects to get the proper date locators +# and formatters. + +fig, ax = plt.subplots(1, 2, constrained_layout=True, figsize=(6, 2.75)) +x = ['2021-10-01', '2021-11-02', '2021-12-03', '2021-09-01'] +y = [0, 2, 3, 1] +ax[0].plot(x, y, 'd') +ax[0].tick_params(axis='x', labelrotation=90, color='r', labelcolor='r') +ax[0].set_title('Dates out of order') + +# convert to datetime64 +x = np.asarray(x, dtype='datetime64[s]') +ax[1].plot(x, y, 'd') +ax[1].tick_params(axis='x', labelrotation=90) +ax[1].set_title('x converted to datetimes') + +plt.show() diff --git a/examples/ticks_and_spines/README.txt b/examples/ticks_and_spines/README.txt deleted file mode 100644 index e7869c5a08d1..000000000000 --- a/examples/ticks_and_spines/README.txt +++ /dev/null @@ -1,4 +0,0 @@ -.. _ticks_and_spines_examples: - -Ticks and spines -================ diff --git a/examples/ticks_and_spines/auto_ticks.py b/examples/ticks_and_spines/auto_ticks.py deleted file mode 100644 index 5e06b8cf02ab..000000000000 --- a/examples/ticks_and_spines/auto_ticks.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -================================= -Automatically setting tick labels -================================= - -Setting the behavior of tick auto-placement. - -If you don't explicitly set tick positions / labels, Matplotlib will attempt -to choose them both automatically based on the displayed data and its limits. - -By default, this attempts to choose tick positions that are distributed -along the axis: -""" - -import matplotlib.pyplot as plt -import numpy as np -np.random.seed(19680801) - -fig, ax = plt.subplots() -dots = np.arange(10) / 100. + .03 -x, y = np.meshgrid(dots, dots) -data = [x.ravel(), y.ravel()] -ax.scatter(*data, c=data[1]) - -############################################################################### -# Sometimes choosing evenly-distributed ticks results in strange tick numbers. -# If you'd like Matplotlib to keep ticks located at round numbers, you can -# change this behavior with the following rcParams value: - -print(plt.rcParams['axes.autolimit_mode']) - -# Now change this value and see the results -with plt.rc_context({'axes.autolimit_mode': 'round_numbers'}): - fig, ax = plt.subplots() - ax.scatter(*data, c=data[1]) - -############################################################################### -# You can also alter the margins of the axes around the data by -# with ``axes.(x,y)margin``: - -with plt.rc_context({'axes.autolimit_mode': 'round_numbers', - 'axes.xmargin': .8, - 'axes.ymargin': .8}): - fig, ax = plt.subplots() - ax.scatter(*data, c=data[1]) - -plt.show() diff --git a/examples/ticks_and_spines/custom_ticker1.py b/examples/ticks_and_spines/custom_ticker1.py deleted file mode 100644 index c45802efbc43..000000000000 --- a/examples/ticks_and_spines/custom_ticker1.py +++ /dev/null @@ -1,42 +0,0 @@ -""" -============== -Custom Ticker1 -============== - -The new ticker code was designed to explicitly support user customized -ticking. The documentation of :mod:`matplotlib.ticker` details this -process. That code defines a lot of preset tickers but was primarily -designed to be user extensible. - -In this example a user defined function is used to format the ticks in -millions of dollars on the y axis. -""" -import matplotlib.pyplot as plt - -money = [1.5e5, 2.5e6, 5.5e6, 2.0e7] - - -def millions(x, pos): - """The two args are the value and tick position.""" - return '${:1.1f}M'.format(x*1e-6) - -fig, ax = plt.subplots() -# Use automatic FuncFormatter creation -ax.yaxis.set_major_formatter(millions) -ax.bar(['Bill', 'Fred', 'Mary', 'Sue'], money) -plt.show() - -############################################################################# -# -# ------------ -# -# References -# """""""""" -# -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.pyplot.subplots -matplotlib.axis.Axis.set_major_formatter -matplotlib.ticker.FuncFormatter diff --git a/examples/ticks_and_spines/date_index_formatter2.py b/examples/ticks_and_spines/date_index_formatter2.py deleted file mode 100644 index 38c9038812ae..000000000000 --- a/examples/ticks_and_spines/date_index_formatter2.py +++ /dev/null @@ -1,45 +0,0 @@ -""" -==================== -Date Index Formatter -==================== - -When plotting daily data, a frequent request is to plot the data -ignoring skips, e.g., no extra spaces for weekends. This is particularly -common in financial time series, when you may have data for M-F and -not Sat, Sun and you don't want gaps in the x axis. The approach is -to simply use the integer index for the xdata and a custom tick -Formatter to get the appropriate date string for a given index. -""" - -import dateutil.parser -from matplotlib import cbook, dates -import matplotlib.pyplot as plt -from matplotlib.ticker import Formatter -import numpy as np - - -datafile = cbook.get_sample_data('msft.csv', asfileobj=False) -print('loading %s' % datafile) -msft_data = np.genfromtxt( - datafile, delimiter=',', names=True, - converters={0: lambda s: dates.date2num(dateutil.parser.parse(s))}) - - -class MyFormatter(Formatter): - def __init__(self, dates, fmt='%Y-%m-%d'): - self.dates = dates - self.fmt = fmt - - def __call__(self, x, pos=0): - """Return the label for time x at position pos.""" - ind = int(round(x)) - if ind >= len(self.dates) or ind < 0: - return '' - return dates.num2date(self.dates[ind]).strftime(self.fmt) - - -fig, ax = plt.subplots() -ax.xaxis.set_major_formatter(MyFormatter(msft_data['Date'])) -ax.plot(msft_data['Close'], 'o-') -fig.autofmt_xdate() -plt.show() diff --git a/examples/ticks_and_spines/tick_xlabel_top.py b/examples/ticks_and_spines/tick_xlabel_top.py deleted file mode 100644 index 5ed2f81c23ce..000000000000 --- a/examples/ticks_and_spines/tick_xlabel_top.py +++ /dev/null @@ -1,27 +0,0 @@ -""" -========================================== -Set default x-axis tick labels on the top -========================================== - -We can use :rc:`xtick.labeltop` and :rc:`xtick.top` and :rc:`xtick.labelbottom` -and :rc:`xtick.bottom` to control where on the axes ticks and their labels -appear. - -These properties can also be set in ``.matplotlib/matplotlibrc``. -""" - -import matplotlib.pyplot as plt -import numpy as np - - -plt.rcParams['xtick.bottom'] = plt.rcParams['xtick.labelbottom'] = False -plt.rcParams['xtick.top'] = plt.rcParams['xtick.labeltop'] = True - -x = np.arange(10) - -fig, ax = plt.subplots() - -ax.plot(x) -ax.set_title('xlabel top') # Note title moves to make room for ticks - -plt.show() diff --git a/examples/units/bar_unit_demo.py b/examples/units/bar_unit_demo.py index da963e19f734..7b24a453154d 100644 --- a/examples/units/bar_unit_demo.py +++ b/examples/units/bar_unit_demo.py @@ -33,8 +33,7 @@ label='Women') ax.set_title('Scores by group and gender') -ax.set_xticks(ind + width / 2) -ax.set_xticklabels(('G1', 'G2', 'G3', 'G4', 'G5')) +ax.set_xticks(ind + width / 2, labels=['G1', 'G2', 'G3', 'G4', 'G5']) ax.legend() ax.yaxis.set_units(inch) diff --git a/examples/units/basic_units.py b/examples/units/basic_units.py index 727b538ec183..3b4f13313f50 100644 --- a/examples/units/basic_units.py +++ b/examples/units/basic_units.py @@ -5,10 +5,10 @@ """ -from distutils.version import LooseVersion import math import numpy as np +from packaging.version import parse as parse_version import matplotlib.units as units import matplotlib.ticker as ticker @@ -79,8 +79,8 @@ def __call__(self, *args): arg_units = [self.unit] for a in args: if hasattr(a, 'get_unit') and not hasattr(a, 'convert_to'): - # if this arg has a unit type but no conversion ability, - # this operation is prohibited + # If this argument has a unit type but no conversion ability, + # this operation is prohibited. return NotImplemented if hasattr(a, 'convert_to'): @@ -132,6 +132,9 @@ def __init__(self, value, unit): self.unit = unit self.proxy_target = self.value + def __copy__(self): + return TaggedValue(self.value, self.unit) + def __getattribute__(self, name): if name.startswith('__'): return object.__getattribute__(self, name) @@ -155,7 +158,7 @@ def __str__(self): def __len__(self): return len(self.value) - if LooseVersion(np.__version__) >= '1.20': + if parse_version(np.__version__) >= parse_version('1.20'): def __getitem__(self, key): return TaggedValue(self.value[key], self.unit) @@ -343,8 +346,6 @@ def axisinfo(unit, axis): @staticmethod def convert(val, unit, axis): - if units.ConversionInterface.is_numlike(val): - return val if np.iterable(val): if isinstance(val, np.ma.MaskedArray): val = val.astype(float).filled(np.nan) diff --git a/examples/units/ellipse_with_units.py b/examples/units/ellipse_with_units.py index 2a2242cde30c..369ad5cf4e68 100644 --- a/examples/units/ellipse_with_units.py +++ b/examples/units/ellipse_with_units.py @@ -1,14 +1,15 @@ """ ================== -Ellipse With Units +Ellipse with units ================== -Compare the ellipse generated with arcs versus a polygonal approximation +Compare the ellipse generated with arcs versus a polygonal approximation. .. only:: builder_html This example requires :download:`basic_units.py ` """ + from basic_units import cm import numpy as np from matplotlib import patches diff --git a/examples/units/evans_test.py b/examples/units/evans_test.py index 8e6c363eaaf9..3a4ac2fe7f55 100644 --- a/examples/units/evans_test.py +++ b/examples/units/evans_test.py @@ -48,9 +48,6 @@ def convert(obj, unit, axis): If *obj* is a sequence, return the converted sequence. """ - if units.ConversionInterface.is_numlike(obj): - return obj - if np.iterable(obj): return [o.value(unit) for o in obj] else: diff --git a/examples/units/units_sample.py b/examples/units/units_sample.py index db39ee73dbe9..81547601e711 100644 --- a/examples/units/units_sample.py +++ b/examples/units/units_sample.py @@ -19,14 +19,14 @@ cms = cm * np.arange(0, 10, 2) -fig, axs = plt.subplots(2, 2) +fig, axs = plt.subplots(2, 2, constrained_layout=True) axs[0, 0].plot(cms, cms) axs[0, 1].plot(cms, cms, xunits=cm, yunits=inch) axs[1, 0].plot(cms, cms, xunits=inch, yunits=cm) -axs[1, 0].set_xlim(3, 6) # scalars are interpreted in current units +axs[1, 0].set_xlim(-1, 4) # scalars are interpreted in current units axs[1, 1].plot(cms, cms, xunits=inch, yunits=inch) axs[1, 1].set_xlim(3*cm, 6*cm) # cm are converted to inches diff --git a/examples/user_interfaces/README.txt b/examples/user_interfaces/README.txt index d526adc9d65d..75b469da7cf6 100644 --- a/examples/user_interfaces/README.txt +++ b/examples/user_interfaces/README.txt @@ -8,6 +8,6 @@ following the embedding_in_SOMEGUI.py examples here. Currently Matplotlib supports PyQt/PySide, PyGObject, Tkinter, and wxPython. When embedding Matplotlib in a GUI, you must use the Matplotlib API -directly rather than the pylab/pyplot proceedural interface, so take a +directly rather than the pylab/pyplot procedural interface, so take a look at the examples/api directory for some example code working with the API. diff --git a/examples/user_interfaces/canvasagg.py b/examples/user_interfaces/canvasagg.py index 38785b94f65d..afb1c3acbf7f 100644 --- a/examples/user_interfaces/canvasagg.py +++ b/examples/user_interfaces/canvasagg.py @@ -14,14 +14,21 @@ the backend to "Agg" would be sufficient. In this example, we show how to save the contents of the agg canvas to a file, -and how to extract them to a string, which can in turn be passed off to PIL or -put in a numpy array. The latter functionality allows e.g. to use Matplotlib -inside a cgi-script *without* needing to write a figure to disk. +and how to extract them to a numpy array, which can in turn be passed off +to Pillow_. The latter functionality allows e.g. to use Matplotlib inside a +cgi-script *without* needing to write a figure to disk, and to write images in +any format supported by Pillow. + +.. _Pillow: https://pillow.readthedocs.io/ +.. redirect-from:: /gallery/misc/agg_buffer +.. redirect-from:: /gallery/misc/agg_buffer_to_array """ from matplotlib.backends.backend_agg import FigureCanvasAgg from matplotlib.figure import Figure import numpy as np +from PIL import Image + fig = Figure(figsize=(5, 4), dpi=100) # A canvas must be manually attached to the figure (pyplot would automatically @@ -37,31 +44,27 @@ # etc.). fig.savefig("test.png") -# Option 2: Retrieve a view on the renderer buffer... +# Option 2: Retrieve a memoryview on the renderer buffer, and convert it to a +# numpy array. canvas.draw() -buf = canvas.buffer_rgba() -# ... convert to a NumPy array ... -X = np.asarray(buf) +rgba = np.asarray(canvas.buffer_rgba()) # ... and pass it to PIL. -from PIL import Image -im = Image.fromarray(X) +im = Image.fromarray(rgba) +# This image can then be saved to any format supported by Pillow, e.g.: +im.save("test.bmp") # Uncomment this line to display the image using ImageMagick's `display` tool. # im.show() ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.backends.backend_agg.FigureCanvasAgg -matplotlib.figure.Figure -matplotlib.figure.Figure.add_subplot -matplotlib.figure.Figure.savefig -matplotlib.axes.Axes.plot +# - `matplotlib.backends.backend_agg.FigureCanvasAgg` +# - `matplotlib.figure.Figure` +# - `matplotlib.figure.Figure.add_subplot` +# - `matplotlib.figure.Figure.savefig` / `matplotlib.pyplot.savefig` +# - `matplotlib.axes.Axes.plot` / `matplotlib.pyplot.plot` diff --git a/examples/user_interfaces/embedding_in_gtk3_panzoom_sgskip.py b/examples/user_interfaces/embedding_in_gtk3_panzoom_sgskip.py index 2f0833f09511..7009b55bada7 100644 --- a/examples/user_interfaces/embedding_in_gtk3_panzoom_sgskip.py +++ b/examples/user_interfaces/embedding_in_gtk3_panzoom_sgskip.py @@ -20,7 +20,7 @@ win = Gtk.Window() win.connect("delete-event", Gtk.main_quit) win.set_default_size(400, 300) -win.set_title("Embedding in GTK") +win.set_title("Embedding in GTK3") fig = Figure(figsize=(5, 4), dpi=100) ax = fig.add_subplot(1, 1, 1) @@ -36,7 +36,7 @@ vbox.pack_start(canvas, True, True, 0) # Create toolbar -toolbar = NavigationToolbar(canvas, win) +toolbar = NavigationToolbar(canvas) vbox.pack_start(toolbar, False, False, 0) win.show_all() diff --git a/examples/user_interfaces/embedding_in_gtk3_sgskip.py b/examples/user_interfaces/embedding_in_gtk3_sgskip.py index f5872304964d..b672ba8d9ff0 100644 --- a/examples/user_interfaces/embedding_in_gtk3_sgskip.py +++ b/examples/user_interfaces/embedding_in_gtk3_sgskip.py @@ -19,7 +19,7 @@ win = Gtk.Window() win.connect("delete-event", Gtk.main_quit) win.set_default_size(400, 300) -win.set_title("Embedding in GTK") +win.set_title("Embedding in GTK3") fig = Figure(figsize=(5, 4), dpi=100) ax = fig.add_subplot() diff --git a/examples/user_interfaces/embedding_in_gtk4_panzoom_sgskip.py b/examples/user_interfaces/embedding_in_gtk4_panzoom_sgskip.py new file mode 100644 index 000000000000..b132aff1074f --- /dev/null +++ b/examples/user_interfaces/embedding_in_gtk4_panzoom_sgskip.py @@ -0,0 +1,51 @@ +""" +=========================================== +Embedding in GTK4 with a navigation toolbar +=========================================== + +Demonstrate NavigationToolbar with GTK4 accessed via pygobject. +""" + +import gi +gi.require_version('Gtk', '4.0') +from gi.repository import Gtk + +from matplotlib.backends.backend_gtk4 import ( + NavigationToolbar2GTK4 as NavigationToolbar) +from matplotlib.backends.backend_gtk4agg import ( + FigureCanvasGTK4Agg as FigureCanvas) +from matplotlib.figure import Figure +import numpy as np + + +def on_activate(app): + win = Gtk.ApplicationWindow(application=app) + win.set_default_size(400, 300) + win.set_title("Embedding in GTK4") + + fig = Figure(figsize=(5, 4), dpi=100) + ax = fig.add_subplot(1, 1, 1) + t = np.arange(0.0, 3.0, 0.01) + s = np.sin(2*np.pi*t) + ax.plot(t, s) + + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + win.set_child(vbox) + + # Add canvas to vbox + canvas = FigureCanvas(fig) # a Gtk.DrawingArea + canvas.set_hexpand(True) + canvas.set_vexpand(True) + vbox.append(canvas) + + # Create toolbar + toolbar = NavigationToolbar(canvas) + vbox.append(toolbar) + + win.show() + + +app = Gtk.Application( + application_id='org.matplotlib.examples.EmbeddingInGTK4PanZoom') +app.connect('activate', on_activate) +app.run(None) diff --git a/examples/user_interfaces/embedding_in_gtk4_sgskip.py b/examples/user_interfaces/embedding_in_gtk4_sgskip.py new file mode 100644 index 000000000000..c92e139de25f --- /dev/null +++ b/examples/user_interfaces/embedding_in_gtk4_sgskip.py @@ -0,0 +1,45 @@ +""" +================= +Embedding in GTK4 +================= + +Demonstrate adding a FigureCanvasGTK4Agg widget to a Gtk.ScrolledWindow using +GTK4 accessed via pygobject. +""" + +import gi +gi.require_version('Gtk', '4.0') +from gi.repository import Gtk + +from matplotlib.backends.backend_gtk4agg import ( + FigureCanvasGTK4Agg as FigureCanvas) +from matplotlib.figure import Figure +import numpy as np + + +def on_activate(app): + win = Gtk.ApplicationWindow(application=app) + win.set_default_size(400, 300) + win.set_title("Embedding in GTK4") + + fig = Figure(figsize=(5, 4), dpi=100) + ax = fig.add_subplot() + t = np.arange(0.0, 3.0, 0.01) + s = np.sin(2*np.pi*t) + ax.plot(t, s) + + # A scrolled margin goes outside the scrollbars and viewport. + sw = Gtk.ScrolledWindow(margin_top=10, margin_bottom=10, + margin_start=10, margin_end=10) + win.set_child(sw) + + canvas = FigureCanvas(fig) # a Gtk.DrawingArea + canvas.set_size_request(800, 600) + sw.set_child(canvas) + + win.show() + + +app = Gtk.Application(application_id='org.matplotlib.examples.EmbeddingInGTK4') +app.connect('activate', on_activate) +app.run(None) diff --git a/examples/user_interfaces/embedding_in_qt_sgskip.py b/examples/user_interfaces/embedding_in_qt_sgskip.py index 8994c079943d..f276a2a47c16 100644 --- a/examples/user_interfaces/embedding_in_qt_sgskip.py +++ b/examples/user_interfaces/embedding_in_qt_sgskip.py @@ -4,9 +4,9 @@ =============== Simple Qt application embedding Matplotlib canvases. This program will work -equally well using Qt4 and Qt5. Either version of Qt can be selected (for -example) by setting the ``MPLBACKEND`` environment variable to "Qt4Agg" or -"Qt5Agg", or by first importing the desired version of PyQt. +equally well using any Qt binding (PyQt6, PySide6, PyQt5, PySide2). The +binding can be selected by setting the ``QT_API`` environment variable to the +binding name, or by first importing it. """ import sys @@ -14,13 +14,9 @@ import numpy as np -from matplotlib.backends.qt_compat import QtCore, QtWidgets -if QtCore.qVersion() >= "5.": - from matplotlib.backends.backend_qt5agg import ( - FigureCanvas, NavigationToolbar2QT as NavigationToolbar) -else: - from matplotlib.backends.backend_qt4agg import ( - FigureCanvas, NavigationToolbar2QT as NavigationToolbar) +from matplotlib.backends.qt_compat import QtWidgets +from matplotlib.backends.backend_qtagg import ( + FigureCanvas, NavigationToolbar2QT as NavigationToolbar) from matplotlib.figure import Figure @@ -32,13 +28,15 @@ def __init__(self): layout = QtWidgets.QVBoxLayout(self._main) static_canvas = FigureCanvas(Figure(figsize=(5, 3))) + # Ideally one would use self.addToolBar here, but it is slightly + # incompatible between PyQt6 and other bindings, so we just add the + # toolbar as a plain widget instead. + layout.addWidget(NavigationToolbar(static_canvas, self)) layout.addWidget(static_canvas) - self.addToolBar(NavigationToolbar(static_canvas, self)) dynamic_canvas = FigureCanvas(Figure(figsize=(5, 3))) layout.addWidget(dynamic_canvas) - self.addToolBar(QtCore.Qt.BottomToolBarArea, - NavigationToolbar(dynamic_canvas, self)) + layout.addWidget(NavigationToolbar(dynamic_canvas, self)) self._static_ax = static_canvas.figure.subplots() t = np.linspace(0, 10, 501) @@ -70,4 +68,4 @@ def _update_canvas(self): app.show() app.activateWindow() app.raise_() - qapp.exec_() + qapp.exec() diff --git a/examples/user_interfaces/embedding_in_tk_sgskip.py b/examples/user_interfaces/embedding_in_tk_sgskip.py index e7342f3dcbf3..55fb2b48b5d6 100644 --- a/examples/user_interfaces/embedding_in_tk_sgskip.py +++ b/examples/user_interfaces/embedding_in_tk_sgskip.py @@ -21,7 +21,10 @@ fig = Figure(figsize=(5, 4), dpi=100) t = np.arange(0, 3, .01) -fig.add_subplot().plot(t, 2 * np.sin(2 * np.pi * t)) +ax = fig.add_subplot() +line, = ax.plot(t, 2 * np.sin(2 * np.pi * t)) +ax.set_xlabel("time [s]") +ax.set_ylabel("f(t)") canvas = FigureCanvasTkAgg(fig, master=root) # A tk.DrawingArea. canvas.draw() @@ -30,19 +33,35 @@ toolbar = NavigationToolbar2Tk(canvas, root, pack_toolbar=False) toolbar.update() - canvas.mpl_connect( "key_press_event", lambda event: print(f"you pressed {event.key}")) canvas.mpl_connect("key_press_event", key_press_handler) -button = tkinter.Button(master=root, text="Quit", command=root.quit) +button_quit = tkinter.Button(master=root, text="Quit", command=root.destroy) + + +def update_frequency(new_val): + # retrieve frequency + f = float(new_val) + + # update data + y = 2 * np.sin(2 * np.pi * f * t) + line.set_data(t, y) + + # required to update canvas and attached toolbar! + canvas.draw() + + +slider_update = tkinter.Scale(root, from_=1, to=5, orient=tkinter.HORIZONTAL, + command=update_frequency, label="Frequency [Hz]") # Packing order is important. Widgets are processed sequentially and if there # is no space left, because the window is too small, they are not displayed. # The canvas is rather flexible in its size, so we pack it last which makes # sure the UI controls are displayed as long as possible. -button.pack(side=tkinter.BOTTOM) +button_quit.pack(side=tkinter.BOTTOM) +slider_update.pack(side=tkinter.BOTTOM) toolbar.pack(side=tkinter.BOTTOM, fill=tkinter.X) -canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1) +canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=True) tkinter.mainloop() diff --git a/examples/user_interfaces/embedding_in_wx2_sgskip.py b/examples/user_interfaces/embedding_in_wx2_sgskip.py index 19580517e9e1..fcebeeee8cd6 100644 --- a/examples/user_interfaces/embedding_in_wx2_sgskip.py +++ b/examples/user_interfaces/embedding_in_wx2_sgskip.py @@ -47,8 +47,8 @@ def add_toolbar(self): self.toolbar.update() -# alternatively you could use -#class App(wx.App): +# Alternatively you could use: +# class App(wx.App): class App(WIT.InspectableApp): def OnInit(self): """Create the main window and insert the custom frame.""" diff --git a/examples/user_interfaces/embedding_in_wx5_sgskip.py b/examples/user_interfaces/embedding_in_wx5_sgskip.py index 2a97089697bc..c3c3d91f7368 100644 --- a/examples/user_interfaces/embedding_in_wx5_sgskip.py +++ b/examples/user_interfaces/embedding_in_wx5_sgskip.py @@ -9,7 +9,7 @@ import wx.lib.agw.aui as aui import wx.lib.mixins.inspection as wit -import matplotlib as mpl +from matplotlib.figure import Figure from matplotlib.backends.backend_wxagg import ( FigureCanvasWxAgg as FigureCanvas, NavigationToolbar2WxAgg as NavigationToolbar) @@ -18,7 +18,7 @@ class Plot(wx.Panel): def __init__(self, parent, id=-1, dpi=None, **kwargs): super().__init__(parent, id=id, **kwargs) - self.figure = mpl.figure.Figure(dpi=dpi, figsize=(2, 2)) + self.figure = Figure(dpi=dpi, figsize=(2, 2)) self.canvas = FigureCanvas(self, -1, self.figure) self.toolbar = NavigationToolbar(self.canvas) self.toolbar.Realize() @@ -44,16 +44,16 @@ def add(self, name="plot"): def demo(): - # alternatively you could use - #app = wx.App() + # Alternatively you could use: + # app = wx.App() # InspectableApp is a great debug tool, see: # http://wiki.wxpython.org/Widget%20Inspection%20Tool app = wit.InspectableApp() frame = wx.Frame(None, -1, 'Plotter') plotter = PlotNotebook(frame) - axes1 = plotter.add('figure 1').gca() + axes1 = plotter.add('figure 1').add_subplot() axes1.plot([1, 2, 3], [2, 1, 4]) - axes2 = plotter.add('figure 2').gca() + axes2 = plotter.add('figure 2').add_subplot() axes2.plot([1, 2, 3, 4, 5], [2, 1, 4, 2, 3]) frame.Show() app.MainLoop() diff --git a/examples/user_interfaces/embedding_webagg_sgskip.py b/examples/user_interfaces/embedding_webagg_sgskip.py index d3a313c6b9ef..dbd6bfb2bcc9 100644 --- a/examples/user_interfaces/embedding_webagg_sgskip.py +++ b/examples/user_interfaces/embedding_webagg_sgskip.py @@ -11,10 +11,13 @@ The framework being used must support web sockets. """ +import argparse import io import json import mimetypes from pathlib import Path +import signal +import socket try: import tornado @@ -27,7 +30,7 @@ import matplotlib as mpl -from matplotlib.backends.backend_webagg_core import ( +from matplotlib.backends.backend_webagg import ( FigureManagerWebAgg, new_figure_manager_given_figure) from matplotlib.figure import Figure @@ -238,13 +241,36 @@ def __init__(self, figure): if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('-p', '--port', type=int, default=8080, + help='Port to listen on (0 for a random port).') + args = parser.parse_args() + figure = create_figure() application = MyApplication(figure) http_server = tornado.httpserver.HTTPServer(application) - http_server.listen(8080) - - print("http://127.0.0.1:8080/") + sockets = tornado.netutil.bind_sockets(args.port, '') + http_server.add_sockets(sockets) + + for s in sockets: + addr, port = s.getsockname()[:2] + if s.family is socket.AF_INET6: + addr = f'[{addr}]' + print(f"Listening on http://{addr}:{port}/") print("Press Ctrl+C to quit") - tornado.ioloop.IOLoop.instance().start() + ioloop = tornado.ioloop.IOLoop.instance() + + def shutdown(): + ioloop.stop() + print("Server stopped") + + old_handler = signal.signal( + signal.SIGINT, + lambda sig, frame: ioloop.add_callback_from_signal(shutdown)) + + try: + ioloop.start() + finally: + signal.signal(signal.SIGINT, old_handler) diff --git a/examples/user_interfaces/fourier_demo_wx_sgskip.py b/examples/user_interfaces/fourier_demo_wx_sgskip.py index ba67e925dcd8..3a24e861d038 100644 --- a/examples/user_interfaces/fourier_demo_wx_sgskip.py +++ b/examples/user_interfaces/fourier_demo_wx_sgskip.py @@ -70,15 +70,15 @@ def __init__(self, parent, label, param): self.sliderText = wx.TextCtrl(parent, -1, style=wx.TE_PROCESS_ENTER) self.slider = wx.Slider(parent, -1) # self.slider.SetMax(param.maximum*1000) - self.slider.SetRange(0, param.maximum * 1000) + self.slider.SetRange(0, int(param.maximum * 1000)) self.setKnob(param.value) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(self.sliderLabel, 0, - wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, + wx.EXPAND | wx.ALL, border=2) sizer.Add(self.sliderText, 0, - wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, + wx.EXPAND | wx.ALL, border=2) sizer.Add(self.slider, 1, wx.EXPAND) self.sizer = sizer @@ -99,7 +99,7 @@ def sliderTextHandler(self, event): def setKnob(self, value): self.sliderText.SetValue('%g' % value) - self.slider.SetValue(value * 1000) + self.slider.SetValue(int(value * 1000)) class FourierDemoFrame(wx.Frame): @@ -115,9 +115,9 @@ def __init__(self, *args, **kwargs): sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.canvas, 1, wx.EXPAND) sizer.Add(self.frequencySliderGroup.sizer, 0, - wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, border=5) + wx.EXPAND | wx.ALL, border=5) sizer.Add(self.amplitudeSliderGroup.sizer, 0, - wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, border=5) + wx.EXPAND | wx.ALL, border=5) panel.SetSizer(sizer) def createCanvas(self, parent): diff --git a/examples/user_interfaces/gtk_spreadsheet_sgskip.py b/examples/user_interfaces/gtk3_spreadsheet_sgskip.py similarity index 97% rename from examples/user_interfaces/gtk_spreadsheet_sgskip.py rename to examples/user_interfaces/gtk3_spreadsheet_sgskip.py index 1f0f6e702240..925ea33faa48 100644 --- a/examples/user_interfaces/gtk_spreadsheet_sgskip.py +++ b/examples/user_interfaces/gtk3_spreadsheet_sgskip.py @@ -1,7 +1,7 @@ """ -=============== -GTK Spreadsheet -=============== +================ +GTK3 Spreadsheet +================ Example of embedding Matplotlib in an application and interacting with a treeview to store data. Double click on an entry to update plot data. diff --git a/examples/user_interfaces/gtk4_spreadsheet_sgskip.py b/examples/user_interfaces/gtk4_spreadsheet_sgskip.py new file mode 100644 index 000000000000..047ae4cf974e --- /dev/null +++ b/examples/user_interfaces/gtk4_spreadsheet_sgskip.py @@ -0,0 +1,91 @@ +""" +================ +GTK4 Spreadsheet +================ + +Example of embedding Matplotlib in an application and interacting with a +treeview to store data. Double click on an entry to update plot data. +""" + +import gi +gi.require_version('Gtk', '4.0') +gi.require_version('Gdk', '4.0') +from gi.repository import Gtk + +from matplotlib.backends.backend_gtk4agg import FigureCanvas # or gtk4cairo. + +from numpy.random import random +from matplotlib.figure import Figure + + +class DataManager(Gtk.ApplicationWindow): + num_rows, num_cols = 20, 10 + + data = random((num_rows, num_cols)) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_default_size(600, 600) + + self.set_title('GtkListStore demo') + + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, + spacing=8) + self.set_child(vbox) + + label = Gtk.Label(label='Double click a row to plot the data') + vbox.append(label) + + sw = Gtk.ScrolledWindow() + sw.set_has_frame(True) + sw.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) + sw.set_hexpand(True) + sw.set_vexpand(True) + vbox.append(sw) + + model = self.create_model() + self.treeview = Gtk.TreeView(model=model) + self.treeview.connect('row-activated', self.plot_row) + sw.set_child(self.treeview) + + # Matplotlib stuff + fig = Figure(figsize=(6, 4), constrained_layout=True) + + self.canvas = FigureCanvas(fig) # a Gtk.DrawingArea + self.canvas.set_hexpand(True) + self.canvas.set_vexpand(True) + vbox.append(self.canvas) + ax = fig.add_subplot() + self.line, = ax.plot(self.data[0, :], 'go') # plot the first row + + self.add_columns() + + def plot_row(self, treeview, path, view_column): + ind, = path # get the index into data + points = self.data[ind, :] + self.line.set_ydata(points) + self.canvas.draw() + + def add_columns(self): + for i in range(self.num_cols): + column = Gtk.TreeViewColumn(str(i), Gtk.CellRendererText(), text=i) + self.treeview.append_column(column) + + def create_model(self): + types = [float] * self.num_cols + store = Gtk.ListStore(*types) + for row in self.data: + # Gtk.ListStore.append is broken in PyGObject, so insert manually. + it = store.insert(-1) + store.set(it, {i: val for i, val in enumerate(row)}) + return store + + +def on_activate(app): + manager = DataManager(application=app) + manager.show() + + +app = Gtk.Application(application_id='org.matplotlib.examples.GTK4Spreadsheet') +app.connect('activate', on_activate) +app.run() diff --git a/examples/user_interfaces/mathtext_wx_sgskip.py b/examples/user_interfaces/mathtext_wx_sgskip.py index ca5a869bef5b..3e00073cca23 100644 --- a/examples/user_interfaces/mathtext_wx_sgskip.py +++ b/examples/user_interfaces/mathtext_wx_sgskip.py @@ -15,7 +15,6 @@ import numpy as np import wx -IS_GTK = 'wxGTK' in wx.PlatformInfo IS_WIN = 'wxMSW' in wx.PlatformInfo @@ -66,7 +65,7 @@ def __init__(self, parent, title): menuBar.Append(menu, "&File") self.Bind(wx.EVT_MENU, self.OnClose, m_exit) - if IS_GTK or IS_WIN: + if IS_WIN: # Equation Menu menu = wx.Menu() for i, (mt, func) in enumerate(functions): diff --git a/examples/user_interfaces/mpl_with_glade3_sgskip.py b/examples/user_interfaces/mpl_with_glade3_sgskip.py index 2464c5e04c41..5e6a43caca61 100644 --- a/examples/user_interfaces/mpl_with_glade3_sgskip.py +++ b/examples/user_interfaces/mpl_with_glade3_sgskip.py @@ -1,8 +1,7 @@ """ ======================= -Matplotlib With Glade 3 +Matplotlib with Glade 3 ======================= - """ from pathlib import Path diff --git a/examples/user_interfaces/pylab_with_gtk_sgskip.py b/examples/user_interfaces/pylab_with_gtk3_sgskip.py similarity index 96% rename from examples/user_interfaces/pylab_with_gtk_sgskip.py rename to examples/user_interfaces/pylab_with_gtk3_sgskip.py index 561bdbad341c..4d943032df5a 100644 --- a/examples/user_interfaces/pylab_with_gtk_sgskip.py +++ b/examples/user_interfaces/pylab_with_gtk3_sgskip.py @@ -1,7 +1,7 @@ """ -=============== -pyplot with GTK -=============== +================ +pyplot with GTK3 +================ An example of how to use pyplot to manage your figure windows, but modify the GUI by accessing the underlying GTK widgets. @@ -46,6 +46,7 @@ vbox.pack_start(label, False, False, 0) vbox.reorder_child(toolbar, -1) + def update(event): if event.xdata is None: label.set_markup('Drag mouse over axes for position') @@ -53,6 +54,7 @@ def update(event): label.set_markup( f'x,y=({event.xdata}, {event.ydata})') + fig.canvas.mpl_connect('motion_notify_event', update) plt.show() diff --git a/examples/user_interfaces/pylab_with_gtk4_sgskip.py b/examples/user_interfaces/pylab_with_gtk4_sgskip.py new file mode 100644 index 000000000000..6e0cebcce23c --- /dev/null +++ b/examples/user_interfaces/pylab_with_gtk4_sgskip.py @@ -0,0 +1,51 @@ +""" +================ +pyplot with GTK4 +================ + +An example of how to use pyplot to manage your figure windows, but modify the +GUI by accessing the underlying GTK widgets. +""" + +import matplotlib +matplotlib.use('GTK4Agg') # or 'GTK4Cairo' +import matplotlib.pyplot as plt + +import gi +gi.require_version('Gtk', '4.0') +from gi.repository import Gtk + + +fig, ax = plt.subplots() +ax.plot([1, 2, 3], 'ro-', label='easy as 1 2 3') +ax.plot([1, 4, 9], 'gs--', label='easy as 1 2 3 squared') +ax.legend() + +manager = fig.canvas.manager +# you can access the window or vbox attributes this way +toolbar = manager.toolbar +vbox = manager.vbox + +# now let's add a button to the toolbar +button = Gtk.Button(label='Click me') +button.connect('clicked', lambda button: print('hi mom')) +button.set_tooltip_text('Click me for fun and profit') +toolbar.append(button) + +# now let's add a widget to the vbox +label = Gtk.Label() +label.set_markup('Drag mouse over axes for position') +vbox.insert_child_after(label, fig.canvas) + + +def update(event): + if event.xdata is None: + label.set_markup('Drag mouse over axes for position') + else: + label.set_markup( + f'x,y=({event.xdata}, {event.ydata})') + + +fig.canvas.mpl_connect('motion_notify_event', update) + +plt.show() diff --git a/examples/user_interfaces/svg_histogram_sgskip.py b/examples/user_interfaces/svg_histogram_sgskip.py index 026cb5b1cc03..4311399fcae6 100644 --- a/examples/user_interfaces/svg_histogram_sgskip.py +++ b/examples/user_interfaces/svg_histogram_sgskip.py @@ -149,7 +149,7 @@ """ % json.dumps(hist_patches) # Add a transition effect -css = tree.getchildren()[0][0] +css = tree.find('.//{http://www.w3.org/2000/svg}style') css.text = css.text + "g {-webkit-transition:opacity 0.4s ease-out;" + \ "-moz-transition:opacity 0.4s ease-out;}" diff --git a/examples/user_interfaces/toolmanager_sgskip.py b/examples/user_interfaces/toolmanager_sgskip.py index cec5e577051f..aa381cc2f490 100644 --- a/examples/user_interfaces/toolmanager_sgskip.py +++ b/examples/user_interfaces/toolmanager_sgskip.py @@ -3,25 +3,26 @@ Tool Manager ============ -This example demonstrates how to: +This example demonstrates how to -* Modify the Toolbar -* Create tools -* Add tools -* Remove tools +* modify the Toolbar +* create tools +* add tools +* remove tools -Using `matplotlib.backend_managers.ToolManager` +using `matplotlib.backend_managers.ToolManager`. """ import matplotlib.pyplot as plt -plt.rcParams['toolbar'] = 'toolmanager' from matplotlib.backend_tools import ToolBase, ToolToggleBase +plt.rcParams['toolbar'] = 'toolmanager' + + class ListTools(ToolBase): """List all the tools controlled by the `ToolManager`.""" - # keyboard shortcut - default_keymap = 'm' + default_keymap = 'm' # keyboard shortcut description = 'List Tools' def trigger(self, *args, **kwargs): @@ -77,7 +78,6 @@ def set_lines_visibility(self, state): fig.canvas.manager.toolmanager.add_tool('List', ListTools) fig.canvas.manager.toolmanager.add_tool('Show', GroupHideTool, gid='mygroup') - # Add an existing tool to new group `foo`. # It can be added as many times as we want fig.canvas.manager.toolbar.add_tool('zoom', 'foo') diff --git a/examples/user_interfaces/web_application_server_sgskip.py b/examples/user_interfaces/web_application_server_sgskip.py index ff16277cc752..c430c24b70ed 100644 --- a/examples/user_interfaces/web_application_server_sgskip.py +++ b/examples/user_interfaces/web_application_server_sgskip.py @@ -44,7 +44,7 @@ def hello(): ############################################################################# # # Since the above code is a Flask application, it should be run using the -# `flask command-line tool `_ +# `flask command-line tool `_ # Assuming that the working directory contains this script: # # Unix-like systems diff --git a/examples/userdemo/anchored_box01.py b/examples/userdemo/anchored_box01.py deleted file mode 100644 index 00f75c7f5518..000000000000 --- a/examples/userdemo/anchored_box01.py +++ /dev/null @@ -1,18 +0,0 @@ -""" -============== -Anchored Box01 -============== - -""" -import matplotlib.pyplot as plt -from matplotlib.offsetbox import AnchoredText - - -fig, ax = plt.subplots(figsize=(3, 3)) - -at = AnchoredText("Figure 1a", - prop=dict(size=15), frameon=True, loc='upper left') -at.patch.set_boxstyle("round,pad=0.,rounding_size=0.2") -ax.add_artist(at) - -plt.show() diff --git a/examples/userdemo/anchored_box02.py b/examples/userdemo/anchored_box02.py deleted file mode 100644 index 59db0a4180a8..000000000000 --- a/examples/userdemo/anchored_box02.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -============== -Anchored Box02 -============== - -""" -from matplotlib.patches import Circle -import matplotlib.pyplot as plt -from mpl_toolkits.axes_grid1.anchored_artists import AnchoredDrawingArea - - -fig, ax = plt.subplots(figsize=(3, 3)) - -ada = AnchoredDrawingArea(40, 20, 0, 0, - loc='upper right', pad=0., frameon=False) -p1 = Circle((10, 10), 10) -ada.drawing_area.add_artist(p1) -p2 = Circle((30, 10), 5, fc="r") -ada.drawing_area.add_artist(p2) - -ax.add_artist(ada) - -plt.show() diff --git a/examples/userdemo/anchored_box03.py b/examples/userdemo/anchored_box03.py deleted file mode 100644 index ba673d8471a5..000000000000 --- a/examples/userdemo/anchored_box03.py +++ /dev/null @@ -1,20 +0,0 @@ -""" -============== -Anchored Box03 -============== - -""" -from matplotlib.patches import Ellipse -import matplotlib.pyplot as plt -from mpl_toolkits.axes_grid1.anchored_artists import AnchoredAuxTransformBox - - -fig, ax = plt.subplots(figsize=(3, 3)) - -box = AnchoredAuxTransformBox(ax.transData, loc='upper left') -el = Ellipse((0, 0), width=0.1, height=0.4, angle=30) # in data coordinates! -box.drawing_area.add_artist(el) - -ax.add_artist(box) - -plt.show() diff --git a/examples/userdemo/anchored_box04.py b/examples/userdemo/anchored_box04.py index d641e7a18ac4..2a4a0f4cea07 100644 --- a/examples/userdemo/anchored_box04.py +++ b/examples/userdemo/anchored_box04.py @@ -12,7 +12,7 @@ fig, ax = plt.subplots(figsize=(3, 3)) -box1 = TextArea(" Test : ", textprops=dict(color="k")) +box1 = TextArea(" Test: ", textprops=dict(color="k")) box2 = DrawingArea(60, 20, 0, 0) el1 = Ellipse((10, 10), width=16, height=5, angle=30, fc="r") diff --git a/examples/userdemo/colormap_normalizations_symlognorm.py b/examples/userdemo/colormap_normalizations_symlognorm.py deleted file mode 100644 index ff0d4872f75e..000000000000 --- a/examples/userdemo/colormap_normalizations_symlognorm.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -================================== -Colormap Normalizations Symlognorm -================================== - -Demonstration of using norm to map colormaps onto data in non-linear ways. -""" - -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.colors as colors - -""" -SymLogNorm: two humps, one negative and one positive, The positive -with 5-times the amplitude. Linearly, you cannot see detail in the -negative hump. Here we logarithmically scale the positive and -negative data separately. - -Note that colorbar labels do not come out looking very good. -""" - -N = 100 -X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)] -Z1 = np.exp(-X**2 - Y**2) -Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2) -Z = (Z1 - Z2) * 2 - -fig, ax = plt.subplots(2, 1) - -pcm = ax[0].pcolormesh(X, Y, Z, - norm=colors.SymLogNorm(linthresh=0.03, linscale=0.03, - vmin=-1.0, vmax=1.0, base=10), - cmap='RdBu_r', shading='nearest') -fig.colorbar(pcm, ax=ax[0], extend='both') - -pcm = ax[1].pcolormesh(X, Y, Z, cmap='RdBu_r', vmin=-np.max(Z), - shading='nearest') -fig.colorbar(pcm, ax=ax[1], extend='both') - -plt.show() diff --git a/examples/userdemo/connectionstyle_demo.py b/examples/userdemo/connectionstyle_demo.py index ad0f425a3b92..f9def1d94f24 100644 --- a/examples/userdemo/connectionstyle_demo.py +++ b/examples/userdemo/connectionstyle_demo.py @@ -30,7 +30,7 @@ def demo_con_style(ax, connectionstyle): transform=ax.transAxes, ha="left", va="top") -fig, axs = plt.subplots(3, 5, figsize=(8, 4.8)) +fig, axs = plt.subplots(3, 5, figsize=(7, 6.3), constrained_layout=True) demo_con_style(axs[0, 0], "angle3,angleA=90,angleB=0") demo_con_style(axs[1, 0], "angle3,angleA=0,angleB=90") demo_con_style(axs[0, 1], "arc3,rad=0.") @@ -47,21 +47,17 @@ def demo_con_style(ax, connectionstyle): demo_con_style(axs[2, 4], "bar,angle=180,fraction=-0.2") for ax in axs.flat: - ax.set(xlim=(0, 1), ylim=(0, 1), xticks=[], yticks=[], aspect=1) -fig.tight_layout(pad=0.2) + ax.set(xlim=(0, 1), ylim=(0, 1.25), xticks=[], yticks=[], aspect=1.25) +fig.set_constrained_layout_pads(wspace=0, hspace=0, w_pad=0, h_pad=0) plt.show() ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.annotate -matplotlib.patches.FancyArrowPatch +# - `matplotlib.axes.Axes.annotate` +# - `matplotlib.patches.FancyArrowPatch` diff --git a/examples/userdemo/demo_gridspec03.py b/examples/userdemo/demo_gridspec03.py index 2fa3fa58c0fc..55f424a808f8 100644 --- a/examples/userdemo/demo_gridspec03.py +++ b/examples/userdemo/demo_gridspec03.py @@ -31,6 +31,7 @@ def annotate_axes(fig): annotate_axes(fig) +############################################################################# fig = plt.figure() fig.suptitle("Controlling spacing around and between subplots") diff --git a/examples/userdemo/pgf_fonts.py b/examples/userdemo/pgf_fonts.py index 1f9d49853bcf..112c249752c2 100644 --- a/examples/userdemo/pgf_fonts.py +++ b/examples/userdemo/pgf_fonts.py @@ -1,8 +1,7 @@ """ ========= -Pgf Fonts +PGF fonts ========= - """ import matplotlib.pyplot as plt diff --git a/examples/userdemo/pgf_preamble_sgskip.py b/examples/userdemo/pgf_preamble_sgskip.py index e16ee0f1bbf5..ca193a0aecc0 100644 --- a/examples/userdemo/pgf_preamble_sgskip.py +++ b/examples/userdemo/pgf_preamble_sgskip.py @@ -1,8 +1,7 @@ """ ============ -Pgf Preamble +PGF preamble ============ - """ import matplotlib as mpl diff --git a/examples/userdemo/pgf_texsystem.py b/examples/userdemo/pgf_texsystem.py index dc44e8c12298..20f35b5cc906 100644 --- a/examples/userdemo/pgf_texsystem.py +++ b/examples/userdemo/pgf_texsystem.py @@ -1,8 +1,7 @@ """ ============= -Pgf Texsystem +PGF texsystem ============= - """ import matplotlib.pyplot as plt diff --git a/examples/widgets/annotated_cursor.py b/examples/widgets/annotated_cursor.py new file mode 100644 index 000000000000..abef5bfdc94f --- /dev/null +++ b/examples/widgets/annotated_cursor.py @@ -0,0 +1,342 @@ +""" +================ +Annotated Cursor +================ + +Display a data cursor including a text box, which shows the plot point close +to the mouse pointer. + +The new cursor inherits from `~matplotlib.widgets.Cursor` and demonstrates the +creation of new widgets and their event callbacks. + +See also the :doc:`cross hair cursor +`, which implements a cursor tracking the +plotted data, but without using inheritance and without displaying the +currently tracked coordinates. + +.. note:: + The figure related to this example does not show the cursor, because that + figure is automatically created in a build queue, where the first mouse + movement, which triggers the cursor creation, is missing. + +""" +from matplotlib.widgets import Cursor +import numpy as np +import matplotlib.pyplot as plt + + +class AnnotatedCursor(Cursor): + """ + A crosshair cursor like `~matplotlib.widgets.Cursor` with a text showing \ + the current coordinates. + + For the cursor to remain responsive you must keep a reference to it. + The data of the axis specified as *dataaxis* must be in ascending + order. Otherwise, the `numpy.searchsorted` call might fail and the text + disappears. You can satisfy the requirement by sorting the data you plot. + Usually the data is already sorted (if it was created e.g. using + `numpy.linspace`), but e.g. scatter plots might cause this problem. + The cursor sticks to the plotted line. + + Parameters + ---------- + line : `matplotlib.lines.Line2D` + The plot line from which the data coordinates are displayed. + + numberformat : `python format string `_, optional, default: "{0:.4g};{1:.4g}" + The displayed text is created by calling *format()* on this string + with the two coordinates. + + offset : (float, float) default: (5, 5) + The offset in display (pixel) coordinates of the text position + relative to the cross hair. + + dataaxis : {"x", "y"}, optional, default: "x" + If "x" is specified, the vertical cursor line sticks to the mouse + pointer. The horizontal cursor line sticks to *line* + at that x value. The text shows the data coordinates of *line* + at the pointed x value. If you specify "y", it works in the opposite + manner. But: For the "y" value, where the mouse points to, there might + be multiple matching x values, if the plotted function is not biunique. + Cursor and text coordinate will always refer to only one x value. + So if you use the parameter value "y", ensure that your function is + biunique. + + Other Parameters + ---------------- + textprops : `matplotlib.text` properties as dictionary + Specifies the appearance of the rendered text object. + + **cursorargs : `matplotlib.widgets.Cursor` properties + Arguments passed to the internal `~matplotlib.widgets.Cursor` instance. + The `matplotlib.axes.Axes` argument is mandatory! The parameter + *useblit* can be set to *True* in order to achieve faster rendering. + + """ + + def __init__(self, line, numberformat="{0:.4g};{1:.4g}", offset=(5, 5), + dataaxis='x', textprops=None, **cursorargs): + if textprops is None: + textprops = {} + # The line object, for which the coordinates are displayed + self.line = line + # The format string, on which .format() is called for creating the text + self.numberformat = numberformat + # Text position offset + self.offset = np.array(offset) + # The axis in which the cursor position is looked up + self.dataaxis = dataaxis + + # First call baseclass constructor. + # Draws cursor and remembers background for blitting. + # Saves ax as class attribute. + super().__init__(**cursorargs) + + # Default value for position of text. + self.set_position(self.line.get_xdata()[0], self.line.get_ydata()[0]) + # Create invisible animated text + self.text = self.ax.text( + self.ax.get_xbound()[0], + self.ax.get_ybound()[0], + "0, 0", + animated=bool(self.useblit), + visible=False, **textprops) + # The position at which the cursor was last drawn + self.lastdrawnplotpoint = None + + def onmove(self, event): + """ + Overridden draw callback for cursor. Called when moving the mouse. + """ + + # Leave method under the same conditions as in overridden method + if self.ignore(event): + self.lastdrawnplotpoint = None + return + if not self.canvas.widgetlock.available(self): + self.lastdrawnplotpoint = None + return + + # If the mouse left drawable area, we now make the text invisible. + # Baseclass will redraw complete canvas after, which makes both text + # and cursor disappear. + if event.inaxes != self.ax: + self.lastdrawnplotpoint = None + self.text.set_visible(False) + super().onmove(event) + return + + # Get the coordinates, which should be displayed as text, + # if the event coordinates are valid. + plotpoint = None + if event.xdata is not None and event.ydata is not None: + # Get plot point related to current x position. + # These coordinates are displayed in text. + plotpoint = self.set_position(event.xdata, event.ydata) + # Modify event, such that the cursor is displayed on the + # plotted line, not at the mouse pointer, + # if the returned plot point is valid + if plotpoint is not None: + event.xdata = plotpoint[0] + event.ydata = plotpoint[1] + + # If the plotpoint is given, compare to last drawn plotpoint and + # return if they are the same. + # Skip even the call of the base class, because this would restore the + # background, draw the cursor lines and would leave us the job to + # re-draw the text. + if plotpoint is not None and plotpoint == self.lastdrawnplotpoint: + return + + # Baseclass redraws canvas and cursor. Due to blitting, + # the added text is removed in this call, because the + # background is redrawn. + super().onmove(event) + + # Check if the display of text is still necessary. + # If not, just return. + # This behaviour is also cloned from the base class. + if not self.get_active() or not self.visible: + return + + # Draw the widget, if event coordinates are valid. + if plotpoint is not None: + # Update position and displayed text. + # Position: Where the event occurred. + # Text: Determined by set_position() method earlier + # Position is transformed to pixel coordinates, + # an offset is added there and this is transformed back. + temp = [event.xdata, event.ydata] + temp = self.ax.transData.transform(temp) + temp = temp + self.offset + temp = self.ax.transData.inverted().transform(temp) + self.text.set_position(temp) + self.text.set_text(self.numberformat.format(*plotpoint)) + self.text.set_visible(self.visible) + + # Tell base class, that we have drawn something. + # Baseclass needs to know, that it needs to restore a clean + # background, if the cursor leaves our figure context. + self.needclear = True + + # Remember the recently drawn cursor position, so events for the + # same position (mouse moves slightly between two plot points) + # can be skipped + self.lastdrawnplotpoint = plotpoint + # otherwise, make text invisible + else: + self.text.set_visible(False) + + # Draw changes. Cannot use _update method of baseclass, + # because it would first restore the background, which + # is done already and is not necessary. + if self.useblit: + self.ax.draw_artist(self.text) + self.canvas.blit(self.ax.bbox) + else: + # If blitting is deactivated, the overridden _update call made + # by the base class immediately returned. + # We still have to draw the changes. + self.canvas.draw_idle() + + def set_position(self, xpos, ypos): + """ + Finds the coordinates, which have to be shown in text. + + The behaviour depends on the *dataaxis* attribute. Function looks + up the matching plot coordinate for the given mouse position. + + Parameters + ---------- + xpos : float + The current x position of the cursor in data coordinates. + Important if *dataaxis* is set to 'x'. + ypos : float + The current y position of the cursor in data coordinates. + Important if *dataaxis* is set to 'y'. + + Returns + ------- + ret : {2D array-like, None} + The coordinates which should be displayed. + *None* is the fallback value. + """ + + # Get plot line data + xdata = self.line.get_xdata() + ydata = self.line.get_ydata() + + # The dataaxis attribute decides, in which axis we look up which cursor + # coordinate. + if self.dataaxis == 'x': + pos = xpos + data = xdata + lim = self.ax.get_xlim() + elif self.dataaxis == 'y': + pos = ypos + data = ydata + lim = self.ax.get_ylim() + else: + raise ValueError(f"The data axis specifier {self.dataaxis} should " + f"be 'x' or 'y'") + + # If position is valid and in valid plot data range. + if pos is not None and lim[0] <= pos <= lim[-1]: + # Find closest x value in sorted x vector. + # This requires the plotted data to be sorted. + index = np.searchsorted(data, pos) + # Return none, if this index is out of range. + if index < 0 or index >= len(data): + return None + # Return plot point as tuple. + return (xdata[index], ydata[index]) + + # Return none if there is no good related point for this x position. + return None + + def clear(self, event): + """ + Overridden clear callback for cursor, called before drawing the figure. + """ + + # The base class saves the clean background for blitting. + # Text and cursor are invisible, + # until the first mouse move event occurs. + super().clear(event) + if self.ignore(event): + return + self.text.set_visible(False) + + def _update(self): + """ + Overridden method for either blitting or drawing the widget canvas. + + Passes call to base class if blitting is activated, only. + In other cases, one draw_idle call is enough, which is placed + explicitly in this class (see *onmove()*). + In that case, `~matplotlib.widgets.Cursor` is not supposed to draw + something using this method. + """ + + if self.useblit: + super()._update() + + +fig, ax = plt.subplots(figsize=(8, 6)) +ax.set_title("Cursor Tracking x Position") + +x = np.linspace(-5, 5, 1000) +y = x**2 + +line, = ax.plot(x, y) +ax.set_xlim(-5, 5) +ax.set_ylim(0, 25) + +# A minimum call +# Set useblit=True on most backends for enhanced performance +# and pass the ax parameter to the Cursor base class. +# cursor = AnnotatedCursor(line=lin[0], ax=ax, useblit=True) + +# A more advanced call. Properties for text and lines are passed. +# Watch the passed color names and the color of cursor line and text, to +# relate the passed options to graphical elements. +# The dataaxis parameter is still the default. +cursor = AnnotatedCursor( + line=line, + numberformat="{0:.2f}\n{1:.2f}", + dataaxis='x', offset=[10, 10], + textprops={'color': 'blue', 'fontweight': 'bold'}, + ax=ax, + useblit=True, + color='red', + linewidth=2) + +plt.show() + +############################################################################### +# Trouble with non-biunique functions +# ----------------------------------- +# A call demonstrating problems with the *dataaxis=y* parameter. +# The text now looks up the matching x value for the current cursor y position +# instead of vice versa. Hover your cursor to y=4. There are two x values +# producing this y value: -2 and 2. The function is only unique, +# but not biunique. Only one value is shown in the text. + +fig, ax = plt.subplots(figsize=(8, 6)) +ax.set_title("Cursor Tracking y Position") + +line, = ax.plot(x, y) +ax.set_xlim(-5, 5) +ax.set_ylim(0, 25) + +cursor = AnnotatedCursor( + line=line, + numberformat="{0:.2f}\n{1:.2f}", + dataaxis='y', offset=[10, 10], + textprops={'color': 'blue', 'fontweight': 'bold'}, + ax=ax, + useblit=True, + color='red', linewidth=2) + +plt.show() diff --git a/examples/widgets/buttons.py b/examples/widgets/buttons.py index b1b918e479c6..24331a4acd00 100644 --- a/examples/widgets/buttons.py +++ b/examples/widgets/buttons.py @@ -16,10 +16,10 @@ freqs = np.arange(2, 20, 3) fig, ax = plt.subplots() -plt.subplots_adjust(bottom=0.2) +fig.subplots_adjust(bottom=0.2) t = np.arange(0.0, 1.0, 0.001) s = np.sin(2*np.pi*freqs[0]*t) -l, = plt.plot(t, s, lw=2) +l, = ax.plot(t, s, lw=2) class Index: @@ -40,8 +40,8 @@ def prev(self, event): plt.draw() callback = Index() -axprev = plt.axes([0.7, 0.05, 0.1, 0.075]) -axnext = plt.axes([0.81, 0.05, 0.1, 0.075]) +axprev = fig.add_axes([0.7, 0.05, 0.1, 0.075]) +axnext = fig.add_axes([0.81, 0.05, 0.1, 0.075]) bnext = Button(axnext, 'Next') bnext.on_clicked(callback.next) bprev = Button(axprev, 'Previous') @@ -51,13 +51,9 @@ def prev(self, event): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.widgets.Button +# - `matplotlib.widgets.Button` diff --git a/examples/widgets/check_buttons.py b/examples/widgets/check_buttons.py index 9a499a25ebce..d4a96c7f95d0 100644 --- a/examples/widgets/check_buttons.py +++ b/examples/widgets/check_buttons.py @@ -9,6 +9,7 @@ check boxes. There are 3 different sine waves shown and we can choose which waves are displayed with the check buttons. """ + import numpy as np import matplotlib.pyplot as plt from matplotlib.widgets import CheckButtons @@ -22,12 +23,12 @@ l0, = ax.plot(t, s0, visible=False, lw=2, color='k', label='2 Hz') l1, = ax.plot(t, s1, lw=2, color='r', label='4 Hz') l2, = ax.plot(t, s2, lw=2, color='g', label='6 Hz') -plt.subplots_adjust(left=0.2) +fig.subplots_adjust(left=0.2) lines = [l0, l1, l2] # Make checkbuttons with all plotted lines with correct visibility -rax = plt.axes([0.05, 0.4, 0.1, 0.15]) +rax = fig.add_axes([0.05, 0.4, 0.1, 0.15]) labels = [str(line.get_label()) for line in lines] visibility = [line.get_visible() for line in lines] check = CheckButtons(rax, labels, visibility) @@ -44,13 +45,9 @@ def func(label): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.widgets.CheckButtons +# - `matplotlib.widgets.CheckButtons` diff --git a/examples/widgets/cursor.py b/examples/widgets/cursor.py index d95c53da5f84..151a8dd08d4c 100644 --- a/examples/widgets/cursor.py +++ b/examples/widgets/cursor.py @@ -12,8 +12,7 @@ # Fixing random state for reproducibility np.random.seed(19680801) -fig = plt.figure(figsize=(8, 6)) -ax = fig.add_subplot(facecolor='#FFFFCC') +fig, ax = plt.subplots(figsize=(8, 6)) x, y = 4*(np.random.rand(2, 100) - .5) ax.plot(x, y, 'o') @@ -27,13 +26,9 @@ ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.widgets.Cursor +# - `matplotlib.widgets.Cursor` diff --git a/examples/widgets/lasso_selector_demo_sgskip.py b/examples/widgets/lasso_selector_demo_sgskip.py index 4827517f7b6f..f1b84316595e 100644 --- a/examples/widgets/lasso_selector_demo_sgskip.py +++ b/examples/widgets/lasso_selector_demo_sgskip.py @@ -102,14 +102,10 @@ def accept(event): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.widgets.LassoSelector -matplotlib.path.Path +# - `matplotlib.widgets.LassoSelector` +# - `matplotlib.path.Path` diff --git a/examples/widgets/mouse_cursor.py b/examples/widgets/mouse_cursor.py new file mode 100644 index 000000000000..1b0a1b2c57c3 --- /dev/null +++ b/examples/widgets/mouse_cursor.py @@ -0,0 +1,46 @@ +""" +============ +Mouse Cursor +============ + +This example sets an alternative cursor on a figure canvas. + +Note, this is an interactive example, and must be run to see the effect. +""" + +import matplotlib.pyplot as plt +from matplotlib.backend_tools import Cursors + + +fig, axs = plt.subplots(len(Cursors), figsize=(6, len(Cursors) + 0.5), + gridspec_kw={'hspace': 0}) +fig.suptitle('Hover over an Axes to see alternate Cursors') + +for cursor, ax in zip(Cursors, axs): + ax.cursor_to_use = cursor + ax.text(0.5, 0.5, cursor.name, + horizontalalignment='center', verticalalignment='center') + ax.set(xticks=[], yticks=[]) + + +def hover(event): + if fig.canvas.widgetlock.locked(): + # Don't do anything if the zoom/pan tools have been enabled. + return + + fig.canvas.set_cursor( + event.inaxes.cursor_to_use if event.inaxes else Cursors.POINTER) + + +fig.canvas.mpl_connect('motion_notify_event', hover) + +plt.show() + +############################################################################# +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.backend_bases.FigureCanvasBase.set_cursor` diff --git a/examples/widgets/multicursor.py b/examples/widgets/multicursor.py index 0916a5134442..618fa17c5ad6 100644 --- a/examples/widgets/multicursor.py +++ b/examples/widgets/multicursor.py @@ -5,33 +5,34 @@ Showing a cursor on multiple plots simultaneously. -This example generates two subplots and on hovering the cursor over data in one -subplot, the values of that datapoint are shown in both respectively. +This example generates three axes split over two different figures. On +hovering the cursor over data in one subplot, the values of that datapoint are +shown in all axes. """ + import numpy as np import matplotlib.pyplot as plt from matplotlib.widgets import MultiCursor t = np.arange(0.0, 2.0, 0.01) s1 = np.sin(2*np.pi*t) -s2 = np.sin(4*np.pi*t) +s2 = np.sin(3*np.pi*t) +s3 = np.sin(4*np.pi*t) fig, (ax1, ax2) = plt.subplots(2, sharex=True) ax1.plot(t, s1) ax2.plot(t, s2) +fig, ax3 = plt.subplots() +ax3.plot(t, s3) -multi = MultiCursor(fig.canvas, (ax1, ax2), color='r', lw=1) +multi = MultiCursor(None, (ax1, ax2, ax3), color='r', lw=1) plt.show() ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.widgets.MultiCursor +# - `matplotlib.widgets.MultiCursor` diff --git a/examples/widgets/polygon_selector_demo.py b/examples/widgets/polygon_selector_demo.py index d4b14154ef3b..da9e65c39268 100644 --- a/examples/widgets/polygon_selector_demo.py +++ b/examples/widgets/polygon_selector_demo.py @@ -1,7 +1,7 @@ """ -================ -Polygon Selector -================ +======================================================= +Select indices from a collection using polygon selector +======================================================= Shows how one can select indices of a polygon interactively. """ @@ -50,7 +50,7 @@ def __init__(self, ax, collection, alpha_other=0.3): elif len(self.fc) == 1: self.fc = np.tile(self.fc, (self.Npts, 1)) - self.poly = PolygonSelector(ax, self.onselect) + self.poly = PolygonSelector(ax, self.onselect, draw_bounding_box=True) self.ind = [] def onselect(self, verts): @@ -94,14 +94,10 @@ def disconnect(self): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.widgets.PolygonSelector -matplotlib.path.Path +# - `matplotlib.widgets.PolygonSelector` +# - `matplotlib.path.Path` diff --git a/examples/widgets/polygon_selector_simple.py b/examples/widgets/polygon_selector_simple.py new file mode 100644 index 000000000000..cdc84b07eaf6 --- /dev/null +++ b/examples/widgets/polygon_selector_simple.py @@ -0,0 +1,46 @@ +""" +================ +Polygon Selector +================ + +Shows how to create a polygon programmatically or interactively +""" + +import matplotlib.pyplot as plt +from matplotlib.widgets import PolygonSelector + +############################################################################### +# +# To create the polygon programmatically +fig, ax = plt.subplots() +fig.show() + +selector = PolygonSelector(ax, lambda *args: None) + +# Add three vertices +selector.verts = [(0.1, 0.4), (0.5, 0.9), (0.3, 0.2)] + + +############################################################################### +# +# To create the polygon interactively + +fig2, ax2 = plt.subplots() +fig2.show() + +selector2 = PolygonSelector(ax2, lambda *args: None) + +print("Click on the figure to create a polygon.") +print("Press the 'esc' key to start a new polygon.") +print("Try holding the 'shift' key to move all of the vertices.") +print("Try holding the 'ctrl' key to move a single vertex.") + + +############################################################################# +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.widgets.PolygonSelector` diff --git a/examples/widgets/radio_buttons.py b/examples/widgets/radio_buttons.py index abad1f5f1efa..28e446fc5b80 100644 --- a/examples/widgets/radio_buttons.py +++ b/examples/widgets/radio_buttons.py @@ -9,6 +9,7 @@ In this case, the buttons let the user choose one of the three different sine waves to be shown in the plot. """ + import numpy as np import matplotlib.pyplot as plt from matplotlib.widgets import RadioButtons @@ -20,10 +21,10 @@ fig, ax = plt.subplots() l, = ax.plot(t, s0, lw=2, color='red') -plt.subplots_adjust(left=0.3) +fig.subplots_adjust(left=0.3) axcolor = 'lightgoldenrodyellow' -rax = plt.axes([0.05, 0.7, 0.15, 0.15], facecolor=axcolor) +rax = fig.add_axes([0.05, 0.7, 0.15, 0.15], facecolor=axcolor) radio = RadioButtons(rax, ('2 Hz', '4 Hz', '8 Hz')) @@ -34,7 +35,7 @@ def hzfunc(label): plt.draw() radio.on_clicked(hzfunc) -rax = plt.axes([0.05, 0.4, 0.15, 0.15], facecolor=axcolor) +rax = fig.add_axes([0.05, 0.4, 0.15, 0.15], facecolor=axcolor) radio2 = RadioButtons(rax, ('red', 'blue', 'green')) @@ -43,8 +44,8 @@ def colorfunc(label): plt.draw() radio2.on_clicked(colorfunc) -rax = plt.axes([0.05, 0.1, 0.15, 0.15], facecolor=axcolor) -radio3 = RadioButtons(rax, ('-', '--', '-.', 'steps', ':')) +rax = fig.add_axes([0.05, 0.1, 0.15, 0.15], facecolor=axcolor) +radio3 = RadioButtons(rax, ('-', '--', '-.', ':')) def stylefunc(label): @@ -56,13 +57,9 @@ def stylefunc(label): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.widgets.RadioButtons +# - `matplotlib.widgets.RadioButtons` diff --git a/examples/widgets/range_slider.py b/examples/widgets/range_slider.py index 9dc104251786..bdb69fa80859 100644 --- a/examples/widgets/range_slider.py +++ b/examples/widgets/range_slider.py @@ -26,14 +26,14 @@ img = np.random.randn(N, N) fig, axs = plt.subplots(1, 2, figsize=(10, 5)) -plt.subplots_adjust(bottom=0.25) +fig.subplots_adjust(bottom=0.25) im = axs[0].imshow(img) axs[1].hist(img.flatten(), bins='auto') axs[1].set_title('Histogram of pixel intensities') # Create the RangeSlider -slider_ax = plt.axes([0.20, 0.1, 0.60, 0.03]) +slider_ax = fig.add_axes([0.20, 0.1, 0.60, 0.03]) slider = RangeSlider(slider_ax, "Threshold", img.min(), img.max()) # Create the Vertical lines on the histogram @@ -62,13 +62,9 @@ def update(val): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.widgets.RangeSlider +# - `matplotlib.widgets.RangeSlider` diff --git a/examples/widgets/rectangle_selector.py b/examples/widgets/rectangle_selector.py index 4dcab351dd8e..8ede9ad66fc4 100644 --- a/examples/widgets/rectangle_selector.py +++ b/examples/widgets/rectangle_selector.py @@ -1,22 +1,21 @@ """ -================== -Rectangle Selector -================== +=============================== +Rectangle and ellipse selectors +=============================== -Do a mouseclick somewhere, move the mouse to some destination, release -the button. This class gives click- and release-events and also draws -a line or a box from the click-point to the actual mouseposition -(within the same axes) until the button is released. Within the -method ``self.ignore()`` it is checked whether the button from eventpress -and eventrelease are the same. +Click somewhere, move the mouse, and release the mouse button. +`.RectangleSelector` and `.EllipseSelector` draw a rectangle or an ellipse +from the initial click position to the current mouse position (within the same +axes) until the button is released. A connected callback receives the click- +and release-events. """ -from matplotlib.widgets import RectangleSelector +from matplotlib.widgets import EllipseSelector, RectangleSelector import numpy as np import matplotlib.pyplot as plt -def line_select_callback(eclick, erelease): +def select_callback(eclick, erelease): """ Callback for line selection. @@ -25,48 +24,50 @@ def line_select_callback(eclick, erelease): x1, y1 = eclick.xdata, eclick.ydata x2, y2 = erelease.xdata, erelease.ydata print(f"({x1:3.2f}, {y1:3.2f}) --> ({x2:3.2f}, {y2:3.2f})") - print(f" The buttons you used were: {eclick.button} {erelease.button}") + print(f"The buttons you used were: {eclick.button} {erelease.button}") def toggle_selector(event): - print(' Key pressed.') + print('Key pressed.') if event.key == 't': - if toggle_selector.RS.active: - print(' RectangleSelector deactivated.') - toggle_selector.RS.set_active(False) - else: - print(' RectangleSelector activated.') - toggle_selector.RS.set_active(True) + for selector in selectors: + name = type(selector).__name__ + if selector.active: + print(f'{name} deactivated.') + selector.set_active(False) + else: + print(f'{name} activated.') + selector.set_active(True) -fig, ax = plt.subplots() +fig = plt.figure(constrained_layout=True) +axs = fig.subplots(2) + N = 100000 # If N is large one can see improvement by using blitting. x = np.linspace(0, 10, N) -ax.plot(x, np.sin(2*np.pi*x)) # plot something -ax.set_title( - "Click and drag to draw a rectangle.\n" - "Press 't' to toggle the selector on and off.") - -# drawtype is 'box' or 'line' or 'none' -toggle_selector.RS = RectangleSelector(ax, line_select_callback, - drawtype='box', useblit=True, - button=[1, 3], # disable middle button - minspanx=5, minspany=5, - spancoords='pixels', - interactive=True) -fig.canvas.mpl_connect('key_press_event', toggle_selector) +selectors = [] +for ax, selector_class in zip(axs, [RectangleSelector, EllipseSelector]): + ax.plot(x, np.sin(2*np.pi*x)) # plot something + ax.set_title(f"Click and drag to draw a {selector_class.__name__}.") + selectors.append(selector_class( + ax, select_callback, + useblit=True, + button=[1, 3], # disable middle button + minspanx=5, minspany=5, + spancoords='pixels', + interactive=True)) + fig.canvas.mpl_connect('key_press_event', toggle_selector) +axs[0].set_title("Press 't' to toggle the selectors on and off.\n" + + axs[0].get_title()) plt.show() ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.widgets.RectangleSelector +# - `matplotlib.widgets.RectangleSelector` +# - `matplotlib.widgets.EllipseSelector` diff --git a/examples/widgets/slider_demo.py b/examples/widgets/slider_demo.py index fade13904618..6f849645f9bf 100644 --- a/examples/widgets/slider_demo.py +++ b/examples/widgets/slider_demo.py @@ -12,6 +12,7 @@ See :doc:`/gallery/widgets/range_slider` for an example of using a ``RangeSlider`` to define a range of values. """ + import numpy as np import matplotlib.pyplot as plt from matplotlib.widgets import Slider, Button @@ -29,17 +30,14 @@ def f(t, amplitude, frequency): # Create the figure and the line that we will manipulate fig, ax = plt.subplots() -line, = plt.plot(t, f(t, init_amplitude, init_frequency), lw=2) +line, = ax.plot(t, f(t, init_amplitude, init_frequency), lw=2) ax.set_xlabel('Time [s]') -axcolor = 'lightgoldenrodyellow' -ax.margins(x=0) - # adjust the main plot to make room for the sliders -plt.subplots_adjust(left=0.25, bottom=0.25) +fig.subplots_adjust(left=0.25, bottom=0.25) # Make a horizontal slider to control the frequency. -axfreq = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=axcolor) +axfreq = fig.add_axes([0.25, 0.1, 0.65, 0.03]) freq_slider = Slider( ax=axfreq, label='Frequency [Hz]', @@ -49,7 +47,7 @@ def f(t, amplitude, frequency): ) # Make a vertically oriented slider to control the amplitude -axamp = plt.axes([0.1, 0.25, 0.0225, 0.63], facecolor=axcolor) +axamp = fig.add_axes([0.1, 0.25, 0.0225, 0.63]) amp_slider = Slider( ax=axamp, label="Amplitude", @@ -71,8 +69,8 @@ def update(val): amp_slider.on_changed(update) # Create a `matplotlib.widgets.Button` to reset the sliders to initial values. -resetax = plt.axes([0.8, 0.025, 0.1, 0.04]) -button = Button(resetax, 'Reset', color=axcolor, hovercolor='0.975') +resetax = fig.add_axes([0.8, 0.025, 0.1, 0.04]) +button = Button(resetax, 'Reset', hovercolor='0.975') def reset(event): @@ -84,14 +82,10 @@ def reset(event): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.widgets.Button -matplotlib.widgets.Slider +# - `matplotlib.widgets.Button` +# - `matplotlib.widgets.Slider` diff --git a/examples/widgets/slider_snap_demo.py b/examples/widgets/slider_snap_demo.py index 085099c552eb..5ace232c12c5 100644 --- a/examples/widgets/slider_snap_demo.py +++ b/examples/widgets/slider_snap_demo.py @@ -15,6 +15,7 @@ See :doc:`/gallery/widgets/range_slider` for an example of using a ``RangeSlider`` to define a range of values. """ + import numpy as np import matplotlib.pyplot as plt from matplotlib.widgets import Slider, Button @@ -25,12 +26,11 @@ s = a0 * np.sin(2 * np.pi * f0 * t) fig, ax = plt.subplots() -plt.subplots_adjust(bottom=0.25) -l, = plt.plot(t, s, lw=2) +fig.subplots_adjust(bottom=0.25) +l, = ax.plot(t, s, lw=2) -slider_bkd_color = 'lightgoldenrodyellow' -ax_freq = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=slider_bkd_color) -ax_amp = plt.axes([0.25, 0.15, 0.65, 0.03], facecolor=slider_bkd_color) +ax_freq = fig.add_axes([0.25, 0.1, 0.65, 0.03]) +ax_amp = fig.add_axes([0.25, 0.15, 0.65, 0.03]) # define the values to use for snapping allowed_amplitudes = np.concatenate([np.linspace(.1, 5, 100), [6, 7, 8, 9]]) @@ -59,8 +59,8 @@ def update(val): sfreq.on_changed(update) samp.on_changed(update) -ax_reset = plt.axes([0.8, 0.025, 0.1, 0.04]) -button = Button(ax_reset, 'Reset', color=slider_bkd_color, hovercolor='0.975') +ax_reset = fig.add_axes([0.8, 0.025, 0.1, 0.04]) +button = Button(ax_reset, 'Reset', hovercolor='0.975') def reset(event): @@ -73,14 +73,10 @@ def reset(event): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.widgets.Slider -matplotlib.widgets.Button +# - `matplotlib.widgets.Slider` +# - `matplotlib.widgets.Button` diff --git a/examples/widgets/span_selector.py b/examples/widgets/span_selector.py index a61038781d44..fa75a70d2863 100644 --- a/examples/widgets/span_selector.py +++ b/examples/widgets/span_selector.py @@ -3,9 +3,18 @@ Span Selector ============= -The SpanSelector is a mouse widget to select a xmin/xmax range and plot the -detail view of the selected region in the lower axes +The `.SpanSelector` is a mouse widget that enables selecting a range on an +axis. + +Here, an x-range can be selected on the upper axis; a detailed view of the +selected range is then plotted on the lower axis. + +.. note:: + + If the SpanSelector object is garbage collected you will lose the + interactivity. You must keep a hard reference to it to prevent this. """ + import numpy as np import matplotlib.pyplot as plt from matplotlib.widgets import SpanSelector @@ -14,40 +23,41 @@ np.random.seed(19680801) fig, (ax1, ax2) = plt.subplots(2, figsize=(8, 6)) -ax1.set(facecolor='#FFFFCC') x = np.arange(0.0, 5.0, 0.01) -y = np.sin(2*np.pi*x) + 0.5*np.random.randn(len(x)) +y = np.sin(2 * np.pi * x) + 0.5 * np.random.randn(len(x)) -ax1.plot(x, y, '-') +ax1.plot(x, y) ax1.set_ylim(-2, 2) -ax1.set_title('Press left mouse button and drag to test') +ax1.set_title('Press left mouse button and drag ' + 'to select a region in the top graph') -ax2.set(facecolor='#FFFFCC') -line2, = ax2.plot(x, y, '-') +line2, = ax2.plot([], []) def onselect(xmin, xmax): indmin, indmax = np.searchsorted(x, (xmin, xmax)) indmax = min(len(x) - 1, indmax) - thisx = x[indmin:indmax] - thisy = y[indmin:indmax] - line2.set_data(thisx, thisy) - ax2.set_xlim(thisx[0], thisx[-1]) - ax2.set_ylim(thisy.min(), thisy.max()) - fig.canvas.draw() - -############################################################################# -# .. note:: -# -# If the SpanSelector object is garbage collected you will lose the -# interactivity. You must keep a hard reference to it to prevent this. -# - - -span = SpanSelector(ax1, onselect, 'horizontal', useblit=True, - rectprops=dict(alpha=0.5, facecolor='red')) + region_x = x[indmin:indmax] + region_y = y[indmin:indmax] + + if len(region_x) >= 2: + line2.set_data(region_x, region_y) + ax2.set_xlim(region_x[0], region_x[-1]) + ax2.set_ylim(region_y.min(), region_y.max()) + fig.canvas.draw_idle() + + +span = SpanSelector( + ax1, + onselect, + "horizontal", + useblit=True, + props=dict(alpha=0.5, facecolor="tab:blue"), + interactive=True, + drag_from_anywhere=True +) # Set useblit=True on most backends for enhanced performance. @@ -55,13 +65,9 @@ def onselect(xmin, xmax): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.widgets.SpanSelector +# - `matplotlib.widgets.SpanSelector` diff --git a/examples/widgets/textbox.py b/examples/widgets/textbox.py index 4e65af7bd9a1..33a996ae73c2 100644 --- a/examples/widgets/textbox.py +++ b/examples/widgets/textbox.py @@ -40,7 +40,7 @@ def submit(expression): axbox = fig.add_axes([0.1, 0.05, 0.8, 0.075]) -text_box = TextBox(axbox, "Evaluate") +text_box = TextBox(axbox, "Evaluate", textalignment="center") text_box.on_submit(submit) text_box.set_val("t ** 2") # Trigger `submit` with the initial string. @@ -48,12 +48,9 @@ def submit(expression): ############################################################################# # -# ------------ +# .. admonition:: References # -# References -# """""""""" +# The use of the following functions, methods, classes and modules is shown +# in this example: # -# The use of the following functions, methods, classes and modules is shown -# in this example: - -from matplotlib.widgets import TextBox +# - `matplotlib.widgets.TextBox` diff --git a/extern/ttconv/pprdrv.h b/extern/ttconv/pprdrv.h index 39e81fee7f0c..8c0b6c195564 100644 --- a/extern/ttconv/pprdrv.h +++ b/extern/ttconv/pprdrv.h @@ -48,20 +48,6 @@ class TTStreamWriter virtual void putline(const char* a); }; -class TTDictionaryCallback -{ -private: - // Private copy and assignment - TTDictionaryCallback& operator=(const TTStreamWriter& other); - TTDictionaryCallback(const TTStreamWriter& other); - -public: - TTDictionaryCallback() { } - virtual ~TTDictionaryCallback() { } - - virtual void add_pair(const char* key, const char* value) = 0; -}; - void replace_newlines_with_spaces(char* a); /* @@ -95,6 +81,12 @@ class TTException #define DEBUG_TRUETYPE /* truetype fonts, conversion to Postscript */ #endif +#if DEBUG_TRUETYPE +#define debug(...) printf(__VA_ARGS__) +#else +#define debug(...) +#endif + /* Do not change anything below this line. */ enum font_type_enum @@ -102,12 +94,9 @@ enum font_type_enum PS_TYPE_3 = 3, PS_TYPE_42 = 42, PS_TYPE_42_3_HYBRID = 43, - PDF_TYPE_3 = -3 }; /* routines in pprdrv_tt.c */ void insert_ttfont(const char *filename, TTStreamWriter& stream, font_type_enum target_type, std::vector& glyph_ids); -void get_pdf_charprocs(const char *filename, std::vector& glyph_ids, TTDictionaryCallback& dict); - /* end of file */ diff --git a/extern/ttconv/pprdrv_tt.cpp b/extern/ttconv/pprdrv_tt.cpp index 934215470808..a0c724c8aa11 100644 --- a/extern/ttconv/pprdrv_tt.cpp +++ b/extern/ttconv/pprdrv_tt.cpp @@ -121,10 +121,7 @@ BYTE *GetTable(struct TTFONT *font, const char *name) { BYTE *ptr; ULONG x; - -#ifdef DEBUG_TRUETYPE debug("GetTable(file,font,\"%s\")",name); -#endif /* We must search the table directory. */ ptr = font->offset_table + 12; @@ -142,9 +139,7 @@ BYTE *GetTable(struct TTFONT *font, const char *name) try { -#ifdef DEBUG_TRUETYPE debug("Loading table \"%s\" from offset %d, %d bytes",name,offset,length); -#endif if ( fseek( font->file, (long)offset, SEEK_SET ) ) { @@ -200,10 +195,7 @@ void Read_name(struct TTFONT *font) int platform; /* Current platform id */ int nameid; /* name id, */ int offset,length; /* offset and length of string. */ - -#ifdef DEBUG_TRUETYPE debug("Read_name()"); -#endif table_ptr = NULL; @@ -235,11 +227,8 @@ void Read_name(struct TTFONT *font) nameid = getUSHORT(ptr2+6); length = getUSHORT(ptr2+8); offset = getUSHORT(ptr2+10); - -#ifdef DEBUG_TRUETYPE debug("platform %d, encoding %d, language 0x%x, name %d, offset %d, length %d", platform,encoding,language,nameid,offset,length); -#endif /* Copyright notice */ if ( platform == 1 && nameid == 0 ) @@ -248,10 +237,7 @@ void Read_name(struct TTFONT *font) strncpy(font->Copyright,(const char*)strings+offset,length); font->Copyright[length]='\0'; replace_newlines_with_spaces(font->Copyright); - -#ifdef DEBUG_TRUETYPE debug("font->Copyright=\"%s\"",font->Copyright); -#endif continue; } @@ -264,10 +250,7 @@ void Read_name(struct TTFONT *font) strncpy(font->FamilyName,(const char*)strings+offset,length); font->FamilyName[length]='\0'; replace_newlines_with_spaces(font->FamilyName); - -#ifdef DEBUG_TRUETYPE debug("font->FamilyName=\"%s\"",font->FamilyName); -#endif continue; } @@ -280,10 +263,7 @@ void Read_name(struct TTFONT *font) strncpy(font->Style,(const char*)strings+offset,length); font->Style[length]='\0'; replace_newlines_with_spaces(font->Style); - -#ifdef DEBUG_TRUETYPE debug("font->Style=\"%s\"",font->Style); -#endif continue; } @@ -296,10 +276,7 @@ void Read_name(struct TTFONT *font) strncpy(font->FullName,(const char*)strings+offset,length); font->FullName[length]='\0'; replace_newlines_with_spaces(font->FullName); - -#ifdef DEBUG_TRUETYPE debug("font->FullName=\"%s\"",font->FullName); -#endif continue; } @@ -312,10 +289,7 @@ void Read_name(struct TTFONT *font) strncpy(font->Version,(const char*)strings+offset,length); font->Version[length]='\0'; replace_newlines_with_spaces(font->Version); - -#ifdef DEBUG_TRUETYPE debug("font->Version=\"%s\"",font->Version); -#endif continue; } @@ -328,10 +302,7 @@ void Read_name(struct TTFONT *font) strncpy(font->PostName,(const char*)strings+offset,length); font->PostName[length]='\0'; replace_newlines_with_spaces(font->PostName); - -#ifdef DEBUG_TRUETYPE debug("font->PostName=\"%s\"",font->PostName); -#endif continue; } @@ -343,10 +314,7 @@ void Read_name(struct TTFONT *font) utf16be_to_ascii(font->PostName, (char *)strings+offset, length); font->PostName[length/2]='\0'; replace_newlines_with_spaces(font->PostName); - -#ifdef DEBUG_TRUETYPE debug("font->PostName=\"%s\"",font->PostName); -#endif continue; } @@ -358,10 +326,7 @@ void Read_name(struct TTFONT *font) strncpy(font->Trademark,(const char*)strings+offset,length); font->Trademark[length]='\0'; replace_newlines_with_spaces(font->Trademark); - -#ifdef DEBUG_TRUETYPE debug("font->Trademark=\"%s\"",font->Trademark); -#endif continue; } } @@ -677,10 +642,7 @@ void sfnts_glyf_table(TTStreamWriter& stream, struct TTFONT *font, ULONG oldoffs ULONG total=0; /* running total of bytes written to table */ int x; bool loca_is_local=false; - -#ifdef DEBUG_TRUETYPE debug("sfnts_glyf_table(font,%d)", (int)correct_total_length); -#endif if (font->loca_table == NULL) { @@ -709,10 +671,7 @@ void sfnts_glyf_table(TTStreamWriter& stream, struct TTFONT *font, ULONG oldoffs length = getULONG( font->loca_table + ((x+1) * 4) ); length -= off; } - -#ifdef DEBUG_TRUETYPE debug("glyph length=%d",(int)length); -#endif /* Start new string if necessary. */ sfnts_new_table( stream, (int)length ); @@ -798,33 +757,36 @@ void ttfont_sfnts(TTStreamWriter& stream, struct TTFONT *font) ** Find the tables we want and store there vital ** statistics in tables[]. */ - for (x=0; x < 9; x++ ) - { - do - { - diff = strncmp( (char*)ptr, table_names[x], 4 ); - - if ( diff > 0 ) /* If we are past it. */ - { - tables[x].length = 0; - diff = 0; - } - else if ( diff < 0 ) /* If we haven't hit it yet. */ - { - ptr += 16; - } - else if ( diff == 0 ) /* Here it is! */ - { - tables[x].newoffset = nextoffset; - tables[x].checksum = getULONG( ptr + 4 ); - tables[x].oldoffset = getULONG( ptr + 8 ); - tables[x].length = getULONG( ptr + 12 ); - nextoffset += ( ((tables[x].length + 3) / 4) * 4 ); - count++; - ptr += 16; - } - } - while (diff != 0); + ULONG num_tables_read = 0; /* Number of tables read from the directory */ + for (x = 0; x < 9; x++) { + do { + if (num_tables_read < font->numTables) { + /* There are still tables to read from ptr */ + diff = strncmp((char*)ptr, table_names[x], 4); + + if (diff > 0) { /* If we are past it. */ + tables[x].length = 0; + diff = 0; + } else if (diff < 0) { /* If we haven't hit it yet. */ + ptr += 16; + num_tables_read++; + } else if (diff == 0) { /* Here it is! */ + tables[x].newoffset = nextoffset; + tables[x].checksum = getULONG( ptr + 4 ); + tables[x].oldoffset = getULONG( ptr + 8 ); + tables[x].length = getULONG( ptr + 12 ); + nextoffset += ( ((tables[x].length + 3) / 4) * 4 ); + count++; + ptr += 16; + num_tables_read++; + } + } else { + /* We've read the whole table directory already */ + /* Some tables couldn't be found */ + tables[x].length = 0; + break; /* Proceed to next tables[x] */ + } + } while (diff != 0); } /* end of for loop which passes over the table directory */ @@ -841,18 +803,23 @@ void ttfont_sfnts(TTStreamWriter& stream, struct TTFONT *font) /* Now, generate those silly numTables numbers. */ sfnts_pputUSHORT(stream, count); /* number of tables */ - if ( count == 9 ) - { - sfnts_pputUSHORT(stream, 7); /* searchRange */ - sfnts_pputUSHORT(stream, 3); /* entrySelector */ - sfnts_pputUSHORT(stream, 81); /* rangeShift */ - } -#ifdef DEBUG_TRUETYPE - else - { - debug("only %d tables selected",count); + + int search_range = 1; + int entry_sel = 0; + + while (search_range <= count) { + search_range <<= 1; + entry_sel++; } -#endif + entry_sel = entry_sel > 0 ? entry_sel - 1 : 0; + search_range = (search_range >> 1) * 16; + int range_shift = count * 16 - search_range; + + sfnts_pputUSHORT(stream, search_range); /* searchRange */ + sfnts_pputUSHORT(stream, entry_sel); /* entrySelector */ + sfnts_pputUSHORT(stream, range_shift); /* rangeShift */ + + debug("only %d tables selected",count); /* Now, emmit the table directory. */ for (x=0; x < 9; x++) @@ -885,10 +852,7 @@ void ttfont_sfnts(TTStreamWriter& stream, struct TTFONT *font) { continue; } - -#ifdef DEBUG_TRUETYPE debug("emmiting table '%s'",table_names[x]); -#endif /* 'glyf' table gets special treatment */ if ( strcmp(table_names[x],"glyf")==0 ) @@ -1278,9 +1242,7 @@ void read_font(const char *filename, font_type_enum target_type, std::vector& glyph_ids, TTDictionaryCallback& dict) -{ - struct TTFONT font; - - read_font(filename, PDF_TYPE_3, glyph_ids, font); - - for (std::vector::const_iterator i = glyph_ids.begin(); - i != glyph_ids.end(); ++i) - { - StringStreamWriter writer; - tt_type3_charproc(writer, &font, *i); - const char* name = ttfont_CharStrings_getname(&font, *i); - dict.add_pair(name, writer.str().c_str()); - } -} - TTFONT::TTFONT() : file(NULL), PostName(NULL), diff --git a/extern/ttconv/pprdrv_tt2.cpp b/extern/ttconv/pprdrv_tt2.cpp index 058bc005348b..ec2298c8c42b 100644 --- a/extern/ttconv/pprdrv_tt2.cpp +++ b/extern/ttconv/pprdrv_tt2.cpp @@ -60,8 +60,6 @@ class GlyphToType3 int stack_depth; /* A book-keeping variable for keeping track of the depth of the PS stack */ - bool pdf_mode; - void load_char(TTFONT* font, BYTE *glyph); void stack(TTStreamWriter& stream, int new_elem); void stack_end(TTStreamWriter& stream); @@ -91,12 +89,6 @@ struct FlaggedPoint FlaggedPoint(Flag flag_, FWord x_, FWord y_): flag(flag_), x(x_), y(y_) {}; }; -double area(FWord *x, FWord *y, int n); -#define sqr(x) ((x)*(x)) - -#define NOMOREINCTR -1 -#define NOMOREOUTCTR -1 - /* ** This routine is used to break the character ** procedure up into a number of smaller @@ -111,9 +103,8 @@ double area(FWord *x, FWord *y, int n); */ void GlyphToType3::stack(TTStreamWriter& stream, int new_elem) { - if ( !pdf_mode && num_pts > 25 ) /* Only do something of we will */ + if ( num_pts > 25 ) /* Only do something of we will have a log of points. */ { - /* have a log of points. */ if (stack_depth == 0) { stream.put_char('{'); @@ -132,7 +123,7 @@ void GlyphToType3::stack(TTStreamWriter& stream, int new_elem) void GlyphToType3::stack_end(TTStreamWriter& stream) /* called at end */ { - if ( !pdf_mode && stack_depth ) + if ( stack_depth ) { stream.puts("}_e"); stack_depth=0; @@ -238,19 +229,17 @@ void GlyphToType3::PSConvert(TTStreamWriter& stream) /* Now, we can fill the whole thing. */ stack(stream, 1); - stream.puts( pdf_mode ? "f" : "_cl" ); + stream.puts("_cl"); } /* end of PSConvert() */ void GlyphToType3::PSMoveto(TTStreamWriter& stream, int x, int y) { - stream.printf(pdf_mode ? "%d %d m\n" : "%d %d _m\n", - x, y); + stream.printf("%d %d _m\n", x, y); } void GlyphToType3::PSLineto(TTStreamWriter& stream, int x, int y) { - stream.printf(pdf_mode ? "%d %d l\n" : "%d %d _l\n", - x, y); + stream.printf("%d %d _l\n", x, y); } /* @@ -278,9 +267,9 @@ void GlyphToType3::PSCurveto(TTStreamWriter& stream, cy[1] = (sy[2]+2*sy[1])/3; cx[2] = sx[2]; cy[2] = sy[2]; - stream.printf("%d %d %d %d %d %d %s\n", + stream.printf("%d %d %d %d %d %d _c\n", (int)cx[0], (int)cy[0], (int)cx[1], (int)cy[1], - (int)cx[2], (int)cy[2], pdf_mode ? "c" : "_c"); + (int)cx[2], (int)cy[2]); } /* @@ -464,50 +453,27 @@ void GlyphToType3::do_composite(TTStreamWriter& stream, struct TTFONT *font, BYT (int)flags,arg1,arg2); #endif - if (pdf_mode) + /* If we have an (X,Y) shift and it is non-zero, */ + /* translate the coordinate system. */ + if ( flags & ARGS_ARE_XY_VALUES ) { - if ( flags & ARGS_ARE_XY_VALUES ) - { - /* We should have been able to use 'Do' to reference the - subglyph here. However, that doesn't seem to work with - xpdf or gs (only acrobat), so instead, this just includes - the subglyph here inline. */ - stream.printf("q 1 0 0 1 %d %d cm\n", topost(arg1), topost(arg2)); - } - else - { - stream.printf("%% unimplemented shift, arg1=%d, arg2=%d\n",arg1,arg2); - } - GlyphToType3(stream, font, glyphIndex, true); - if ( flags & ARGS_ARE_XY_VALUES ) - { - stream.printf("\nQ\n"); - } + if ( arg1 != 0 || arg2 != 0 ) + stream.printf("gsave %d %d translate\n", topost(arg1), topost(arg2) ); } else { - /* If we have an (X,Y) shif and it is non-zero, */ - /* translate the coordinate system. */ - if ( flags & ARGS_ARE_XY_VALUES ) - { - if ( arg1 != 0 || arg2 != 0 ) - stream.printf("gsave %d %d translate\n", topost(arg1), topost(arg2) ); - } - else - { - stream.printf("%% unimplemented shift, arg1=%d, arg2=%d\n",arg1,arg2); - } + stream.printf("%% unimplemented shift, arg1=%d, arg2=%d\n",arg1,arg2); + } - /* Invoke the CharStrings procedure to print the component. */ - stream.printf("false CharStrings /%s get exec\n", - ttfont_CharStrings_getname(font,glyphIndex)); + /* Invoke the CharStrings procedure to print the component. */ + stream.printf("false CharStrings /%s get exec\n", + ttfont_CharStrings_getname(font, glyphIndex)); - /* If we translated the coordinate system, */ - /* put it back the way it was. */ - if ( flags & ARGS_ARE_XY_VALUES && (arg1 != 0 || arg2 != 0) ) - { - stream.puts("grestore "); - } + /* If we translated the coordinate system, */ + /* put it back the way it was. */ + if ( flags & ARGS_ARE_XY_VALUES && (arg1 != 0 || arg2 != 0) ) + { + stream.puts("grestore "); } } @@ -559,7 +525,6 @@ GlyphToType3::GlyphToType3(TTStreamWriter& stream, struct TTFONT *font, int char ycoor = NULL; epts_ctr = NULL; stack_depth = 0; - pdf_mode = font->target_type < 0; /* Get a pointer to the data. */ glyph = find_glyph_data( font, charindex ); @@ -610,15 +575,7 @@ GlyphToType3::GlyphToType3(TTStreamWriter& stream, struct TTFONT *font, int char /* Execute setcachedevice in order to inform the font machinery */ /* of the character bounding box and advance width. */ stack(stream, 7); - if (pdf_mode) - { - if (!embedded) { - stream.printf("%d 0 %d %d %d %d d1\n", - topost(advance_width), - topost(llx), topost(lly), topost(urx), topost(ury) ); - } - } - else if (font->target_type == PS_TYPE_42_3_HYBRID) + if (font->target_type == PS_TYPE_42_3_HYBRID) { stream.printf("pop gsave .001 .001 scale %d 0 %d %d %d %d setcachedevice\n", topost(advance_width), diff --git a/extern/ttconv/ttutil.cpp b/extern/ttconv/ttutil.cpp index 85e7e23c4f07..6028e1d45d4a 100644 --- a/extern/ttconv/ttutil.cpp +++ b/extern/ttconv/ttutil.cpp @@ -14,18 +14,6 @@ #include #include "pprdrv.h" -#if DEBUG_TRUETYPE -void debug(const char *format, ... ) -{ - va_list arg_list; - va_start(arg_list, format); - - printf(format, arg_list); - - va_end(arg_list); -} -#endif - #define PRINTF_BUFFER_SIZE 512 void TTStreamWriter::printf(const char* format, ...) { @@ -45,6 +33,7 @@ void TTStreamWriter::printf(const char* format, ...) #else vsnprintf(buffer2, size, format, arg_list); #endif + this->write(buffer2); free(buffer2); } else { this->write(buffer); diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index c3d4aaf62d0b..77d25bbe3358 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -17,10 +17,13 @@ at the ipython shell prompt. -For the most part, direct use of the object-oriented library is encouraged when -programming; pyplot is primarily for working interactively. The exceptions are -the pyplot functions `.pyplot.figure`, `.pyplot.subplot`, `.pyplot.subplots`, -and `.pyplot.savefig`, which can greatly simplify scripting. +For the most part, direct use of the explicit object-oriented library is +encouraged when programming; the implicit pyplot interface is primarily for +working interactively. The exceptions to this suggestion are the pyplot +functions `.pyplot.figure`, `.pyplot.subplot`, `.pyplot.subplots`, and +`.pyplot.savefig`, which can greatly simplify scripting. See +:ref:`api_interfaces` for an explanation of the tradeoffs between the implicit +and explicit interfaces. Modules include: @@ -78,14 +81,14 @@ developed and maintained by a host of others. Occasionally the internal documentation (python docstrings) will refer -to MATLAB®, a registered trademark of The MathWorks, Inc. +to MATLAB®, a registered trademark of The MathWorks, Inc. + """ import atexit from collections import namedtuple from collections.abc import MutableMapping import contextlib -from distutils.version import LooseVersion import functools import importlib import inspect @@ -102,20 +105,16 @@ import tempfile import warnings +import numpy +from packaging.version import parse as parse_version + # cbook must import matplotlib only within function # definitions, so it is safe to import from it here. -from . import _api, cbook, docstring, rcsetup -from matplotlib.cbook import MatplotlibDeprecationWarning, sanitize_sequence -from matplotlib.cbook import mplDeprecation # deprecated +from . import _api, _version, cbook, _docstring, rcsetup +from matplotlib.cbook import sanitize_sequence +from matplotlib._api import MatplotlibDeprecationWarning from matplotlib.rcsetup import validate_backend, cycler -import numpy - -# Get the version from the _version.py versioneer file. For a git checkout, -# this is computed based on the number of commits since the last tag. -from ._version import get_versions -__version__ = str(get_versions()['version']) -del get_versions _log = logging.getLogger(__name__) @@ -134,6 +133,64 @@ year = 2007 }""" +# modelled after sys.version_info +_VersionInfo = namedtuple('_VersionInfo', + 'major, minor, micro, releaselevel, serial') + + +def _parse_to_version_info(version_str): + """ + Parse a version string to a namedtuple analogous to sys.version_info. + + See: + https://packaging.pypa.io/en/latest/version.html#packaging.version.parse + https://docs.python.org/3/library/sys.html#sys.version_info + """ + v = parse_version(version_str) + if v.pre is None and v.post is None and v.dev is None: + return _VersionInfo(v.major, v.minor, v.micro, 'final', 0) + elif v.dev is not None: + return _VersionInfo(v.major, v.minor, v.micro, 'alpha', v.dev) + elif v.pre is not None: + releaselevel = { + 'a': 'alpha', + 'b': 'beta', + 'rc': 'candidate'}.get(v.pre[0], 'alpha') + return _VersionInfo(v.major, v.minor, v.micro, releaselevel, v.pre[1]) + else: + # fallback for v.post: guess-next-dev scheme from setuptools_scm + return _VersionInfo(v.major, v.minor, v.micro + 1, 'alpha', v.post) + + +def _get_version(): + """Return the version string used for __version__.""" + # Only shell out to a git subprocess if really needed, i.e. when we are in + # a matplotlib git repo but not in a shallow clone, such as those used by + # CI, as the latter would trigger a warning from setuptools_scm. + root = Path(__file__).resolve().parents[2] + if ((root / ".matplotlib-repo").exists() + and (root / ".git").exists() + and not (root / ".git/shallow").exists()): + import setuptools_scm + return setuptools_scm.get_version( + root=root, + version_scheme="release-branch-semver", + local_scheme="node-and-date", + fallback_version=_version.version, + ) + else: # Get the version from the _version.py setuptools_scm file. + return _version.version + + +@_api.caching_module_getattr +class __getattr__: + __version__ = property(lambda self: _get_version()) + __version_info__ = property( + lambda self: _parse_to_version_info(self.__version__)) + # module-level deprecations + URL_REGEX = _api.deprecated("3.5", obj_type="")(property( + lambda self: re.compile(r'^http://|^https://|^ftp://|^file:'))) + def _check_versions(): @@ -145,13 +202,13 @@ def _check_versions(): ("cycler", "0.10"), ("dateutil", "2.7"), ("kiwisolver", "1.0.1"), - ("numpy", "1.16"), + ("numpy", "1.19"), ("pyparsing", "2.2.1"), ]: module = importlib.import_module(modname) - if LooseVersion(module.__version__) < minver: - raise ImportError("Matplotlib requires {}>={}; you have {}" - .format(modname, minver, module.__version__)) + if parse_version(module.__version__) < parse_version(minver): + raise ImportError(f"Matplotlib requires {modname}>={minver}; " + f"you have {module.__version__}") _check_versions() @@ -227,7 +284,7 @@ def wrapper(**kwargs): return wrapper -_ExecInfo = namedtuple("_ExecInfo", "executable version") +_ExecInfo = namedtuple("_ExecInfo", "executable raw_version version") class ExecutableNotFoundError(FileNotFoundError): @@ -251,21 +308,23 @@ def _get_executable_info(name): ---------- name : str The executable to query. The following values are currently supported: - "dvipng", "gs", "inkscape", "magick", "pdftops". This list is subject - to change without notice. + "dvipng", "gs", "inkscape", "magick", "pdftocairo", "pdftops". This + list is subject to change without notice. Returns ------- tuple A namedtuple with fields ``executable`` (`str`) and ``version`` - (`distutils.version.LooseVersion`, or ``None`` if the version cannot be - determined). + (`packaging.Version`, or ``None`` if the version cannot be determined). Raises ------ ExecutableNotFoundError If the executable is not found or older than the oldest version - supported by Matplotlib. + supported by Matplotlib. For debugging purposes, it is also + possible to "hide" an executable from Matplotlib by adding it to the + :envvar:`_MPLHIDEEXECUTABLES` environment variable (a comma-separated + list), which must be set prior to any calls to this function. ValueError If the executable is not one that we know how to query. """ @@ -289,17 +348,21 @@ def impl(args, regex, min_ver=None, ignore_exit_code=False): raise ExecutableNotFoundError(str(_ose)) from _ose match = re.search(regex, output) if match: - version = LooseVersion(match.group(1)) - if min_ver is not None and version < min_ver: + raw_version = match.group(1) + version = parse_version(raw_version) + if min_ver is not None and version < parse_version(min_ver): raise ExecutableNotFoundError( f"You have {args[0]} version {version} but the minimum " f"version supported by Matplotlib is {min_ver}") - return _ExecInfo(args[0], version) + return _ExecInfo(args[0], raw_version, version) else: raise ExecutableNotFoundError( f"Failed to determine the version of {args[0]} from " f"{' '.join(args)}, which output {output}") + if name in os.environ.get("_MPLHIDEEXECUTABLES", "").split(","): + raise ExecutableNotFoundError(f"{name} was hidden") + if name == "dvipng": return impl(["dvipng", "-version"], "(?m)^dvipng(?: .*)? (.+)", "1.6") elif name == "gs": @@ -351,17 +414,20 @@ def impl(args, regex, min_ver=None, ignore_exit_code=False): else: path = "convert" info = impl([path, "--version"], r"^Version: ImageMagick (\S*)") - if info.version == "7.0.10-34": + if info.raw_version == "7.0.10-34": # https://github.com/ImageMagick/ImageMagick/issues/2720 raise ExecutableNotFoundError( f"You have ImageMagick {info.version}, which is unsupported") return info + elif name == "pdftocairo": + return impl(["pdftocairo", "-v"], "pdftocairo version (.*)") elif name == "pdftops": info = impl(["pdftops", "-v"], "^pdftops version (.*)", ignore_exit_code=True) - if info and not ("3.0" <= info.version - # poppler version numbers. - or "0.9" <= info.version <= "1.0"): + if info and not ( + 3 <= info.version.major or + # poppler version numbers. + parse_version("0.9") <= info.version < parse_version("1.0")): raise ExecutableNotFoundError( f"You have pdftops version {info.version} but the minimum " f"version supported by Matplotlib is 3.0") @@ -370,6 +436,7 @@ def impl(args, regex, min_ver=None, ignore_exit_code=False): raise ValueError("Unknown executable: {!r}".format(name)) +@_api.deprecated("3.6", alternative="a vendored copy of this function") def checkdep_usetex(s): if not s: return False @@ -394,7 +461,7 @@ def _get_xdg_config_dir(): Return the XDG configuration directory, according to the XDG base directory spec: - https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html """ return os.environ.get('XDG_CONFIG_HOME') or str(Path.home() / ".config") @@ -403,17 +470,19 @@ def _get_xdg_cache_dir(): """ Return the XDG cache directory, according to the XDG base directory spec: - https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html """ return os.environ.get('XDG_CACHE_HOME') or str(Path.home() / ".cache") -def _get_config_or_cache_dir(xdg_base): +def _get_config_or_cache_dir(xdg_base_getter): configdir = os.environ.get('MPLCONFIGDIR') if configdir: configdir = Path(configdir).resolve() - elif sys.platform.startswith(('linux', 'freebsd')) and xdg_base: - configdir = Path(xdg_base, "matplotlib") + elif sys.platform.startswith(('linux', 'freebsd')): + # Only call _xdg_base_getter here so that MPLCONFIGDIR is tried first, + # as _xdg_base_getter can throw. + configdir = Path(xdg_base_getter(), "matplotlib") else: configdir = Path.home() / ".matplotlib" try: @@ -441,7 +510,7 @@ def _get_config_or_cache_dir(xdg_base): @_logged_cached('CONFIGDIR=%s') def get_configdir(): """ - Return the string path of the the configuration directory. + Return the string path of the configuration directory. The directory is chosen as follows: @@ -454,7 +523,7 @@ def get_configdir(): 4. Else, create a temporary directory, and use it as the configuration directory. """ - return _get_config_or_cache_dir(_get_xdg_config_dir()) + return _get_config_or_cache_dir(_get_xdg_config_dir) @_logged_cached('CACHEDIR=%s') @@ -465,7 +534,7 @@ def get_cachedir(): The procedure used to find the directory is the same as for _get_config_dir, except using ``$XDG_CACHE_HOME``/``$HOME/.cache`` instead. """ - return _get_config_or_cache_dir(_get_xdg_cache_dir()) + return _get_config_or_cache_dir(_get_xdg_cache_dir) @_logged_cached('matplotlib data path: %s') @@ -522,31 +591,18 @@ def gen_candidates(): # rcParams deprecated and automatically mapped to another key. # Values are tuples of (version, new_name, f_old2new, f_new2old). _deprecated_map = {} - # rcParams deprecated; some can manually be mapped to another key. # Values are tuples of (version, new_name_or_None). -_deprecated_ignore_map = { - 'mpl_toolkits.legacy_colorbar': ('3.4', None), -} - +_deprecated_ignore_map = {} # rcParams deprecated; can use None to suppress warnings; remain actually -# listed in the rcParams (not included in _all_deprecated). +# listed in the rcParams. # Values are tuples of (version,) -_deprecated_remain_as_none = { - 'animation.avconv_path': ('3.3',), - 'animation.avconv_args': ('3.3',), - 'animation.html_args': ('3.3',), - 'mathtext.fallback_to_cm': ('3.3',), - 'keymap.all_axes': ('3.3',), - 'savefig.jpeg_quality': ('3.3',), - 'text.latex.preview': ('3.3',), -} +_deprecated_remain_as_none = {} -_all_deprecated = {*_deprecated_map, *_deprecated_ignore_map} - - -@docstring.Substitution("\n".join(map("- {}".format, rcsetup._validators))) +@_docstring.Substitution( + "\n".join(map("- {}".format, sorted(rcsetup._validators, key=str.lower))) +) class RcParams(MutableMapping, dict): """ A dictionary object including validation. @@ -612,7 +668,9 @@ def __getitem__(self, key): version, name=key, obj_type="rcparam", alternative=alt_key) return dict.__getitem__(self, alt_key) if alt_key else None - elif key == "backend": + # In theory, this should only ever be used after the global rcParams + # has been set up, but better be safe e.g. in presence of breakpoints. + elif key == "backend" and self is globals().get("rcParams"): val = dict.__getitem__(self, key) if val is rcsetup._auto_backend_sentinel: from matplotlib import pyplot as plt @@ -620,6 +678,11 @@ def __getitem__(self, key): return dict.__getitem__(self, key) + def _get_backend_or_none(self): + """Get the requested backend, if any, without triggering resolution.""" + backend = dict.__getitem__(self, "backend") + return None if backend is rcsetup._auto_backend_sentinel else backend + def __repr__(self): class_name = self.__class__.__name__ indent = len(class_name) + 1 @@ -657,7 +720,11 @@ def find_all(self, pattern): if pattern_re.search(key)) def copy(self): - return {k: dict.__getitem__(self, k) for k in self} + """Copy this RcParams instance.""" + rccopy = RcParams() + for k in self: # Skip deprecations and revalidation. + dict.__setitem__(rccopy, k, dict.__getitem__(self, k)) + return rccopy def rc_params(fail_on_error=False): @@ -665,12 +732,10 @@ def rc_params(fail_on_error=False): return rc_params_from_file(matplotlib_fname(), fail_on_error) -URL_REGEX = re.compile(r'^http://|^https://|^ftp://|^file:') - - +@_api.deprecated("3.5") def is_url(filename): """Return whether *filename* is an http, https, ftp, or file URL path.""" - return URL_REGEX.match(filename) is not None + return __getattr__("URL_REGEX").match(filename) is not None @functools.lru_cache() @@ -686,7 +751,8 @@ def _get_ssl_context(): @contextlib.contextmanager def _open_file_or_url(fname): - if not isinstance(fname, Path) and is_url(fname): + if (isinstance(fname, str) + and fname.startswith(('http://', 'https://', 'ftp://', 'file:'))): import urllib.request ssl_ctx = _get_ssl_context() if ssl_ctx is None: @@ -697,10 +763,7 @@ def _open_file_or_url(fname): yield (line.decode('utf-8') for line in f) else: fname = os.path.expanduser(fname) - encoding = locale.getpreferredencoding(do_setlocale=False) - if encoding is None: - encoding = "utf-8" - with open(fname, encoding=encoding) as f: + with open(fname, encoding='utf-8') as f: yield f @@ -721,12 +784,13 @@ def _rc_params_in_file(fname, transform=lambda x: x, fail_on_error=False): fail_on_error : bool, default: False Whether invalid entries should result in an exception or a warning. """ + import matplotlib as mpl rc_temp = {} with _open_file_or_url(fname) as fd: try: for line_no, line in enumerate(fd, 1): line = transform(line) - strippedline = line.split('#', 1)[0].strip() + strippedline = cbook._strip_comment(line) if not strippedline: continue tup = strippedline.split(':', 1) @@ -737,16 +801,15 @@ def _rc_params_in_file(fname, transform=lambda x: x, fail_on_error=False): key, val = tup key = key.strip() val = val.strip() + if val.startswith('"') and val.endswith('"'): + val = val[1:-1] # strip double quotes if key in rc_temp: _log.warning('Duplicate key in file %r, line %d (%r)', fname, line_no, line.rstrip('\n')) rc_temp[key] = (val, line, line_no) except UnicodeDecodeError: - _log.warning('Cannot decode configuration file %s with encoding ' - '%s, check LANG and LC_* variables.', - fname, - locale.getpreferredencoding(do_setlocale=False) - or 'utf-8 (default)') + _log.warning('Cannot decode configuration file %r as utf-8.', + fname) raise config = RcParams() @@ -767,7 +830,10 @@ def _rc_params_in_file(fname, transform=lambda x: x, fail_on_error=False): version, name=key, alternative=alt_key, obj_type='rcparam', addendum="Please update your matplotlibrc.") else: - version = 'master' if '.post' in __version__ else f'v{__version__}' + # __version__ must be looked up as an attribute to trigger the + # module-level __getattr__. + version = ('main' if '.post' in mpl.__version__ + else f'v{mpl.__version__}') _log.warning(""" Bad key %(key)s in file %(fname)s, line %(line_no)s (%(line)r) You probably need to get an updated matplotlibrc file from @@ -823,11 +889,17 @@ def rc_params_from_file(fname, fail_on_error=False, use_default_template=True): transform=lambda line: line[1:] if line.startswith("#") else line, fail_on_error=True) dict.update(rcParamsDefault, rcsetup._hardcoded_defaults) +# Normally, the default matplotlibrc file contains *no* entry for backend (the +# corresponding line starts with ##, not #; we fill on _auto_backend_sentinel +# in that case. However, packagers can set a different default backend +# (resulting in a normal `#backend: foo` line) in which case we should *not* +# fill in _auto_backend_sentinel. +dict.setdefault(rcParamsDefault, "backend", rcsetup._auto_backend_sentinel) rcParams = RcParams() # The global instance. dict.update(rcParams, dict.items(rcParamsDefault)) dict.update(rcParams, _rc_params_in_file(matplotlib_fname())) +rcParamsOrig = rcParams.copy() with _api.suppress_matplotlib_deprecation_warning(): - rcParamsOrig = RcParams(rcParams.copy()) # This also checks that all rcParams are indeed listed in the template. # Assigning to rcsetup.defaultParams is left only for backcompat. defaultParams = rcsetup.defaultParams = { @@ -921,7 +993,7 @@ def rcdefaults(): Restore the `.rcParams` from Matplotlib's internal default style. Style-blacklisted `.rcParams` (defined in - `matplotlib.style.core.STYLE_BLACKLIST`) are not updated. + ``matplotlib.style.core.STYLE_BLACKLIST``) are not updated. See Also -------- @@ -946,7 +1018,7 @@ def rc_file_defaults(): Restore the `.rcParams` from the original rc file loaded by Matplotlib. Style-blacklisted `.rcParams` (defined in - `matplotlib.style.core.STYLE_BLACKLIST`) are not updated. + ``matplotlib.style.core.STYLE_BLACKLIST``) are not updated. """ # Deprecation warnings were already handled when creating rcParamsOrig, no # need to reemit them here. @@ -961,7 +1033,7 @@ def rc_file(fname, *, use_default_template=True): Update `.rcParams` from file. Style-blacklisted `.rcParams` (defined in - `matplotlib.style.core.STYLE_BLACKLIST`) are not updated. + ``matplotlib.style.core.STYLE_BLACKLIST``) are not updated. Parameters ---------- @@ -988,6 +1060,8 @@ def rc_context(rc=None, fname=None): """ Return a context manager for temporarily changing rcParams. + The :rc:`backend` will not be reset by the context manager. + Parameters ---------- rc : dict @@ -1016,7 +1090,8 @@ def rc_context(rc=None, fname=None): plt.plot(x, y) # uses 'print.rc' """ - orig = rcParams.copy() + orig = dict(rcParams.copy()) + del orig['backend'] try: if fname: rc_file(fname) @@ -1038,20 +1113,24 @@ def use(backend, *, force=True): backend names, which are case-insensitive: - interactive backends: - GTK3Agg, GTK3Cairo, MacOSX, nbAgg, - Qt4Agg, Qt4Cairo, Qt5Agg, Qt5Cairo, - TkAgg, TkCairo, WebAgg, WX, WXAgg, WXCairo + GTK3Agg, GTK3Cairo, GTK4Agg, GTK4Cairo, MacOSX, nbAgg, QtAgg, + QtCairo, TkAgg, TkCairo, WebAgg, WX, WXAgg, WXCairo, Qt5Agg, Qt5Cairo - non-interactive backends: agg, cairo, pdf, pgf, ps, svg, template or a string of the form: ``module://my.module.name``. + Switching to an interactive backend is not possible if an unrelated + event loop has already been started (e.g., switching to GTK3Agg if a + TkAgg window has already been opened). Switching to a non-interactive + backend is always possible. + force : bool, default: True If True (the default), raise an `ImportError` if the backend cannot be set up (either because it fails to import, or because an incompatible - GUI interactive framework is already running); if False, ignore the - failure. + GUI interactive framework is already running); if False, silently + ignore the failure. See Also -------- @@ -1059,9 +1138,8 @@ def use(backend, *, force=True): matplotlib.get_backend """ name = validate_backend(backend) - # we need to use the base-class method here to avoid (prematurely) - # resolving the "auto" backend setting - if dict.__getitem__(rcParams, 'backend') == name: + # don't (prematurely) resolve the "auto" backend setting + if rcParams._get_backend_or_none() == name: # Nothing to do if the requested backend is already set pass else: @@ -1112,7 +1190,14 @@ def interactive(b): def is_interactive(): - """Return whether to redraw after every plotting command.""" + """ + Return whether to redraw after every plotting command. + + .. note:: + + This function is only intended for use in backends. End users should + use `.pyplot.isinteractive` instead. + """ return rcParams['interactive'] @@ -1133,15 +1218,15 @@ def _init_tests(): _log.warning( f"Matplotlib is not built with the correct FreeType version to " f"run tests. Rebuild without setting system_freetype=1 in " - f"setup.cfg. Expect many image comparison failures below. " + f"mplsetup.cfg. Expect many image comparison failures below. " f"Expected freetype version {LOCAL_FREETYPE_VERSION}. " f"Found freetype version {ft2font.__freetype_version__}. " "Freetype build type is {}local".format( "" if ft2font.__freetype_build_type__ == 'local' else "not ")) -@_api.delete_parameter("3.3", "recursionlimit") -def test(verbosity=None, coverage=False, *, recursionlimit=0, **kwargs): +@_api.deprecated("3.5", alternative='pytest') +def test(verbosity=None, coverage=False, **kwargs): """Run the matplotlib test suite.""" try: @@ -1155,11 +1240,8 @@ def test(verbosity=None, coverage=False, *, recursionlimit=0, **kwargs): return -1 old_backend = get_backend() - old_recursionlimit = sys.getrecursionlimit() try: use('agg') - if recursionlimit: - sys.setrecursionlimit(recursionlimit) args = kwargs.pop('argv', []) provide_default_modules = True @@ -1188,8 +1270,6 @@ def test(verbosity=None, coverage=False, *, recursionlimit=0, **kwargs): finally: if old_backend.lower() != 'agg': use(old_backend) - if recursionlimit: - sys.setrecursionlimit(old_recursionlimit) return retcode @@ -1222,24 +1302,6 @@ def _label_from_arg(y, default_name): return None -_DATA_DOC_TITLE = """ - -Notes ------ -""" - -_DATA_DOC_APPENDIX = """ - -.. note:: - In addition to the above described arguments, this function can take - a *data* keyword argument. If such a *data* argument is given, -{replaced} - - Objects passed as **data** must support item access (``data[s]``) and - membership test (``s in data``). -""" - - def _add_data_doc(docstring, replace_names): """ Add documentation for a *data* field to the given docstring. @@ -1262,17 +1324,26 @@ def _add_data_doc(docstring, replace_names): or replace_names is not None and len(replace_names) == 0): return docstring docstring = inspect.cleandoc(docstring) - repl = ( - (" every other argument can also be string ``s``, which is\n" - " interpreted as ``data[s]`` (unless this raises an exception).") - if replace_names is None else - (" the following arguments can also be string ``s``, which is\n" - " interpreted as ``data[s]`` (unless this raises an exception):\n" - " " + ", ".join(map("*{}*".format, replace_names))) + ".") - addendum = _DATA_DOC_APPENDIX.format(replaced=repl) - if _DATA_DOC_TITLE not in docstring: - addendum = _DATA_DOC_TITLE + addendum - return docstring + addendum + + data_doc = ("""\ + If given, all parameters also accept a string ``s``, which is + interpreted as ``data[s]`` (unless this raises an exception).""" + if replace_names is None else f"""\ + If given, the following parameters also accept a string ``s``, which is + interpreted as ``data[s]`` (unless this raises an exception): + + {', '.join(map('*{}*'.format, replace_names))}""") + # using string replacement instead of formatting has the advantages + # 1) simpler indent handling + # 2) prevent problems with formatting characters '{', '%' in the docstring + if _log.level <= logging.DEBUG: + # test_data_parameter_replacement() tests against these log messages + # make sure to keep message and test in sync + if "data : indexable object, optional" not in docstring: + _log.debug("data parameter docstring error: no data parameter") + if 'DATA_PARAMETER_PLACEHOLDER' not in docstring: + _log.debug("data parameter docstring error: missing placeholder") + return docstring.replace(' DATA_PARAMETER_PLACEHOLDER', data_doc) def _preprocess_data(func=None, *, replace_names=None, label_namer=None): @@ -1382,7 +1453,11 @@ def inner(ax, *args, data=None, **kwargs): return inner -_log.debug('matplotlib version %s', __version__) _log.debug('interactive is %s', is_interactive()) _log.debug('platform is %s', sys.platform) -_log.debug('loaded modules: %s', list(sys.modules)) + + +# workaround: we must defer colormaps import to after loading rcParams, because +# colormap creation depends on rcParams +from matplotlib.cm import _colormaps as colormaps +from matplotlib.colors import _color_sequences as color_sequences diff --git a/lib/matplotlib/_afm.py b/lib/matplotlib/_afm.py new file mode 100644 index 000000000000..3d02d7f9c1d6 --- /dev/null +++ b/lib/matplotlib/_afm.py @@ -0,0 +1,532 @@ +""" +A python interface to Adobe Font Metrics Files. + +Although a number of other Python implementations exist, and may be more +complete than this, it was decided not to go with them because they were +either: + +1) copyrighted or used a non-BSD compatible license +2) had too many dependencies and a free standing lib was needed +3) did more than needed and it was easier to write afresh rather than + figure out how to get just what was needed. + +It is pretty easy to use, and has no external dependencies: + +>>> import matplotlib as mpl +>>> from pathlib import Path +>>> afm_path = Path(mpl.get_data_path(), 'fonts', 'afm', 'ptmr8a.afm') +>>> +>>> from matplotlib.afm import AFM +>>> with afm_path.open('rb') as fh: +... afm = AFM(fh) +>>> afm.string_width_height('What the heck?') +(6220.0, 694) +>>> afm.get_fontname() +'Times-Roman' +>>> afm.get_kern_dist('A', 'f') +0 +>>> afm.get_kern_dist('A', 'y') +-92.0 +>>> afm.get_bbox_char('!') +[130, -9, 238, 676] + +As in the Adobe Font Metrics File Format Specification, all dimensions +are given in units of 1/1000 of the scale factor (point size) of the font +being used. +""" + +from collections import namedtuple +import logging +import re + +from ._mathtext_data import uni2type1 + + +_log = logging.getLogger(__name__) + + +def _to_int(x): + # Some AFM files have floats where we are expecting ints -- there is + # probably a better way to handle this (support floats, round rather than + # truncate). But I don't know what the best approach is now and this + # change to _to_int should at least prevent Matplotlib from crashing on + # these. JDH (2009-11-06) + return int(float(x)) + + +def _to_float(x): + # Some AFM files use "," instead of "." as decimal separator -- this + # shouldn't be ambiguous (unless someone is wicked enough to use "," as + # thousands separator...). + if isinstance(x, bytes): + # Encoding doesn't really matter -- if we have codepoints >127 the call + # to float() will error anyways. + x = x.decode('latin-1') + return float(x.replace(',', '.')) + + +def _to_str(x): + return x.decode('utf8') + + +def _to_list_of_ints(s): + s = s.replace(b',', b' ') + return [_to_int(val) for val in s.split()] + + +def _to_list_of_floats(s): + return [_to_float(val) for val in s.split()] + + +def _to_bool(s): + if s.lower().strip() in (b'false', b'0', b'no'): + return False + else: + return True + + +def _parse_header(fh): + """ + Read the font metrics header (up to the char metrics) and returns + a dictionary mapping *key* to *val*. *val* will be converted to the + appropriate python type as necessary; e.g.: + + * 'False'->False + * '0'->0 + * '-168 -218 1000 898'-> [-168, -218, 1000, 898] + + Dictionary keys are + + StartFontMetrics, FontName, FullName, FamilyName, Weight, + ItalicAngle, IsFixedPitch, FontBBox, UnderlinePosition, + UnderlineThickness, Version, Notice, EncodingScheme, CapHeight, + XHeight, Ascender, Descender, StartCharMetrics + """ + header_converters = { + b'StartFontMetrics': _to_float, + b'FontName': _to_str, + b'FullName': _to_str, + b'FamilyName': _to_str, + b'Weight': _to_str, + b'ItalicAngle': _to_float, + b'IsFixedPitch': _to_bool, + b'FontBBox': _to_list_of_ints, + b'UnderlinePosition': _to_float, + b'UnderlineThickness': _to_float, + b'Version': _to_str, + # Some AFM files have non-ASCII characters (which are not allowed by + # the spec). Given that there is actually no public API to even access + # this field, just return it as straight bytes. + b'Notice': lambda x: x, + b'EncodingScheme': _to_str, + b'CapHeight': _to_float, # Is the second version a mistake, or + b'Capheight': _to_float, # do some AFM files contain 'Capheight'? -JKS + b'XHeight': _to_float, + b'Ascender': _to_float, + b'Descender': _to_float, + b'StdHW': _to_float, + b'StdVW': _to_float, + b'StartCharMetrics': _to_int, + b'CharacterSet': _to_str, + b'Characters': _to_int, + } + d = {} + first_line = True + for line in fh: + line = line.rstrip() + if line.startswith(b'Comment'): + continue + lst = line.split(b' ', 1) + key = lst[0] + if first_line: + # AFM spec, Section 4: The StartFontMetrics keyword + # [followed by a version number] must be the first line in + # the file, and the EndFontMetrics keyword must be the + # last non-empty line in the file. We just check the + # first header entry. + if key != b'StartFontMetrics': + raise RuntimeError('Not an AFM file') + first_line = False + if len(lst) == 2: + val = lst[1] + else: + val = b'' + try: + converter = header_converters[key] + except KeyError: + _log.error('Found an unknown keyword in AFM header (was %r)' % key) + continue + try: + d[key] = converter(val) + except ValueError: + _log.error('Value error parsing header in AFM: %s, %s', key, val) + continue + if key == b'StartCharMetrics': + break + else: + raise RuntimeError('Bad parse') + return d + + +CharMetrics = namedtuple('CharMetrics', 'width, name, bbox') +CharMetrics.__doc__ = """ + Represents the character metrics of a single character. + + Notes + ----- + The fields do currently only describe a subset of character metrics + information defined in the AFM standard. + """ +CharMetrics.width.__doc__ = """The character width (WX).""" +CharMetrics.name.__doc__ = """The character name (N).""" +CharMetrics.bbox.__doc__ = """ + The bbox of the character (B) as a tuple (*llx*, *lly*, *urx*, *ury*).""" + + +def _parse_char_metrics(fh): + """ + Parse the given filehandle for character metrics information and return + the information as dicts. + + It is assumed that the file cursor is on the line behind + 'StartCharMetrics'. + + Returns + ------- + ascii_d : dict + A mapping "ASCII num of the character" to `.CharMetrics`. + name_d : dict + A mapping "character name" to `.CharMetrics`. + + Notes + ----- + This function is incomplete per the standard, but thus far parses + all the sample afm files tried. + """ + required_keys = {'C', 'WX', 'N', 'B'} + + ascii_d = {} + name_d = {} + for line in fh: + # We are defensively letting values be utf8. The spec requires + # ascii, but there are non-compliant fonts in circulation + line = _to_str(line.rstrip()) # Convert from byte-literal + if line.startswith('EndCharMetrics'): + return ascii_d, name_d + # Split the metric line into a dictionary, keyed by metric identifiers + vals = dict(s.strip().split(' ', 1) for s in line.split(';') if s) + # There may be other metrics present, but only these are needed + if not required_keys.issubset(vals): + raise RuntimeError('Bad char metrics line: %s' % line) + num = _to_int(vals['C']) + wx = _to_float(vals['WX']) + name = vals['N'] + bbox = _to_list_of_floats(vals['B']) + bbox = list(map(int, bbox)) + metrics = CharMetrics(wx, name, bbox) + # Workaround: If the character name is 'Euro', give it the + # corresponding character code, according to WinAnsiEncoding (see PDF + # Reference). + if name == 'Euro': + num = 128 + elif name == 'minus': + num = ord("\N{MINUS SIGN}") # 0x2212 + if num != -1: + ascii_d[num] = metrics + name_d[name] = metrics + raise RuntimeError('Bad parse') + + +def _parse_kern_pairs(fh): + """ + Return a kern pairs dictionary; keys are (*char1*, *char2*) tuples and + values are the kern pair value. For example, a kern pairs line like + ``KPX A y -50`` + + will be represented as:: + + d[ ('A', 'y') ] = -50 + + """ + + line = next(fh) + if not line.startswith(b'StartKernPairs'): + raise RuntimeError('Bad start of kern pairs data: %s' % line) + + d = {} + for line in fh: + line = line.rstrip() + if not line: + continue + if line.startswith(b'EndKernPairs'): + next(fh) # EndKernData + return d + vals = line.split() + if len(vals) != 4 or vals[0] != b'KPX': + raise RuntimeError('Bad kern pairs line: %s' % line) + c1, c2, val = _to_str(vals[1]), _to_str(vals[2]), _to_float(vals[3]) + d[(c1, c2)] = val + raise RuntimeError('Bad kern pairs parse') + + +CompositePart = namedtuple('CompositePart', 'name, dx, dy') +CompositePart.__doc__ = """ + Represents the information on a composite element of a composite char.""" +CompositePart.name.__doc__ = """Name of the part, e.g. 'acute'.""" +CompositePart.dx.__doc__ = """x-displacement of the part from the origin.""" +CompositePart.dy.__doc__ = """y-displacement of the part from the origin.""" + + +def _parse_composites(fh): + """ + Parse the given filehandle for composites information return them as a + dict. + + It is assumed that the file cursor is on the line behind 'StartComposites'. + + Returns + ------- + dict + A dict mapping composite character names to a parts list. The parts + list is a list of `.CompositePart` entries describing the parts of + the composite. + + Examples + -------- + A composite definition line:: + + CC Aacute 2 ; PCC A 0 0 ; PCC acute 160 170 ; + + will be represented as:: + + composites['Aacute'] = [CompositePart(name='A', dx=0, dy=0), + CompositePart(name='acute', dx=160, dy=170)] + + """ + composites = {} + for line in fh: + line = line.rstrip() + if not line: + continue + if line.startswith(b'EndComposites'): + return composites + vals = line.split(b';') + cc = vals[0].split() + name, _num_parts = cc[1], _to_int(cc[2]) + pccParts = [] + for s in vals[1:-1]: + pcc = s.split() + part = CompositePart(pcc[1], _to_float(pcc[2]), _to_float(pcc[3])) + pccParts.append(part) + composites[name] = pccParts + + raise RuntimeError('Bad composites parse') + + +def _parse_optional(fh): + """ + Parse the optional fields for kern pair data and composites. + + Returns + ------- + kern_data : dict + A dict containing kerning information. May be empty. + See `._parse_kern_pairs`. + composites : dict + A dict containing composite information. May be empty. + See `._parse_composites`. + """ + optional = { + b'StartKernData': _parse_kern_pairs, + b'StartComposites': _parse_composites, + } + + d = {b'StartKernData': {}, + b'StartComposites': {}} + for line in fh: + line = line.rstrip() + if not line: + continue + key = line.split()[0] + + if key in optional: + d[key] = optional[key](fh) + + return d[b'StartKernData'], d[b'StartComposites'] + + +class AFM: + + def __init__(self, fh): + """Parse the AFM file in file object *fh*.""" + self._header = _parse_header(fh) + self._metrics, self._metrics_by_name = _parse_char_metrics(fh) + self._kern, self._composite = _parse_optional(fh) + + def get_bbox_char(self, c, isord=False): + if not isord: + c = ord(c) + return self._metrics[c].bbox + + def string_width_height(self, s): + """ + Return the string width (including kerning) and string height + as a (*w*, *h*) tuple. + """ + if not len(s): + return 0, 0 + total_width = 0 + namelast = None + miny = 1e9 + maxy = 0 + for c in s: + if c == '\n': + continue + wx, name, bbox = self._metrics[ord(c)] + + total_width += wx + self._kern.get((namelast, name), 0) + l, b, w, h = bbox + miny = min(miny, b) + maxy = max(maxy, b + h) + + namelast = name + + return total_width, maxy - miny + + def get_str_bbox_and_descent(self, s): + """Return the string bounding box and the maximal descent.""" + if not len(s): + return 0, 0, 0, 0, 0 + total_width = 0 + namelast = None + miny = 1e9 + maxy = 0 + left = 0 + if not isinstance(s, str): + s = _to_str(s) + for c in s: + if c == '\n': + continue + name = uni2type1.get(ord(c), f"uni{ord(c):04X}") + try: + wx, _, bbox = self._metrics_by_name[name] + except KeyError: + name = 'question' + wx, _, bbox = self._metrics_by_name[name] + total_width += wx + self._kern.get((namelast, name), 0) + l, b, w, h = bbox + left = min(left, l) + miny = min(miny, b) + maxy = max(maxy, b + h) + + namelast = name + + return left, miny, total_width, maxy - miny, -miny + + def get_str_bbox(self, s): + """Return the string bounding box.""" + return self.get_str_bbox_and_descent(s)[:4] + + def get_name_char(self, c, isord=False): + """Get the name of the character, i.e., ';' is 'semicolon'.""" + if not isord: + c = ord(c) + return self._metrics[c].name + + def get_width_char(self, c, isord=False): + """ + Get the width of the character from the character metric WX field. + """ + if not isord: + c = ord(c) + return self._metrics[c].width + + def get_width_from_char_name(self, name): + """Get the width of the character from a type1 character name.""" + return self._metrics_by_name[name].width + + def get_height_char(self, c, isord=False): + """Get the bounding box (ink) height of character *c* (space is 0).""" + if not isord: + c = ord(c) + return self._metrics[c].bbox[-1] + + def get_kern_dist(self, c1, c2): + """ + Return the kerning pair distance (possibly 0) for chars *c1* and *c2*. + """ + name1, name2 = self.get_name_char(c1), self.get_name_char(c2) + return self.get_kern_dist_from_name(name1, name2) + + def get_kern_dist_from_name(self, name1, name2): + """ + Return the kerning pair distance (possibly 0) for chars + *name1* and *name2*. + """ + return self._kern.get((name1, name2), 0) + + def get_fontname(self): + """Return the font name, e.g., 'Times-Roman'.""" + return self._header[b'FontName'] + + @property + def postscript_name(self): # For consistency with FT2Font. + return self.get_fontname() + + def get_fullname(self): + """Return the font full name, e.g., 'Times-Roman'.""" + name = self._header.get(b'FullName') + if name is None: # use FontName as a substitute + name = self._header[b'FontName'] + return name + + def get_familyname(self): + """Return the font family name, e.g., 'Times'.""" + name = self._header.get(b'FamilyName') + if name is not None: + return name + + # FamilyName not specified so we'll make a guess + name = self.get_fullname() + extras = (r'(?i)([ -](regular|plain|italic|oblique|bold|semibold|' + r'light|ultralight|extra|condensed))+$') + return re.sub(extras, '', name) + + @property + def family_name(self): + """The font family name, e.g., 'Times'.""" + return self.get_familyname() + + def get_weight(self): + """Return the font weight, e.g., 'Bold' or 'Roman'.""" + return self._header[b'Weight'] + + def get_angle(self): + """Return the fontangle as float.""" + return self._header[b'ItalicAngle'] + + def get_capheight(self): + """Return the cap height as float.""" + return self._header[b'CapHeight'] + + def get_xheight(self): + """Return the xheight as float.""" + return self._header[b'XHeight'] + + def get_underline_thickness(self): + """Return the underline thickness as float.""" + return self._header[b'UnderlineThickness'] + + def get_horizontal_stem_width(self): + """ + Return the standard horizontal stem width as float, or *None* if + not specified in AFM file. + """ + return self._header.get(b'StdHW', None) + + def get_vertical_stem_width(self): + """ + Return the standard vertical stem width as float, or *None* if + not specified in AFM file. + """ + return self._header.get(b'StdVW', None) diff --git a/lib/matplotlib/_animation_data.py b/lib/matplotlib/_animation_data.py index 257f0c4a7f6f..4bf2ae3148d2 100644 --- a/lib/matplotlib/_animation_data.py +++ b/lib/matplotlib/_animation_data.py @@ -1,4 +1,4 @@ -# Javascript template for HTMLWriter +# JavaScript template for HTMLWriter JS_INCLUDE = """ @@ -196,7 +196,7 @@
+ oninput="anim{id}.set_frame(parseInt(this.value));">
diff --git a/lib/matplotlib/_api/__init__.py b/lib/matplotlib/_api/__init__.py index f251e07bab59..96ea22df4498 100644 --- a/lib/matplotlib/_api/__init__.py +++ b/lib/matplotlib/_api/__init__.py @@ -3,13 +3,14 @@ This documentation is only relevant for Matplotlib developers, not for users. -.. warning: +.. warning:: This module and its submodules are for internal use only. Do not use them in your own code. We may change the API at any time with no warning. """ +import functools import itertools import re import sys @@ -119,15 +120,15 @@ def check_in_list(_values, *, _print_supported_values=True, **kwargs): -------- >>> _api.check_in_list(["foo", "bar"], arg=arg, other_arg=other_arg) """ + if not kwargs: + raise TypeError("No argument to check!") values = _values for key, val in kwargs.items(): if val not in values: + msg = f"{val!r} is not a valid value for {key}" if _print_supported_values: - raise ValueError( - f"{val!r} is not a valid value for {key}; " - f"supported values are {', '.join(map(repr, values))}") - else: - raise ValueError(f"{val!r} is not a valid value for {key}") + msg += f"; supported values are {', '.join(map(repr, values))}" + raise ValueError(msg) def check_shape(_shape, **kwargs): @@ -191,6 +192,155 @@ def check_getitem(_mapping, **kwargs): .format(v, k, ', '.join(map(repr, mapping)))) from None +def caching_module_getattr(cls): + """ + Helper decorator for implementing module-level ``__getattr__`` as a class. + + This decorator must be used at the module toplevel as follows:: + + @caching_module_getattr + class __getattr__: # The class *must* be named ``__getattr__``. + @property # Only properties are taken into account. + def name(self): ... + + The ``__getattr__`` class will be replaced by a ``__getattr__`` + function such that trying to access ``name`` on the module will + resolve the corresponding property (which may be decorated e.g. with + ``_api.deprecated`` for deprecating module globals). The properties are + all implicitly cached. Moreover, a suitable AttributeError is generated + and raised if no property with the given name exists. + """ + + assert cls.__name__ == "__getattr__" + # Don't accidentally export cls dunders. + props = {name: prop for name, prop in vars(cls).items() + if isinstance(prop, property)} + instance = cls() + + @functools.lru_cache(None) + def __getattr__(name): + if name in props: + return props[name].__get__(instance) + raise AttributeError( + f"module {cls.__module__!r} has no attribute {name!r}") + + return __getattr__ + + +def define_aliases(alias_d, cls=None): + """ + Class decorator for defining property aliases. + + Use as :: + + @_api.define_aliases({"property": ["alias", ...], ...}) + class C: ... + + For each property, if the corresponding ``get_property`` is defined in the + class so far, an alias named ``get_alias`` will be defined; the same will + be done for setters. If neither the getter nor the setter exists, an + exception will be raised. + + The alias map is stored as the ``_alias_map`` attribute on the class and + can be used by `.normalize_kwargs` (which assumes that higher priority + aliases come last). + """ + if cls is None: # Return the actual class decorator. + return functools.partial(define_aliases, alias_d) + + def make_alias(name): # Enforce a closure over *name*. + @functools.wraps(getattr(cls, name)) + def method(self, *args, **kwargs): + return getattr(self, name)(*args, **kwargs) + return method + + for prop, aliases in alias_d.items(): + exists = False + for prefix in ["get_", "set_"]: + if prefix + prop in vars(cls): + exists = True + for alias in aliases: + method = make_alias(prefix + prop) + method.__name__ = prefix + alias + method.__doc__ = "Alias for `{}`.".format(prefix + prop) + setattr(cls, prefix + alias, method) + if not exists: + raise ValueError( + "Neither getter nor setter exists for {!r}".format(prop)) + + def get_aliased_and_aliases(d): + return {*d, *(alias for aliases in d.values() for alias in aliases)} + + preexisting_aliases = getattr(cls, "_alias_map", {}) + conflicting = (get_aliased_and_aliases(preexisting_aliases) + & get_aliased_and_aliases(alias_d)) + if conflicting: + # Need to decide on conflict resolution policy. + raise NotImplementedError( + f"Parent class already defines conflicting aliases: {conflicting}") + cls._alias_map = {**preexisting_aliases, **alias_d} + return cls + + +def select_matching_signature(funcs, *args, **kwargs): + """ + Select and call the function that accepts ``*args, **kwargs``. + + *funcs* is a list of functions which should not raise any exception (other + than `TypeError` if the arguments passed do not match their signature). + + `select_matching_signature` tries to call each of the functions in *funcs* + with ``*args, **kwargs`` (in the order in which they are given). Calls + that fail with a `TypeError` are silently skipped. As soon as a call + succeeds, `select_matching_signature` returns its return value. If no + function accepts ``*args, **kwargs``, then the `TypeError` raised by the + last failing call is re-raised. + + Callers should normally make sure that any ``*args, **kwargs`` can only + bind a single *func* (to avoid any ambiguity), although this is not checked + by `select_matching_signature`. + + Notes + ----- + `select_matching_signature` is intended to help implementing + signature-overloaded functions. In general, such functions should be + avoided, except for back-compatibility concerns. A typical use pattern is + :: + + def my_func(*args, **kwargs): + params = select_matching_signature( + [lambda old1, old2: locals(), lambda new: locals()], + *args, **kwargs) + if "old1" in params: + warn_deprecated(...) + old1, old2 = params.values() # note that locals() is ordered. + else: + new, = params.values() + # do things with params + + which allows *my_func* to be called either with two parameters (*old1* and + *old2*) or a single one (*new*). Note that the new signature is given + last, so that callers get a `TypeError` corresponding to the new signature + if the arguments they passed in do not match any signature. + """ + # Rather than relying on locals() ordering, one could have just used func's + # signature (``bound = inspect.signature(func).bind(*args, **kwargs); + # bound.apply_defaults(); return bound``) but that is significantly slower. + for i, func in enumerate(funcs): + try: + return func(*args, **kwargs) + except TypeError: + if i == len(funcs) - 1: + raise + + +def recursive_subclasses(cls): + """Yield *cls* and direct and indirect subclasses of *cls*.""" + yield cls + for subcls in cls.__subclasses__(): + yield from recursive_subclasses(subcls) + + def warn_external(message, category=None): """ `warnings.warn` wrapper that sets *stacklevel* to "outside Matplotlib". diff --git a/lib/matplotlib/_api/deprecation.py b/lib/matplotlib/_api/deprecation.py index 7c3a17965928..7c304173b2e5 100644 --- a/lib/matplotlib/_api/deprecation.py +++ b/lib/matplotlib/_api/deprecation.py @@ -3,7 +3,7 @@ This documentation is only relevant for Matplotlib developers, not for users. -.. warning: +.. warning:: This module is for internal use only. Do not use it in your own code. We may change the API at any time with no warning. @@ -13,25 +13,12 @@ import contextlib import functools import inspect +import math import warnings -class MatplotlibDeprecationWarning(UserWarning): - """ - A class for issuing deprecation warnings for Matplotlib users. - - In light of the fact that Python builtin DeprecationWarnings are ignored - by default as of Python 2.7 (see link below), this class was put in to - allow for the signaling of deprecation, but via UserWarnings which are not - ignored by default. - - https://docs.python.org/dev/whatsnew/2.7.html#the-future-for-python-2-x - """ - - -# mplDeprecation is deprecated. Use MatplotlibDeprecationWarning instead. -# remove when removing the re-import from cbook -mplDeprecation = MatplotlibDeprecationWarning +class MatplotlibDeprecationWarning(DeprecationWarning): + """A class for issuing deprecation warnings for Matplotlib users.""" def _generate_deprecation_warning( @@ -45,13 +32,11 @@ def _generate_deprecation_warning( removal = f"in {removal}" if removal else "two minor releases later" if not message: message = ( - "\nThe %(name)s %(obj_type)s" + ("The %(name)s %(obj_type)s" if obj_type else "%(name)s") + (" will be deprecated in a future version" if pending else (" was deprecated in Matplotlib %(since)s" - + (" and will be removed %(removal)s" - if removal else - ""))) + + (" and will be removed %(removal)s" if removal else ""))) + "." + (" Use %(alternative)s instead." if alternative else "") + (" %(addendum)s" if addendum else "")) @@ -72,31 +57,24 @@ def warn_deprecated( ---------- since : str The release at which this API became deprecated. - message : str, optional Override the default deprecation message. The ``%(since)s``, ``%(name)s``, ``%(alternative)s``, ``%(obj_type)s``, ``%(addendum)s``, and ``%(removal)s`` format specifiers will be replaced by the values of the respective arguments passed to this function. - name : str, optional The name of the deprecated object. - alternative : str, optional An alternative API that the user may use in place of the deprecated API. The deprecation warning will tell the user about this alternative if provided. - pending : bool, optional If True, uses a PendingDeprecationWarning instead of a DeprecationWarning. Cannot be used together with *removal*. - obj_type : str, optional The object type being deprecated. - addendum : str, optional Additional text appended directly to the final message. - removal : str, optional The expected removal version. With the default (an empty string), a removal version is automatically computed from *since*. Set to other @@ -105,7 +83,7 @@ def warn_deprecated( Examples -------- - Basic example:: + :: # To warn of the deprecation of "matplotlib.name_of_module" warn_deprecated('1.4.0', name='matplotlib.name_of_module', @@ -134,46 +112,13 @@ def deprecated(since, *, message='', name='', alternative='', pending=False, ``@deprecated`` would mess up ``__init__`` inheritance when installing its own (deprecation-emitting) ``C.__init__``). - Parameters - ---------- - since : str - The release at which this API became deprecated. - - message : str, optional - Override the default deprecation message. The ``%(since)s``, - ``%(name)s``, ``%(alternative)s``, ``%(obj_type)s``, ``%(addendum)s``, - and ``%(removal)s`` format specifiers will be replaced by the values - of the respective arguments passed to this function. - - name : str, optional - The name used in the deprecation message; if not provided, the name - is automatically determined from the deprecated object. - - alternative : str, optional - An alternative API that the user may use in place of the deprecated - API. The deprecation warning will tell the user about this alternative - if provided. - - pending : bool, optional - If True, uses a PendingDeprecationWarning instead of a - DeprecationWarning. Cannot be used together with *removal*. - - obj_type : str, optional - The object type being deprecated; by default, 'class' if decorating - a class, 'attribute' if decorating a property, 'function' otherwise. - - addendum : str, optional - Additional text appended directly to the final message. - - removal : str, optional - The expected removal version. With the default (an empty string), a - removal version is automatically computed from *since*. Set to other - Falsy values to not schedule a removal date. Cannot be used together - with *pending*. + Parameters are the same as for `warn_deprecated`, except that *obj_type* + defaults to 'class' if decorating a class, 'attribute' if decorating a + property, and 'function' otherwise. Examples -------- - Basic example:: + :: @deprecated('1.4.0') def the_function_to_deprecate(): @@ -200,13 +145,14 @@ def finalize(wrapper, new_doc): return obj elif isinstance(obj, (property, classproperty)): - obj_type = "attribute" + if obj_type is None: + obj_type = "attribute" func = None name = name or obj.fget.__name__ old_doc = obj.__doc__ class _deprecated_property(type(obj)): - def __get__(self, instance, owner): + def __get__(self, instance, owner=None): if instance is not None or owner is not None \ and isinstance(self, classproperty): emit_warning() @@ -256,10 +202,13 @@ def wrapper(*args, **kwargs): old_doc = inspect.cleandoc(old_doc or '').strip('\n') notes_header = '\nNotes\n-----' + second_arg = ' '.join([t.strip() for t in + (message, f"Use {alternative} instead." + if alternative else "", addendum) if t]) new_doc = (f"[*Deprecated*] {old_doc}\n" f"{notes_header if notes_header not in old_doc else ''}\n" f".. deprecated:: {since}\n" - f" {message.strip()}") + f" {second_arg}") if not old_doc: # This is to prevent a spurious 'unexpected unindent' warning from @@ -273,7 +222,7 @@ def wrapper(*args, **kwargs): class deprecate_privatize_attribute: """ - Helper to deprecate public access to an attribute. + Helper to deprecate public access to an attribute (or method). This helper should only be used at class scope, as follows:: @@ -281,9 +230,10 @@ class Foo: attr = _deprecate_privatize_attribute(*args, **kwargs) where *all* parameters are forwarded to `deprecated`. This form makes - ``attr`` a property which forwards access to ``self._attr`` (same name but - with a leading underscore), with a deprecation warning. Note that the - attribute name is derived from *the name this helper is assigned to*. + ``attr`` a property which forwards read and write access to ``self._attr`` + (same name but with a leading underscore), with a deprecation warning. + Note that the attribute name is derived from *the name this helper is + assigned to*. This helper also works for deprecating methods. """ def __init__(self, *args, **kwargs): @@ -291,7 +241,17 @@ def __init__(self, *args, **kwargs): def __set_name__(self, owner, name): setattr(owner, name, self.deprecator( - property(lambda self: getattr(self, f"_{name}")), name=name)) + property(lambda self: getattr(self, f"_{name}"), + lambda self, value: setattr(self, f"_{name}", value)), + name=name)) + + +# Used by _copy_docstring_and_deprecators to redecorate pyplot wrappers and +# boilerplate.py to retrieve original signatures. It may seem natural to store +# this information as an attribute on the wrapper, but if the wrapper gets +# itself functools.wraps()ed, then such attributes are silently propagated to +# the outer wrapper, which is not desired. +DECORATORS = {} def rename_parameter(since, old, new, func=None): @@ -313,8 +273,10 @@ def rename_parameter(since, old, new, func=None): def func(good_name): ... """ + decorator = functools.partial(rename_parameter, since, old, new) + if func is None: - return functools.partial(rename_parameter, since, old, new) + return decorator signature = inspect.signature(func) assert old not in signature.parameters, ( @@ -339,6 +301,7 @@ def wrapper(*args, **kwargs): # would both show up in the pyplot function for an Axes method as well and # pyplot would explicitly pass both arguments to the Axes method. + DECORATORS[wrapper] = decorator return wrapper @@ -375,8 +338,10 @@ def delete_parameter(since, name, func=None, **kwargs): def func(used_arg, other_arg, unused, more_args): ... """ + decorator = functools.partial(delete_parameter, since, name, **kwargs) + if func is None: - return functools.partial(delete_parameter, since, name, **kwargs) + return decorator signature = inspect.signature(func) # Name of `**kwargs` parameter of the decorated function, typically @@ -389,12 +354,22 @@ def func(used_arg, other_arg, unused, more_args): ... is_varargs = kind is inspect.Parameter.VAR_POSITIONAL is_varkwargs = kind is inspect.Parameter.VAR_KEYWORD if not is_varargs and not is_varkwargs: + name_idx = ( + # Deprecated parameter can't be passed positionally. + math.inf if kind is inspect.Parameter.KEYWORD_ONLY + # If call site has no more than this number of parameters, the + # deprecated parameter can't have been passed positionally. + else [*signature.parameters].index(name)) func.__signature__ = signature = signature.replace(parameters=[ param.replace(default=_deprecated_parameter) if param.name == name else param for param in signature.parameters.values()]) + else: + name_idx = -1 # Deprecated parameter can always have been passed. else: is_varargs = is_varkwargs = False + # Deprecated parameter can't be passed positionally. + name_idx = math.inf assert kwargs_name, ( f"Matplotlib internal error: {name!r} must be a parameter for " f"{func.__name__}()") @@ -403,6 +378,10 @@ def func(used_arg, other_arg, unused, more_args): ... @functools.wraps(func) def wrapper(*inner_args, **inner_kwargs): + if len(inner_args) <= name_idx and name not in inner_kwargs: + # Early return in the simple, non-deprecated case (much faster than + # calling bind()). + return func(*inner_args, **inner_kwargs) arguments = signature.bind(*inner_args, **inner_kwargs).arguments if is_varargs and arguments.get(name): warn_deprecated( @@ -430,6 +409,7 @@ def wrapper(*inner_args, **inner_kwargs): **kwargs) return func(*inner_args, **inner_kwargs) + DECORATORS[wrapper] = decorator return wrapper @@ -437,10 +417,16 @@ def make_keyword_only(since, name, func=None): """ Decorator indicating that passing parameter *name* (or any of the following ones) positionally to *func* is being deprecated. + + When used on a method that has a pyplot wrapper, this should be the + outermost decorator, so that :file:`boilerplate.py` can access the original + signature. """ + decorator = functools.partial(make_keyword_only, since, name) + if func is None: - return functools.partial(make_keyword_only, since, name) + return decorator signature = inspect.signature(func) POK = inspect.Parameter.POSITIONAL_OR_KEYWORD @@ -450,19 +436,16 @@ def make_keyword_only(since, name, func=None): f"Matplotlib internal error: {name!r} must be a positional-or-keyword " f"parameter for {func.__name__}()") names = [*signature.parameters] - kwonly = [name for name in names[names.index(name):] + name_idx = names.index(name) + kwonly = [name for name in names[name_idx:] if signature.parameters[name].kind == POK] - func.__signature__ = signature.replace(parameters=[ - param.replace(kind=KWO) if param.name in kwonly else param - for param in signature.parameters.values()]) @functools.wraps(func) def wrapper(*args, **kwargs): # Don't use signature.bind here, as it would fail when stacked with # rename_parameter and an "old" argument name is passed in # (signature.bind would fail, but the actual call would succeed). - idx = [*func.__signature__.parameters].index(name) - if len(args) > idx: + if len(args) > name_idx: warn_deprecated( since, message="Passing the %(name)s %(obj_type)s " "positionally is deprecated since Matplotlib %(since)s; the " @@ -470,6 +453,11 @@ def wrapper(*args, **kwargs): name=name, obj_type=f"parameter of {func.__name__}()") return func(*args, **kwargs) + # Don't modify *func*'s signature, as boilerplate.py needs it. + wrapper.__signature__ = signature.replace(parameters=[ + param.replace(kind=KWO) if param.name in kwonly else param + for param in signature.parameters.values()]) + DECORATORS[wrapper] = decorator return wrapper diff --git a/lib/matplotlib/_blocking_input.py b/lib/matplotlib/_blocking_input.py new file mode 100644 index 000000000000..45f077571443 --- /dev/null +++ b/lib/matplotlib/_blocking_input.py @@ -0,0 +1,30 @@ +def blocking_input_loop(figure, event_names, timeout, handler): + """ + Run *figure*'s event loop while listening to interactive events. + + The events listed in *event_names* are passed to *handler*. + + This function is used to implement `.Figure.waitforbuttonpress`, + `.Figure.ginput`, and `.Axes.clabel`. + + Parameters + ---------- + figure : `~matplotlib.figure.Figure` + event_names : list of str + The names of the events passed to *handler*. + timeout : float + If positive, the event loop is stopped after *timeout* seconds. + handler : Callable[[Event], Any] + Function called for each event; it can force an early exit of the event + loop by calling ``canvas.stop_event_loop()``. + """ + if figure.canvas.manager: + figure.show() # Ensure that the figure is shown if we are managing it. + # Connect the events to the on_event function call. + cids = [figure.canvas.mpl_connect(name, handler) for name in event_names] + try: + figure.canvas.start_event_loop(timeout) # Start event loop. + finally: # Run even on exception like ctrl-c. + # Disconnect the callbacks. + for cid in cids: + figure.canvas.mpl_disconnect(cid) diff --git a/lib/matplotlib/_cm.py b/lib/matplotlib/_cm.py index 84b2fd3799f2..586417d53954 100644 --- a/lib/matplotlib/_cm.py +++ b/lib/matplotlib/_cm.py @@ -459,44 +459,50 @@ def _g36(x): return 2 * x - 1 'blue': ((0., 1., 1.), (1.0, 0.5, 0.5))} _nipy_spectral_data = { - 'red': [(0.0, 0.0, 0.0), (0.05, 0.4667, 0.4667), - (0.10, 0.5333, 0.5333), (0.15, 0.0, 0.0), - (0.20, 0.0, 0.0), (0.25, 0.0, 0.0), - (0.30, 0.0, 0.0), (0.35, 0.0, 0.0), - (0.40, 0.0, 0.0), (0.45, 0.0, 0.0), - (0.50, 0.0, 0.0), (0.55, 0.0, 0.0), - (0.60, 0.0, 0.0), (0.65, 0.7333, 0.7333), - (0.70, 0.9333, 0.9333), (0.75, 1.0, 1.0), - (0.80, 1.0, 1.0), (0.85, 1.0, 1.0), - (0.90, 0.8667, 0.8667), (0.95, 0.80, 0.80), - (1.0, 0.80, 0.80)], - 'green': [(0.0, 0.0, 0.0), (0.05, 0.0, 0.0), - (0.10, 0.0, 0.0), (0.15, 0.0, 0.0), - (0.20, 0.0, 0.0), (0.25, 0.4667, 0.4667), - (0.30, 0.6000, 0.6000), (0.35, 0.6667, 0.6667), - (0.40, 0.6667, 0.6667), (0.45, 0.6000, 0.6000), - (0.50, 0.7333, 0.7333), (0.55, 0.8667, 0.8667), - (0.60, 1.0, 1.0), (0.65, 1.0, 1.0), - (0.70, 0.9333, 0.9333), (0.75, 0.8000, 0.8000), - (0.80, 0.6000, 0.6000), (0.85, 0.0, 0.0), - (0.90, 0.0, 0.0), (0.95, 0.0, 0.0), - (1.0, 0.80, 0.80)], - 'blue': [(0.0, 0.0, 0.0), (0.05, 0.5333, 0.5333), - (0.10, 0.6000, 0.6000), (0.15, 0.6667, 0.6667), - (0.20, 0.8667, 0.8667), (0.25, 0.8667, 0.8667), - (0.30, 0.8667, 0.8667), (0.35, 0.6667, 0.6667), - (0.40, 0.5333, 0.5333), (0.45, 0.0, 0.0), - (0.5, 0.0, 0.0), (0.55, 0.0, 0.0), - (0.60, 0.0, 0.0), (0.65, 0.0, 0.0), - (0.70, 0.0, 0.0), (0.75, 0.0, 0.0), - (0.80, 0.0, 0.0), (0.85, 0.0, 0.0), - (0.90, 0.0, 0.0), (0.95, 0.0, 0.0), - (1.0, 0.80, 0.80)], + 'red': [ + (0.0, 0.0, 0.0), (0.05, 0.4667, 0.4667), + (0.10, 0.5333, 0.5333), (0.15, 0.0, 0.0), + (0.20, 0.0, 0.0), (0.25, 0.0, 0.0), + (0.30, 0.0, 0.0), (0.35, 0.0, 0.0), + (0.40, 0.0, 0.0), (0.45, 0.0, 0.0), + (0.50, 0.0, 0.0), (0.55, 0.0, 0.0), + (0.60, 0.0, 0.0), (0.65, 0.7333, 0.7333), + (0.70, 0.9333, 0.9333), (0.75, 1.0, 1.0), + (0.80, 1.0, 1.0), (0.85, 1.0, 1.0), + (0.90, 0.8667, 0.8667), (0.95, 0.80, 0.80), + (1.0, 0.80, 0.80), + ], + 'green': [ + (0.0, 0.0, 0.0), (0.05, 0.0, 0.0), + (0.10, 0.0, 0.0), (0.15, 0.0, 0.0), + (0.20, 0.0, 0.0), (0.25, 0.4667, 0.4667), + (0.30, 0.6000, 0.6000), (0.35, 0.6667, 0.6667), + (0.40, 0.6667, 0.6667), (0.45, 0.6000, 0.6000), + (0.50, 0.7333, 0.7333), (0.55, 0.8667, 0.8667), + (0.60, 1.0, 1.0), (0.65, 1.0, 1.0), + (0.70, 0.9333, 0.9333), (0.75, 0.8000, 0.8000), + (0.80, 0.6000, 0.6000), (0.85, 0.0, 0.0), + (0.90, 0.0, 0.0), (0.95, 0.0, 0.0), + (1.0, 0.80, 0.80), + ], + 'blue': [ + (0.0, 0.0, 0.0), (0.05, 0.5333, 0.5333), + (0.10, 0.6000, 0.6000), (0.15, 0.6667, 0.6667), + (0.20, 0.8667, 0.8667), (0.25, 0.8667, 0.8667), + (0.30, 0.8667, 0.8667), (0.35, 0.6667, 0.6667), + (0.40, 0.5333, 0.5333), (0.45, 0.0, 0.0), + (0.5, 0.0, 0.0), (0.55, 0.0, 0.0), + (0.60, 0.0, 0.0), (0.65, 0.0, 0.0), + (0.70, 0.0, 0.0), (0.75, 0.0, 0.0), + (0.80, 0.0, 0.0), (0.85, 0.0, 0.0), + (0.90, 0.0, 0.0), (0.95, 0.0, 0.0), + (1.0, 0.80, 0.80), + ], } # 34 colormaps based on color specifications and designs -# developed by Cynthia Brewer (http://colorbrewer.org). +# developed by Cynthia Brewer (https://colorbrewer2.org/). # The ColorBrewer palettes have been included under the terms # of an Apache-stype license (for details, see the file # LICENSE_COLORBREWER in the license directory of the matplotlib @@ -1207,7 +1213,7 @@ def _gist_yarg(x): return 1 - x # Implementation of Carey Rappaport's CMRmap. # See `A Color Map for Effective Black-and-White Rendering of Color-Scale # Images' by Carey Rappaport -# http://www.mathworks.com/matlabcentral/fileexchange/2662-cmrmap-m +# https://www.mathworks.com/matlabcentral/fileexchange/2662-cmrmap-m _CMRmap_data = {'red': ((0.000, 0.00, 0.00), (0.125, 0.15, 0.15), (0.250, 0.30, 0.30), @@ -1239,7 +1245,7 @@ def _gist_yarg(x): return 1 - x # An MIT licensed, colorblind-friendly heatmap from Wistia: # https://github.com/wistia/heatmap-palette -# http://wistia.com/blog/heatmaps-for-colorblindness +# https://wistia.com/learn/culture/heatmaps-for-colorblindness # # >>> import matplotlib.colors as c # >>> colors = ["#e4ff7a", "#ffe81a", "#ffbd00", "#ffa000", "#fc7f00"] diff --git a/lib/matplotlib/_color_data.py b/lib/matplotlib/_color_data.py index e50998b18fd5..44f97adbb76a 100644 --- a/lib/matplotlib/_color_data.py +++ b/lib/matplotlib/_color_data.py @@ -1,6 +1,3 @@ -from collections import OrderedDict - - BASE_COLORS = { 'b': (0, 0, 1), # blue 'g': (0, 0.5, 0), # green @@ -14,32 +11,29 @@ # These colors are from Tableau -TABLEAU_COLORS = ( - ('blue', '#1f77b4'), - ('orange', '#ff7f0e'), - ('green', '#2ca02c'), - ('red', '#d62728'), - ('purple', '#9467bd'), - ('brown', '#8c564b'), - ('pink', '#e377c2'), - ('gray', '#7f7f7f'), - ('olive', '#bcbd22'), - ('cyan', '#17becf'), -) +TABLEAU_COLORS = { + 'tab:blue': '#1f77b4', + 'tab:orange': '#ff7f0e', + 'tab:green': '#2ca02c', + 'tab:red': '#d62728', + 'tab:purple': '#9467bd', + 'tab:brown': '#8c564b', + 'tab:pink': '#e377c2', + 'tab:gray': '#7f7f7f', + 'tab:olive': '#bcbd22', + 'tab:cyan': '#17becf', +} -# Normalize name to "tab:" to avoid name collisions. -TABLEAU_COLORS = OrderedDict( - ('tab:' + name, value) for name, value in TABLEAU_COLORS) # This mapping of color names -> hex values is taken from # a survey run by Randall Munroe see: # https://blog.xkcd.com/2010/05/03/color-survey-results/ # for more details. The results are hosted at -# https://xkcd.com/color/rgb +# https://xkcd.com/color/rgb/ # and also available as a text file at # https://xkcd.com/color/rgb.txt # -# License: http://creativecommons.org/publicdomain/zero/1.0/ +# License: https://creativecommons.org/publicdomain/zero/1.0/ XKCD_COLORS = { 'cloudy blue': '#acc2d9', 'dark pastel green': '#56ae57', diff --git a/lib/matplotlib/_constrained_layout.py b/lib/matplotlib/_constrained_layout.py index 58bb0ccabf95..5b5e0b9cf642 100644 --- a/lib/matplotlib/_constrained_layout.py +++ b/lib/matplotlib/_constrained_layout.py @@ -11,18 +11,7 @@ layout. Axes manually placed via ``figure.add_axes()`` will not. See Tutorial: :doc:`/tutorials/intermediate/constrainedlayout_guide` -""" - -import logging - -import numpy as np - -from matplotlib import _api -import matplotlib.transforms as mtransforms -_log = logging.getLogger(__name__) - -""" General idea: ------------- @@ -31,8 +20,8 @@ often just set to 1 for an equal grid. Subplotspecs that are derived from this gridspec can contain either a -``SubPanel``, a ``GridSpecFromSubplotSpec``, or an axes. The ``SubPanel`` and -``GridSpecFromSubplotSpec`` are dealt with recursively and each contain an +``SubPanel``, a ``GridSpecFromSubplotSpec``, or an ``Axes``. The ``SubPanel`` +and ``GridSpecFromSubplotSpec`` are dealt with recursively and each contain an analogous layout. Each ``GridSpec`` has a ``_layoutgrid`` attached to it. The ``_layoutgrid`` @@ -58,10 +47,22 @@ for more discussion of the algorithm with examples. """ +import logging + +import numpy as np + +from matplotlib import _api, artist as martist +import matplotlib.transforms as mtransforms +import matplotlib._layoutgrid as mlayoutgrid + + +_log = logging.getLogger(__name__) + ###################################################### -def do_constrained_layout(fig, renderer, h_pad, w_pad, - hspace=None, wspace=None): +def do_constrained_layout(fig, h_pad, w_pad, + hspace=None, wspace=None, rect=(0, 0, 1, 1), + compress=False): """ Do the constrained_layout. Called at draw time in ``figure.constrained_layout()`` @@ -83,20 +84,29 @@ def do_constrained_layout(fig, renderer, h_pad, w_pad, A value of 0.2 for a three-column layout would have a space of 0.1 of the figure width between each column. If h/wspace < h/w_pad, then the pads are used instead. + + rect : tuple of 4 floats + Rectangle in figure coordinates to perform constrained layout in + [left, bottom, width, height], each from 0-1. + + compress : bool + Whether to shift Axes so that white space in between them is + removed. This is useful for simple grids of fixed-aspect Axes (e.g. + a grid of images). + + Returns + ------- + layoutgrid : private debugging structure """ - # list of unique gridspecs that contain child axes: - gss = set() - for ax in fig.axes: - if hasattr(ax, 'get_subplotspec'): - gs = ax.get_subplotspec().get_gridspec() - if gs._layoutgrid is not None: - gss.add(gs) - gss = list(gss) - if len(gss) == 0: + renderer = fig._get_renderer() + # make layoutgrid tree... + layoutgrids = make_layoutgrids(fig, None, rect=rect) + if not layoutgrids['hasgrids']: _api.warn_external('There are no gridspecs with layoutgrids. ' 'Possibly did not call parent GridSpec with the' ' "figure" keyword') + return for _ in range(2): # do the algorithm twice. This has to be done because decorations @@ -106,42 +116,144 @@ def do_constrained_layout(fig, renderer, h_pad, w_pad, # make margins for all the axes and subfigures in the # figure. Add margins for colorbars... - _make_layout_margins(fig, renderer, h_pad=h_pad, w_pad=w_pad, - hspace=hspace, wspace=wspace) - _make_margin_suptitles(fig, renderer, h_pad=h_pad, w_pad=w_pad) + make_layout_margins(layoutgrids, fig, renderer, h_pad=h_pad, + w_pad=w_pad, hspace=hspace, wspace=wspace) + make_margin_suptitles(layoutgrids, fig, renderer, h_pad=h_pad, + w_pad=w_pad) # if a layout is such that a columns (or rows) margin has no # constraints, we need to make all such instances in the grid # match in margin size. - _match_submerged_margins(fig) + match_submerged_margins(layoutgrids, fig) # update all the variables in the layout. - fig._layoutgrid.update_variables() - - if _check_no_collapsed_axes(fig): - _reposition_axes(fig, renderer, h_pad=h_pad, w_pad=w_pad, - hspace=hspace, wspace=wspace) + layoutgrids[fig].update_variables() + + warn_collapsed = ('constrained_layout not applied because ' + 'axes sizes collapsed to zero. Try making ' + 'figure larger or axes decorations smaller.') + if check_no_collapsed_axes(layoutgrids, fig): + reposition_axes(layoutgrids, fig, renderer, h_pad=h_pad, + w_pad=w_pad, hspace=hspace, wspace=wspace) + if compress: + layoutgrids = compress_fixed_aspect(layoutgrids, fig) + layoutgrids[fig].update_variables() + if check_no_collapsed_axes(layoutgrids, fig): + reposition_axes(layoutgrids, fig, renderer, h_pad=h_pad, + w_pad=w_pad, hspace=hspace, wspace=wspace) + else: + _api.warn_external(warn_collapsed) else: - _api.warn_external('constrained_layout not applied because ' - 'axes sizes collapsed to zero. Try making ' - 'figure larger or axes decorations smaller.') - _reset_margins(fig) + _api.warn_external(warn_collapsed) + reset_margins(layoutgrids, fig) + return layoutgrids -def _check_no_collapsed_axes(fig): +def make_layoutgrids(fig, layoutgrids, rect=(0, 0, 1, 1)): + """ + Make the layoutgrid tree. + + (Sub)Figures get a layoutgrid so we can have figure margins. + + Gridspecs that are attached to axes get a layoutgrid so axes + can have margins. + """ + + if layoutgrids is None: + layoutgrids = dict() + layoutgrids['hasgrids'] = False + if not hasattr(fig, '_parent'): + # top figure; pass rect as parent to allow user-specified + # margins + layoutgrids[fig] = mlayoutgrid.LayoutGrid(parent=rect, name='figlb') + else: + # subfigure + gs = fig._subplotspec.get_gridspec() + # it is possible the gridspec containing this subfigure hasn't + # been added to the tree yet: + layoutgrids = make_layoutgrids_gs(layoutgrids, gs) + # add the layoutgrid for the subfigure: + parentlb = layoutgrids[gs] + layoutgrids[fig] = mlayoutgrid.LayoutGrid( + parent=parentlb, + name='panellb', + parent_inner=True, + nrows=1, ncols=1, + parent_pos=(fig._subplotspec.rowspan, + fig._subplotspec.colspan)) + # recursively do all subfigures in this figure... + for sfig in fig.subfigs: + layoutgrids = make_layoutgrids(sfig, layoutgrids) + + # for each axes at the local level add its gridspec: + for ax in fig._localaxes: + if hasattr(ax, 'get_subplotspec'): + gs = ax.get_subplotspec().get_gridspec() + layoutgrids = make_layoutgrids_gs(layoutgrids, gs) + + return layoutgrids + + +def make_layoutgrids_gs(layoutgrids, gs): + """ + Make the layoutgrid for a gridspec (and anything nested in the gridspec) + """ + + if gs in layoutgrids or gs.figure is None: + return layoutgrids + # in order to do constrained_layout there has to be at least *one* + # gridspec in the tree: + layoutgrids['hasgrids'] = True + if not hasattr(gs, '_subplot_spec'): + # normal gridspec + parent = layoutgrids[gs.figure] + layoutgrids[gs] = mlayoutgrid.LayoutGrid( + parent=parent, + parent_inner=True, + name='gridspec', + ncols=gs._ncols, nrows=gs._nrows, + width_ratios=gs.get_width_ratios(), + height_ratios=gs.get_height_ratios()) + else: + # this is a gridspecfromsubplotspec: + subplot_spec = gs._subplot_spec + parentgs = subplot_spec.get_gridspec() + # if a nested gridspec it is possible the parent is not in there yet: + if parentgs not in layoutgrids: + layoutgrids = make_layoutgrids_gs(layoutgrids, parentgs) + subspeclb = layoutgrids[parentgs] + # gridspecfromsubplotspec need an outer container: + # get a unique representation: + rep = (gs, 'top') + if rep not in layoutgrids: + layoutgrids[rep] = mlayoutgrid.LayoutGrid( + parent=subspeclb, + name='top', + nrows=1, ncols=1, + parent_pos=(subplot_spec.rowspan, subplot_spec.colspan)) + layoutgrids[gs] = mlayoutgrid.LayoutGrid( + parent=layoutgrids[rep], + name='gridspec', + nrows=gs._nrows, ncols=gs._ncols, + width_ratios=gs.get_width_ratios(), + height_ratios=gs.get_height_ratios()) + return layoutgrids + + +def check_no_collapsed_axes(layoutgrids, fig): """ Check that no axes have collapsed to zero size. """ - for panel in fig.subfigs: - ok = _check_no_collapsed_axes(panel) + for sfig in fig.subfigs: + ok = check_no_collapsed_axes(layoutgrids, sfig) if not ok: return False for ax in fig.axes: if hasattr(ax, 'get_subplotspec'): gs = ax.get_subplotspec().get_gridspec() - lg = gs._layoutgrid - if lg is not None: + if gs in layoutgrids: + lg = layoutgrids[gs] for i in range(gs.nrows): for j in range(gs.ncols): bb = lg.get_inner_bbox(i, j) @@ -150,12 +262,48 @@ def _check_no_collapsed_axes(fig): return True -def _get_margin_from_padding(object, *, w_pad=0, h_pad=0, - hspace=0, wspace=0): - - ss = object._subplotspec +def compress_fixed_aspect(layoutgrids, fig): + gs = None + for ax in fig.axes: + if not hasattr(ax, 'get_subplotspec'): + continue + ax.apply_aspect() + sub = ax.get_subplotspec() + _gs = sub.get_gridspec() + if gs is None: + gs = _gs + extraw = np.zeros(gs.ncols) + extrah = np.zeros(gs.nrows) + elif _gs != gs: + raise ValueError('Cannot do compressed layout if axes are not' + 'all from the same gridspec') + orig = ax.get_position(original=True) + actual = ax.get_position(original=False) + dw = orig.width - actual.width + if dw > 0: + extraw[sub.colspan] = np.maximum(extraw[sub.colspan], dw) + dh = orig.height - actual.height + if dh > 0: + extrah[sub.rowspan] = np.maximum(extrah[sub.rowspan], dh) + + if gs is None: + raise ValueError('Cannot do compressed layout if no axes ' + 'are part of a gridspec.') + w = np.sum(extraw) / 2 + layoutgrids[fig].edit_margin_min('left', w) + layoutgrids[fig].edit_margin_min('right', w) + + h = np.sum(extrah) / 2 + layoutgrids[fig].edit_margin_min('top', h) + layoutgrids[fig].edit_margin_min('bottom', h) + return layoutgrids + + +def get_margin_from_padding(obj, *, w_pad=0, h_pad=0, + hspace=0, wspace=0): + + ss = obj._subplotspec gs = ss.get_gridspec() - lg = gs._layoutgrid if hasattr(gs, 'hspace'): _hspace = (gs.hspace if gs.hspace is not None else hspace) @@ -189,8 +337,8 @@ def _get_margin_from_padding(object, *, w_pad=0, h_pad=0, return margin -def _make_layout_margins(fig, renderer, *, w_pad=0, h_pad=0, - hspace=0, wspace=0): +def make_layout_margins(layoutgrids, fig, renderer, *, w_pad=0, h_pad=0, + hspace=0, wspace=0): """ For each axes, make a margin between the *pos* layoutbox and the *axes* layoutbox be a minimum size that can accommodate the @@ -198,30 +346,29 @@ def _make_layout_margins(fig, renderer, *, w_pad=0, h_pad=0, Then make room for colorbars. """ - for panel in fig.subfigs: # recursively make child panel margins - ss = panel._subplotspec - _make_layout_margins(panel, renderer, w_pad=w_pad, h_pad=h_pad, - hspace=hspace, wspace=wspace) + for sfig in fig.subfigs: # recursively make child panel margins + ss = sfig._subplotspec + make_layout_margins(layoutgrids, sfig, renderer, + w_pad=w_pad, h_pad=h_pad, + hspace=hspace, wspace=wspace) - margins = _get_margin_from_padding(panel, w_pad=0, h_pad=0, - hspace=hspace, wspace=wspace) - panel._layoutgrid.parent.edit_outer_margin_mins(margins, ss) + margins = get_margin_from_padding(sfig, w_pad=0, h_pad=0, + hspace=hspace, wspace=wspace) + layoutgrids[sfig].parent.edit_outer_margin_mins(margins, ss) - for ax in fig._localaxes.as_list(): + for ax in fig._localaxes: if not hasattr(ax, 'get_subplotspec') or not ax.get_in_layout(): continue ss = ax.get_subplotspec() gs = ss.get_gridspec() - nrows, ncols = gs.get_geometry() - if gs._layoutgrid is None: + if gs not in layoutgrids: return - margin = _get_margin_from_padding(ax, w_pad=w_pad, h_pad=h_pad, - hspace=hspace, wspace=wspace) - margin0 = margin.copy() - pos, bbox = _get_pos_and_bbox(ax, renderer) + margin = get_margin_from_padding(ax, w_pad=w_pad, h_pad=h_pad, + hspace=hspace, wspace=wspace) + pos, bbox = get_pos_and_bbox(ax, renderer) # the margin is the distance between the bounding box of the axes # and its position (plus the padding from above) margin['left'] += pos.x0 - bbox.x0 @@ -234,11 +381,11 @@ def _make_layout_margins(fig, renderer, *, w_pad=0, h_pad=0, # padding margin, versus the margin for axes decorators. for cbax in ax._colorbars: # note pad is a fraction of the parent width... - pad = _colorbar_get_pad(cbax) + pad = colorbar_get_pad(layoutgrids, cbax) # colorbars can be child of more than one subplot spec: - cbp_rspan, cbp_cspan = _get_cb_parent_spans(cbax) + cbp_rspan, cbp_cspan = get_cb_parent_spans(cbax) loc = cbax._colorbar_info['location'] - cbpos, cbbbox = _get_pos_and_bbox(cbax, renderer) + cbpos, cbbbox = get_pos_and_bbox(cbax, renderer) if loc == 'right': if cbp_cspan.stop == ss.colspan.stop: # only increase if the colorbar is on the right edge @@ -271,10 +418,10 @@ def _make_layout_margins(fig, renderer, *, w_pad=0, h_pad=0, cbbbox.y1 > bbox.y1): margin['top'] += cbbbox.y1 - bbox.y1 # pass the new margins down to the layout grid for the solution... - gs._layoutgrid.edit_outer_margin_mins(margin, ss) + layoutgrids[gs].edit_outer_margin_mins(margin, ss) -def _make_margin_suptitles(fig, renderer, *, w_pad=0, h_pad=0): +def make_margin_suptitles(layoutgrids, fig, renderer, *, w_pad=0, h_pad=0): # Figure out how large the suptitle is and make the # top level figure margin larger. @@ -286,29 +433,34 @@ def _make_margin_suptitles(fig, renderer, *, w_pad=0, h_pad=0): h_pad_local = padbox.height w_pad_local = padbox.width - for panel in fig.subfigs: - _make_margin_suptitles(panel, renderer, w_pad=w_pad, h_pad=h_pad) + for sfig in fig.subfigs: + make_margin_suptitles(layoutgrids, sfig, renderer, + w_pad=w_pad, h_pad=h_pad) if fig._suptitle is not None and fig._suptitle.get_in_layout(): p = fig._suptitle.get_position() - fig._suptitle.set_position((p[0], 1 - h_pad_local)) - bbox = inv_trans_fig(fig._suptitle.get_tightbbox(renderer)) - fig._layoutgrid.edit_margin_min('top', bbox.height + 2.0 * h_pad) + if getattr(fig._suptitle, '_autopos', False): + fig._suptitle.set_position((p[0], 1 - h_pad_local)) + bbox = inv_trans_fig(fig._suptitle.get_tightbbox(renderer)) + layoutgrids[fig].edit_margin_min('top', bbox.height + 2 * h_pad) if fig._supxlabel is not None and fig._supxlabel.get_in_layout(): p = fig._supxlabel.get_position() - fig._supxlabel.set_position((p[0], h_pad_local)) - bbox = inv_trans_fig(fig._supxlabel.get_tightbbox(renderer)) - fig._layoutgrid.edit_margin_min('bottom', bbox.height + 2.0 * h_pad) + if getattr(fig._supxlabel, '_autopos', False): + fig._supxlabel.set_position((p[0], h_pad_local)) + bbox = inv_trans_fig(fig._supxlabel.get_tightbbox(renderer)) + layoutgrids[fig].edit_margin_min('bottom', + bbox.height + 2 * h_pad) - if fig._supylabel is not None and fig._supxlabel.get_in_layout(): + if fig._supylabel is not None and fig._supylabel.get_in_layout(): p = fig._supylabel.get_position() - fig._supylabel.set_position((w_pad_local, p[1])) - bbox = inv_trans_fig(fig._supylabel.get_tightbbox(renderer)) - fig._layoutgrid.edit_margin_min('left', bbox.width + 2.0 * w_pad) + if getattr(fig._supylabel, '_autopos', False): + fig._supylabel.set_position((w_pad_local, p[1])) + bbox = inv_trans_fig(fig._supylabel.get_tightbbox(renderer)) + layoutgrids[fig].edit_margin_min('left', bbox.width + 2 * w_pad) -def _match_submerged_margins(fig): +def match_submerged_margins(layoutgrids, fig): """ Make the margins that are submerged inside an Axes the same size. @@ -333,18 +485,18 @@ def _match_submerged_margins(fig): See test_constrained_layout::test_constrained_layout12 for an example. """ - for panel in fig.subfigs: - _match_submerged_margins(panel) + for sfig in fig.subfigs: + match_submerged_margins(layoutgrids, sfig) axs = [a for a in fig.get_axes() if (hasattr(a, 'get_subplotspec') and a.get_in_layout())] for ax1 in axs: ss1 = ax1.get_subplotspec() - lg1 = ss1.get_gridspec()._layoutgrid - if lg1 is None: + if ss1.get_gridspec() not in layoutgrids: axs.remove(ax1) continue + lg1 = layoutgrids[ss1.get_gridspec()] # interior columns: if len(ss1.colspan) > 1: @@ -358,7 +510,7 @@ def _match_submerged_margins(fig): ) for ax2 in axs: ss2 = ax2.get_subplotspec() - lg2 = ss2.get_gridspec()._layoutgrid + lg2 = layoutgrids[ss2.get_gridspec()] if lg2 is not None and len(ss2.colspan) > 1: maxsubl2 = np.max( lg2.margin_vals['left'][ss2.colspan[1:]] + @@ -388,7 +540,7 @@ def _match_submerged_margins(fig): for ax2 in axs: ss2 = ax2.get_subplotspec() - lg2 = ss2.get_gridspec()._layoutgrid + lg2 = layoutgrids[ss2.get_gridspec()] if lg2 is not None: if len(ss2.rowspan) > 1: maxsubt = np.max([np.max( @@ -405,7 +557,7 @@ def _match_submerged_margins(fig): lg1.edit_margin_min('bottom', maxsubb, cell=i) -def _get_cb_parent_spans(cbax): +def get_cb_parent_spans(cbax): """ Figure out which subplotspecs this colorbar belongs to: """ @@ -425,7 +577,7 @@ def _get_cb_parent_spans(cbax): return rowspan, colspan -def _get_pos_and_bbox(ax, renderer): +def get_pos_and_bbox(ax, renderer): """ Get the position and the bbox for the axes. @@ -440,17 +592,12 @@ def _get_pos_and_bbox(ax, renderer): Position in figure coordinates. bbox : Bbox Tight bounding box in figure coordinates. - """ fig = ax.figure pos = ax.get_position(original=True) # pos is in panel co-ords, but we need in figure for the layout pos = pos.transformed(fig.transSubfigure - fig.transFigure) - try: - tightbbox = ax.get_tightbbox(renderer=renderer, for_layout_only=True) - except TypeError: - tightbbox = ax.get_tightbbox(renderer=renderer) - + tightbbox = martist._get_tightbbox_for_layout_only(ax, renderer) if tightbbox is None: bbox = pos else: @@ -458,20 +605,21 @@ def _get_pos_and_bbox(ax, renderer): return pos, bbox -def _reposition_axes(fig, renderer, *, w_pad=0, h_pad=0, hspace=0, wspace=0): +def reposition_axes(layoutgrids, fig, renderer, *, + w_pad=0, h_pad=0, hspace=0, wspace=0): """ Reposition all the axes based on the new inner bounding box. """ trans_fig_to_subfig = fig.transFigure - fig.transSubfigure for sfig in fig.subfigs: - bbox = sfig._layoutgrid.get_outer_bbox() + bbox = layoutgrids[sfig].get_outer_bbox() sfig._redo_transform_rel_fig( bbox=bbox.transformed(trans_fig_to_subfig)) - _reposition_axes(sfig, renderer, - w_pad=w_pad, h_pad=h_pad, - wspace=wspace, hspace=hspace) + reposition_axes(layoutgrids, sfig, renderer, + w_pad=w_pad, h_pad=h_pad, + wspace=wspace, hspace=hspace) - for ax in fig._localaxes.as_list(): + for ax in fig._localaxes: if not hasattr(ax, 'get_subplotspec') or not ax.get_in_layout(): continue @@ -479,14 +627,11 @@ def _reposition_axes(fig, renderer, *, w_pad=0, h_pad=0, hspace=0, wspace=0): # coordinates... ss = ax.get_subplotspec() gs = ss.get_gridspec() - nrows, ncols = gs.get_geometry() - if gs._layoutgrid is None: + if gs not in layoutgrids: return - bbox = gs._layoutgrid.get_inner_bbox(rows=ss.rowspan, cols=ss.colspan) - - bboxouter = gs._layoutgrid.get_outer_bbox(rows=ss.rowspan, - cols=ss.colspan) + bbox = layoutgrids[gs].get_inner_bbox(rows=ss.rowspan, + cols=ss.colspan) # transform from figure to panel for set_position: newbbox = trans_fig_to_subfig.transform_bbox(bbox) @@ -498,11 +643,11 @@ def _reposition_axes(fig, renderer, *, w_pad=0, h_pad=0, hspace=0, wspace=0): offset = {'left': 0, 'right': 0, 'bottom': 0, 'top': 0} for nn, cbax in enumerate(ax._colorbars[::-1]): if ax == cbax._colorbar_info['parents'][0]: - margin = _reposition_colorbar( - cbax, renderer, offset=offset) + reposition_colorbar(layoutgrids, cbax, renderer, + offset=offset) -def _reposition_colorbar(cbax, renderer, *, offset=None): +def reposition_colorbar(layoutgrids, cbax, renderer, *, offset=None): """ Place the colorbar in its new place. @@ -524,13 +669,13 @@ def _reposition_colorbar(cbax, renderer, *, offset=None): parents = cbax._colorbar_info['parents'] gs = parents[0].get_gridspec() - ncols, nrows = gs.ncols, gs.nrows fig = cbax.figure trans_fig_to_subfig = fig.transFigure - fig.transSubfigure - cb_rspans, cb_cspans = _get_cb_parent_spans(cbax) - bboxparent = gs._layoutgrid.get_bbox_for_cb(rows=cb_rspans, cols=cb_cspans) - pb = gs._layoutgrid.get_inner_bbox(rows=cb_rspans, cols=cb_cspans) + cb_rspans, cb_cspans = get_cb_parent_spans(cbax) + bboxparent = layoutgrids[gs].get_bbox_for_cb(rows=cb_rspans, + cols=cb_cspans) + pb = layoutgrids[gs].get_inner_bbox(rows=cb_rspans, cols=cb_cspans) location = cbax._colorbar_info['location'] anchor = cbax._colorbar_info['anchor'] @@ -538,12 +683,12 @@ def _reposition_colorbar(cbax, renderer, *, offset=None): aspect = cbax._colorbar_info['aspect'] shrink = cbax._colorbar_info['shrink'] - cbpos, cbbbox = _get_pos_and_bbox(cbax, renderer) + cbpos, cbbbox = get_pos_and_bbox(cbax, renderer) # Colorbar gets put at extreme edge of outer bbox of the subplotspec # It needs to be moved in by: 1) a pad 2) its "margin" 3) by # any colorbars already added at this location: - cbpad = _colorbar_get_pad(cbax) + cbpad = colorbar_get_pad(layoutgrids, cbax) if location in ('left', 'right'): # fraction and shrink are fractions of parent pbcb = pb.shrunk(fraction, shrink).anchored(anchor, pb) @@ -579,34 +724,38 @@ def _reposition_colorbar(cbax, renderer, *, offset=None): pbcb = trans_fig_to_subfig.transform_bbox(pbcb) cbax.set_transform(fig.transSubfigure) cbax._set_position(pbcb) - cbax.set_aspect(aspect, anchor=anchor, adjustable='box') + cbax.set_anchor(anchor) + if location in ['bottom', 'top']: + aspect = 1 / aspect + cbax.set_box_aspect(aspect) + cbax.set_aspect('auto') return offset -def _reset_margins(fig): +def reset_margins(layoutgrids, fig): """ Reset the margins in the layoutboxes of fig. Margins are usually set as a minimum, so if the figure gets smaller the minimum needs to be zero in order for it to grow again. """ - for span in fig.subfigs: - _reset_margins(span) + for sfig in fig.subfigs: + reset_margins(layoutgrids, sfig) for ax in fig.axes: if hasattr(ax, 'get_subplotspec') and ax.get_in_layout(): ss = ax.get_subplotspec() gs = ss.get_gridspec() - if gs._layoutgrid is not None: - gs._layoutgrid.reset_margins() - fig._layoutgrid.reset_margins() + if gs in layoutgrids: + layoutgrids[gs].reset_margins() + layoutgrids[fig].reset_margins() -def _colorbar_get_pad(cax): +def colorbar_get_pad(layoutgrids, cax): parents = cax._colorbar_info['parents'] gs = parents[0].get_gridspec() - cb_rspans, cb_cspans = _get_cb_parent_spans(cax) - bboxouter = gs._layoutgrid.get_inner_bbox(rows=cb_rspans, cols=cb_cspans) + cb_rspans, cb_cspans = get_cb_parent_spans(cax) + bboxouter = layoutgrids[gs].get_inner_bbox(rows=cb_rspans, cols=cb_cspans) if cax._colorbar_info['location'] in ['right', 'left']: size = bboxouter.width diff --git a/lib/matplotlib/_docstring.py b/lib/matplotlib/_docstring.py new file mode 100644 index 000000000000..ecd209ca0853 --- /dev/null +++ b/lib/matplotlib/_docstring.py @@ -0,0 +1,97 @@ +import inspect + +from . import _api + + +class Substitution: + """ + A decorator that performs %-substitution on an object's docstring. + + This decorator should be robust even if ``obj.__doc__`` is None (for + example, if -OO was passed to the interpreter). + + Usage: construct a docstring.Substitution with a sequence or dictionary + suitable for performing substitution; then decorate a suitable function + with the constructed object, e.g.:: + + sub_author_name = Substitution(author='Jason') + + @sub_author_name + def some_function(x): + "%(author)s wrote this function" + + # note that some_function.__doc__ is now "Jason wrote this function" + + One can also use positional arguments:: + + sub_first_last_names = Substitution('Edgar Allen', 'Poe') + + @sub_first_last_names + def some_function(x): + "%s %s wrote the Raven" + """ + def __init__(self, *args, **kwargs): + if args and kwargs: + raise TypeError("Only positional or keyword args are allowed") + self.params = args or kwargs + + def __call__(self, func): + if func.__doc__: + func.__doc__ = inspect.cleandoc(func.__doc__) % self.params + return func + + def update(self, *args, **kwargs): + """ + Update ``self.params`` (which must be a dict) with the supplied args. + """ + self.params.update(*args, **kwargs) + + +class _ArtistKwdocLoader(dict): + def __missing__(self, key): + if not key.endswith(":kwdoc"): + raise KeyError(key) + name = key[:-len(":kwdoc")] + from matplotlib.artist import Artist, kwdoc + try: + cls, = [cls for cls in _api.recursive_subclasses(Artist) + if cls.__name__ == name] + except ValueError as e: + raise KeyError(key) from e + return self.setdefault(key, kwdoc(cls)) + + +class _ArtistPropertiesSubstitution(Substitution): + """ + A `.Substitution` with two additional features: + + - Substitutions of the form ``%(classname:kwdoc)s`` (ending with the + literal ":kwdoc" suffix) trigger lookup of an Artist subclass with the + given *classname*, and are substituted with the `.kwdoc` of that class. + - Decorating a class triggers substitution both on the class docstring and + on the class' ``__init__`` docstring (which is a commonly required + pattern for Artist subclasses). + """ + + def __init__(self): + self.params = _ArtistKwdocLoader() + + def __call__(self, obj): + super().__call__(obj) + if isinstance(obj, type) and obj.__init__ != object.__init__: + self(obj.__init__) + return obj + + +def copy(source): + """Copy a docstring from another source function (if present).""" + def do_copy(target): + if source.__doc__: + target.__doc__ = source.__doc__ + return target + return do_copy + + +# Create a decorator that will house the various docstring snippets reused +# throughout Matplotlib. +dedent_interpd = interpd = _ArtistPropertiesSubstitution() diff --git a/lib/matplotlib/_enums.py b/lib/matplotlib/_enums.py index 35fe82482869..c8c50f7c3028 100644 --- a/lib/matplotlib/_enums.py +++ b/lib/matplotlib/_enums.py @@ -11,7 +11,7 @@ """ from enum import Enum, auto -from matplotlib import cbook, docstring +from matplotlib import _docstring class _AutoStringNameEnum(Enum): @@ -24,23 +24,6 @@ def __hash__(self): return str(self).__hash__() -def _deprecate_case_insensitive_join_cap(s): - s_low = s.lower() - if s != s_low: - if s_low in ['miter', 'round', 'bevel']: - cbook.warn_deprecated( - "3.3", message="Case-insensitive capstyles are deprecated " - "since %(since)s and support for them will be removed " - "%(removal)s; please pass them in lowercase.") - elif s_low in ['butt', 'round', 'projecting']: - cbook.warn_deprecated( - "3.3", message="Case-insensitive joinstyles are deprecated " - "since %(since)s and support for them will be removed " - "%(removal)s; please pass them in lowercase.") - # Else, error out at the check_in_list stage. - return s_low - - class JoinStyle(str, _AutoStringNameEnum): """ Define how the connection between two line segments is drawn. @@ -100,10 +83,6 @@ class JoinStyle(str, _AutoStringNameEnum): round = auto() bevel = auto() - def __init__(self, s): - s = _deprecate_case_insensitive_join_cap(s) - Enum.__init__(self) - @staticmethod def demo(): """Demonstrate how each JoinStyle looks for various join angles.""" @@ -149,6 +128,9 @@ class CapStyle(str, _AutoStringNameEnum): For a visual impression of each *CapStyle*, `view these docs online ` or run `CapStyle.demo`. + By default, `~.backend_bases.GraphicsContextBase` draws a stroked line as + squared off at its endpoints. + **Supported values:** .. rst-class:: value-list @@ -169,13 +151,9 @@ class CapStyle(str, _AutoStringNameEnum): CapStyle.demo() """ - butt = 'butt' - projecting = 'projecting' - round = 'round' - - def __init__(self, s): - s = _deprecate_case_insensitive_join_cap(s) - Enum.__init__(self) + butt = auto() + projecting = auto() + round = auto() @staticmethod def demo(): @@ -193,7 +171,6 @@ def demo(): ax.plot(xx, yy, lw=12, color='tab:blue', solid_capstyle=style) ax.plot(xx, yy, lw=1, color='black') ax.plot(xx, yy, 'o', color='tab:red', markersize=3) - ax.text(2.25, 0.55, '(default)', ha='center') ax.set_ylim(-.5, 1.5) ax.set_axis_off() @@ -204,5 +181,5 @@ def demo(): + ", ".join([f"'{cs.name}'" for cs in CapStyle]) \ + "}" -docstring.interpd.update({'JoinStyle': JoinStyle.input_description, +_docstring.interpd.update({'JoinStyle': JoinStyle.input_description, 'CapStyle': CapStyle.input_description}) diff --git a/lib/matplotlib/_fontconfig_pattern.py b/lib/matplotlib/_fontconfig_pattern.py new file mode 100644 index 000000000000..c47e19bf99dc --- /dev/null +++ b/lib/matplotlib/_fontconfig_pattern.py @@ -0,0 +1,209 @@ +""" +A module for parsing and generating `fontconfig patterns`_. + +.. _fontconfig patterns: + https://www.freedesktop.org/software/fontconfig/fontconfig-user.html +""" + +# This class logically belongs in `matplotlib.font_manager`, but placing it +# there would have created cyclical dependency problems, because it also needs +# to be available from `matplotlib.rcsetup` (for parsing matplotlibrc files). + +from functools import lru_cache +import re +import numpy as np +from pyparsing import (Literal, ZeroOrMore, Optional, Regex, StringEnd, + ParseException, Suppress) + +family_punc = r'\\\-:,' +family_unescape = re.compile(r'\\([%s])' % family_punc).sub +family_escape = re.compile(r'([%s])' % family_punc).sub + +value_punc = r'\\=_:,' +value_unescape = re.compile(r'\\([%s])' % value_punc).sub +value_escape = re.compile(r'([%s])' % value_punc).sub + + +class FontconfigPatternParser: + """ + A simple pyparsing-based parser for `fontconfig patterns`_. + + .. _fontconfig patterns: + https://www.freedesktop.org/software/fontconfig/fontconfig-user.html + """ + + _constants = { + 'thin': ('weight', 'light'), + 'extralight': ('weight', 'light'), + 'ultralight': ('weight', 'light'), + 'light': ('weight', 'light'), + 'book': ('weight', 'book'), + 'regular': ('weight', 'regular'), + 'normal': ('weight', 'normal'), + 'medium': ('weight', 'medium'), + 'demibold': ('weight', 'demibold'), + 'semibold': ('weight', 'semibold'), + 'bold': ('weight', 'bold'), + 'extrabold': ('weight', 'extra bold'), + 'black': ('weight', 'black'), + 'heavy': ('weight', 'heavy'), + 'roman': ('slant', 'normal'), + 'italic': ('slant', 'italic'), + 'oblique': ('slant', 'oblique'), + 'ultracondensed': ('width', 'ultra-condensed'), + 'extracondensed': ('width', 'extra-condensed'), + 'condensed': ('width', 'condensed'), + 'semicondensed': ('width', 'semi-condensed'), + 'expanded': ('width', 'expanded'), + 'extraexpanded': ('width', 'extra-expanded'), + 'ultraexpanded': ('width', 'ultra-expanded') + } + + def __init__(self): + + family = Regex( + r'([^%s]|(\\[%s]))*' % (family_punc, family_punc) + ).setParseAction(self._family) + + size = Regex( + r"([0-9]+\.?[0-9]*|\.[0-9]+)" + ).setParseAction(self._size) + + name = Regex( + r'[a-z]+' + ).setParseAction(self._name) + + value = Regex( + r'([^%s]|(\\[%s]))*' % (value_punc, value_punc) + ).setParseAction(self._value) + + families = ( + family + + ZeroOrMore( + Literal(',') + + family) + ).setParseAction(self._families) + + point_sizes = ( + size + + ZeroOrMore( + Literal(',') + + size) + ).setParseAction(self._point_sizes) + + property = ( + (name + + Suppress(Literal('=')) + + value + + ZeroOrMore( + Suppress(Literal(',')) + + value)) + | name + ).setParseAction(self._property) + + pattern = ( + Optional( + families) + + Optional( + Literal('-') + + point_sizes) + + ZeroOrMore( + Literal(':') + + property) + + StringEnd() + ) + + self._parser = pattern + self.ParseException = ParseException + + def parse(self, pattern): + """ + Parse the given fontconfig *pattern* and return a dictionary + of key/value pairs useful for initializing a + `.font_manager.FontProperties` object. + """ + props = self._properties = {} + try: + self._parser.parseString(pattern) + except self.ParseException as e: + raise ValueError( + "Could not parse font string: '%s'\n%s" % (pattern, e)) from e + + self._properties = None + + self._parser.resetCache() + + return props + + def _family(self, s, loc, tokens): + return [family_unescape(r'\1', str(tokens[0]))] + + def _size(self, s, loc, tokens): + return [float(tokens[0])] + + def _name(self, s, loc, tokens): + return [str(tokens[0])] + + def _value(self, s, loc, tokens): + return [value_unescape(r'\1', str(tokens[0]))] + + def _families(self, s, loc, tokens): + self._properties['family'] = [str(x) for x in tokens] + return [] + + def _point_sizes(self, s, loc, tokens): + self._properties['size'] = [str(x) for x in tokens] + return [] + + def _property(self, s, loc, tokens): + if len(tokens) == 1: + if tokens[0] in self._constants: + key, val = self._constants[tokens[0]] + self._properties.setdefault(key, []).append(val) + else: + key = tokens[0] + val = tokens[1:] + self._properties.setdefault(key, []).extend(val) + return [] + + +# `parse_fontconfig_pattern` is a bottleneck during the tests because it is +# repeatedly called when the rcParams are reset (to validate the default +# fonts). In practice, the cache size doesn't grow beyond a few dozen entries +# during the test suite. +parse_fontconfig_pattern = lru_cache()(FontconfigPatternParser().parse) + + +def _escape_val(val, escape_func): + """ + Given a string value or a list of string values, run each value through + the input escape function to make the values into legal font config + strings. The result is returned as a string. + """ + if not np.iterable(val) or isinstance(val, str): + val = [val] + + return ','.join(escape_func(r'\\\1', str(x)) for x in val + if x is not None) + + +def generate_fontconfig_pattern(d): + """ + Given a dictionary of key/value pairs, generates a fontconfig + pattern string. + """ + props = [] + + # Family is added first w/o a keyword + family = d.get_family() + if family is not None and family != []: + props.append(_escape_val(family, family_escape)) + + # The other keys are added as key=value + for key in ['style', 'variant', 'weight', 'stretch', 'file', 'size']: + val = getattr(d, 'get_' + key)() + # Don't use 'if not val' because 0 is a valid input. + if val is not None and val != []: + props.append(":%s=%s" % (key, _escape_val(val, value_escape))) + + return ''.join(props) diff --git a/lib/matplotlib/_layoutgrid.py b/lib/matplotlib/_layoutgrid.py index e46b3fe8c062..12eec6f2b2d6 100644 --- a/lib/matplotlib/_layoutgrid.py +++ b/lib/matplotlib/_layoutgrid.py @@ -20,8 +20,10 @@ import kiwisolver as kiwi import logging import numpy as np -from matplotlib.transforms import Bbox +import matplotlib as mpl +import matplotlib.patches as mpatches +from matplotlib.transforms import Bbox _log = logging.getLogger(__name__) @@ -39,7 +41,9 @@ def __init__(self, parent=None, parent_pos=(0, 0), self.parent = parent self.parent_pos = parent_pos self.parent_inner = parent_inner - self.name = name + self.name = name + seq_id() + if isinstance(parent, LayoutGrid): + self.name = f'{parent.name}.{self.name}' self.nrows = nrows self.ncols = ncols self.height_ratios = np.atleast_1d(height_ratios) @@ -50,8 +54,10 @@ def __init__(self, parent=None, parent_pos=(0, 0), self.width_ratios = np.ones(ncols) sn = self.name + '_' - if parent is None: - self.parent = None + if not isinstance(parent, LayoutGrid): + # parent can be a rect if not a LayoutGrid + # allows specifying a rectangle to contain the layout. + self.parent = parent self.solver = kiwi.Solver() else: self.parent = parent @@ -168,7 +174,8 @@ def hard_constraints(self): self.solver.addConstraint(c | 'required') def add_child(self, child, i=0, j=0): - self.children[i, j] = child + # np.ix_ returns the cross product of i and j indices + self.children[np.ix_(np.atleast_1d(i), np.atleast_1d(j))] = child def parent_constraints(self): # constraints that are due to the parent... @@ -176,12 +183,13 @@ def parent_constraints(self): # parent's left, the last column right equal to the # parent's right... parent = self.parent - if parent is None: - hc = [self.lefts[0] == 0, - self.rights[-1] == 1, + if not isinstance(parent, LayoutGrid): + # specify a rectangle in figure coordinates + hc = [self.lefts[0] == parent[0], + self.rights[-1] == parent[0] + parent[2], # top and bottom reversed order... - self.tops[0] == 1, - self.bottoms[-1] == 0] + self.tops[0] == parent[1] + parent[3], + self.bottoms[-1] == parent[1]] else: rows, cols = self.parent_pos rows = np.atleast_1d(rows) @@ -502,20 +510,12 @@ def seq_id(): return '%06d' % next(_layoutboxobjnum) -def print_children(lb): - """Print the children of the layoutbox.""" - for child in lb.children: - print_children(child) - - -def plot_children(fig, lg, level=0, printit=False): +def plot_children(fig, lg=None, level=0): """Simple plotting to show where boxes are.""" - import matplotlib.pyplot as plt - import matplotlib.patches as mpatches - - fig.canvas.draw() - - colors = plt.rcParams["axes.prop_cycle"].by_key()["color"] + if lg is None: + _layoutgrids = fig.get_layout_engine().execute(fig) + lg = _layoutgrids[fig] + colors = mpl.rcParams["axes.prop_cycle"].by_key()["color"] col = colors[level] for i in range(lg.nrows): for j in range(lg.ncols): diff --git a/lib/matplotlib/_mathtext.py b/lib/matplotlib/_mathtext.py index 6f0e47b239e6..44bb5a9e8fd8 100644 --- a/lib/matplotlib/_mathtext.py +++ b/lib/matplotlib/_mathtext.py @@ -2,28 +2,28 @@ Implementation details for :mod:`.mathtext`. """ +import copy from collections import namedtuple import enum import functools -from io import StringIO import logging import os +import re import types import unicodedata import numpy as np from pyparsing import ( - Combine, Empty, FollowedBy, Forward, Group, Literal, oneOf, OneOrMore, - Optional, ParseBaseException, ParseFatalException, ParserElement, - ParseResults, QuotedString, Regex, StringEnd, Suppress, ZeroOrMore) + Empty, Forward, Literal, NotAny, oneOf, OneOrMore, Optional, + ParseBaseException, ParseExpression, ParseFatalException, ParserElement, + ParseResults, QuotedString, Regex, StringEnd, ZeroOrMore, pyparsing_common) import matplotlib as mpl from . import _api, cbook from ._mathtext_data import ( - latex_to_bakoma, latex_to_standard, stix_virtual_fonts, tex2uni) -from .afm import AFM + latex_to_bakoma, stix_glyph_fixes, stix_virtual_fonts, tex2uni) from .font_manager import FontProperties, findfont, get_font -from .ft2font import KERNING_DEFAULT +from .ft2font import FT2Image, KERNING_DEFAULT ParserElement.enablePackrat() @@ -34,29 +34,28 @@ # FONTS -def get_unicode_index(symbol, math=True): +@_api.delete_parameter("3.6", "math") +def get_unicode_index(symbol, math=False): # Publicly exported. r""" Return the integer index (from the Unicode table) of *symbol*. Parameters ---------- symbol : str - A single unicode character, a TeX command (e.g. r'\pi') or a Type1 + A single (Unicode) character, a TeX command (e.g. r'\pi') or a Type1 symbol name (e.g. 'phi'). - math : bool, default: True - If False, always treat as a single unicode character. + math : bool, default: False + If True (deprecated), replace ASCII hyphen-minus by Unicode minus. """ - # for a non-math symbol, simply return its unicode index - if not math: - return ord(symbol) # From UTF #25: U+2212 minus sign is the preferred # representation of the unary and binary minus sign rather than # the ASCII-derived U+002D hyphen-minus, because minus sign is # unambiguous and because it is rendered with a more desirable # length, usually longer than a hyphen. - if symbol == '-': + # Remove this block when the 'math' parameter is deleted. + if math and symbol == '-': return 0x2212 - try: # This will succeed if symbol is a single unicode char + try: # This will succeed if symbol is a single Unicode char return ord(symbol) except TypeError: pass @@ -68,6 +67,85 @@ def get_unicode_index(symbol, math=True): .format(symbol)) from err +VectorParse = namedtuple("VectorParse", "width height depth glyphs rects", + module="matplotlib.mathtext") +VectorParse.__doc__ = r""" +The namedtuple type returned by ``MathTextParser("path").parse(...)``. + +This tuple contains the global metrics (*width*, *height*, *depth*), a list of +*glyphs* (including their positions) and of *rect*\angles. +""" + + +RasterParse = namedtuple("RasterParse", "ox oy width height depth image", + module="matplotlib.mathtext") +RasterParse.__doc__ = r""" +The namedtuple type returned by ``MathTextParser("agg").parse(...)``. + +This tuple contains the global metrics (*width*, *height*, *depth*), and a +raster *image*. The offsets *ox*, *oy* are always zero. +""" + + +class Output: + r""" + Result of `ship`\ping a box: lists of positioned glyphs and rectangles. + + This class is not exposed to end users, but converted to a `VectorParse` or + a `RasterParse` by `.MathTextParser.parse`. + """ + + def __init__(self, box): + self.box = box + self.glyphs = [] # (ox, oy, info) + self.rects = [] # (x1, y1, x2, y2) + + def to_vector(self): + w, h, d = map( + np.ceil, [self.box.width, self.box.height, self.box.depth]) + gs = [(info.font, info.fontsize, info.num, ox, h - oy + info.offset) + for ox, oy, info in self.glyphs] + rs = [(x1, h - y2, x2 - x1, y2 - y1) + for x1, y1, x2, y2 in self.rects] + return VectorParse(w, h + d, d, gs, rs) + + def to_raster(self): + # Metrics y's and mathtext y's are oriented in opposite directions, + # hence the switch between ymin and ymax. + xmin = min([*[ox + info.metrics.xmin for ox, oy, info in self.glyphs], + *[x1 for x1, y1, x2, y2 in self.rects], 0]) - 1 + ymin = min([*[oy - info.metrics.ymax for ox, oy, info in self.glyphs], + *[y1 for x1, y1, x2, y2 in self.rects], 0]) - 1 + xmax = max([*[ox + info.metrics.xmax for ox, oy, info in self.glyphs], + *[x2 for x1, y1, x2, y2 in self.rects], 0]) + 1 + ymax = max([*[oy - info.metrics.ymin for ox, oy, info in self.glyphs], + *[y2 for x1, y1, x2, y2 in self.rects], 0]) + 1 + w = xmax - xmin + h = ymax - ymin - self.box.depth + d = ymax - ymin - self.box.height + image = FT2Image(np.ceil(w), np.ceil(h + max(d, 0))) + + # Ideally, we could just use self.glyphs and self.rects here, shifting + # their coordinates by (-xmin, -ymin), but this yields slightly + # different results due to floating point slop; shipping twice is the + # old approach and keeps baseline images backcompat. + shifted = ship(self.box, (-xmin, -ymin)) + + for ox, oy, info in shifted.glyphs: + info.font.draw_glyph_to_bitmap( + image, ox, oy - info.metrics.iceberg, info.glyph, + antialiased=mpl.rcParams['text.antialiased']) + for x1, y1, x2, y2 in shifted.rects: + height = max(int(y2 - y1) - 1, 0) + if height == 0: + center = (y2 + y1) / 2 + y = int(center - (height + 1) / 2) + else: + y = int(y1) + image.draw_rect_filled(int(x1), y, np.ceil(x2), y + height) + return RasterParse(0, 0, w, h + d, d, image) + + class Fonts: """ An abstract base class for a system of fonts to use for mathtext. @@ -77,27 +155,19 @@ class Fonts: to do the actual drawing. """ - def __init__(self, default_font_prop, mathtext_backend): + def __init__(self, default_font_prop, load_glyph_flags): """ Parameters ---------- default_font_prop : `~.font_manager.FontProperties` The default non-math font, or the base font for Unicode (generic) font rendering. - mathtext_backend : `MathtextBackend` subclass - Backend to which rendering is actually delegated. + load_glyph_flags : int + Flags passed to the glyph loader (e.g. ``FT_Load_Glyph`` and + ``FT_Load_Char`` for FreeType-based fonts). """ self.default_font_prop = default_font_prop - self.mathtext_backend = mathtext_backend - self.used_characters = {} - - @_api.deprecated("3.4") - def destroy(self): - """ - Fix any cyclical references before the object is about - to be destroyed. - """ - self.used_characters = None + self.load_glyph_flags = load_glyph_flags def get_kern(self, font1, fontclass1, sym1, fontsize1, font2, fontclass2, sym2, fontsize2, dpi): @@ -108,7 +178,7 @@ def get_kern(self, font1, fontclass1, sym1, fontsize1, """ return 0. - def get_metrics(self, font, font_class, sym, fontsize, dpi, math=True): + def get_metrics(self, font, font_class, sym, fontsize, dpi): r""" Parameters ---------- @@ -127,8 +197,6 @@ def get_metrics(self, font, font_class, sym, fontsize, dpi, math=True): Font size in points. dpi : float Rendering dots-per-inch. - math : bool - Whether we are currently in math mode or not. Returns ------- @@ -146,33 +214,23 @@ def get_metrics(self, font, font_class, sym, fontsize, dpi, math=True): - *slanted*: Whether the glyph should be considered as "slanted" (currently used for kerning sub/superscripts). """ - info = self._get_info(font, font_class, sym, fontsize, dpi, math) + info = self._get_info(font, font_class, sym, fontsize, dpi) return info.metrics - def set_canvas_size(self, w, h, d): - """ - Set the size of the buffer used to render the math expression. - Only really necessary for the bitmap backends. - """ - self.width, self.height, self.depth = np.ceil([w, h, d]) - self.mathtext_backend.set_canvas_size( - self.width, self.height, self.depth) - - @_api.rename_parameter("3.4", "facename", "font") - def render_glyph(self, ox, oy, font, font_class, sym, fontsize, dpi): + def render_glyph( + self, output, ox, oy, font, font_class, sym, fontsize, dpi): """ At position (*ox*, *oy*), draw the glyph specified by the remaining parameters (see `get_metrics` for their detailed description). """ info = self._get_info(font, font_class, sym, fontsize, dpi) - self.used_characters.setdefault(info.font.fname, set()).add(info.num) - self.mathtext_backend.render_glyph(ox, oy, info) + output.glyphs.append((ox, oy, info)) - def render_rect_filled(self, x1, y1, x2, y2): + def render_rect_filled(self, output, x1, y1, x2, y2): """ Draw a filled rectangle from (*x1*, *y1*) to (*x2*, *y2*). """ - self.mathtext_backend.render_rect_filled(x1, y1, x2, y2) + output.rects.append((x1, y1, x2, y2)) def get_xheight(self, font, fontsize, dpi): """ @@ -195,20 +253,6 @@ def get_used_characters(self): """ return self.used_characters - def get_results(self, box): - """ - Get the data needed by the backend to render the math - expression. The return value is backend-specific. - """ - result = self.mathtext_backend.get_results( - box, self.get_used_characters()) - if self.destroy != TruetypeFonts.destroy.__get__(self): - destroy = _api.deprecate_method_override( - __class__.destroy, self, since="3.4") - if destroy: - destroy() - return result - def get_sized_alternatives_for_symbol(self, fontname, sym): """ Override if your font provides multiple sizes of the same @@ -224,21 +268,18 @@ class TruetypeFonts(Fonts): A generic base class for all font setups that use Truetype fonts (through FT2Font). """ - def __init__(self, default_font_prop, mathtext_backend): - super().__init__(default_font_prop, mathtext_backend) - self.glyphd = {} + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # Per-instance cache. + self._get_info = functools.lru_cache(None)(self._get_info) self._fonts = {} - filename = findfont(default_font_prop) + filename = findfont(self.default_font_prop) default_font = get_font(filename) self._fonts['default'] = default_font self._fonts['regular'] = default_font - @_api.deprecated("3.4") - def destroy(self): - self.glyphd = None - super().destroy() - def _get_font(self, font): if font in self.fontmap: basename = self.fontmap[font] @@ -257,19 +298,11 @@ def _get_offset(self, font, glyph, fontsize, dpi): return (glyph.height / 64 / 2) + (fontsize/3 * dpi/72) return 0. - def _get_info(self, fontname, font_class, sym, fontsize, dpi, math=True): - key = fontname, font_class, sym, fontsize, dpi - bunch = self.glyphd.get(key) - if bunch is not None: - return bunch - - font, num, symbol_name, fontsize, slanted = \ - self._get_glyph(fontname, font_class, sym, fontsize, math) - + # The return value of _get_info is cached per-instance. + def _get_info(self, fontname, font_class, sym, fontsize, dpi): + font, num, slanted = self._get_glyph(fontname, font_class, sym) font.set_size(fontsize, dpi) - glyph = font.load_char( - num, - flags=self.mathtext_backend.get_hinting_type()) + glyph = font.load_char(num, flags=self.load_glyph_flags) xmin, ymin, xmax, ymax = [val/64.0 for val in glyph.bbox] offset = self._get_offset(font, glyph, fontsize, dpi) @@ -286,17 +319,15 @@ def _get_info(self, fontname, font_class, sym, fontsize, dpi, math=True): slanted = slanted ) - result = self.glyphd[key] = types.SimpleNamespace( + return types.SimpleNamespace( font = font, fontsize = fontsize, postscript_name = font.postscript_name, metrics = metrics, - symbol_name = symbol_name, num = num, glyph = glyph, offset = offset ) - return result def get_xheight(self, fontname, fontsize, dpi): font = self._get_font(fontname) @@ -356,8 +387,7 @@ def __init__(self, *args, **kwargs): _slanted_symbols = set(r"\int \oint".split()) - def _get_glyph(self, fontname, font_class, sym, fontsize, math=True): - symbol_name = None + def _get_glyph(self, fontname, font_class, sym): font = None if fontname in self.fontmap and sym in latex_to_bakoma: basename, num = latex_to_bakoma[sym] @@ -368,17 +398,10 @@ def _get_glyph(self, fontname, font_class, sym, fontsize, math=True): font = self._get_font(fontname) if font is not None: num = ord(sym) - - if font is not None: - gid = font.get_char_index(num) - if gid != 0: - symbol_name = font.get_glyph_name(gid) - - if symbol_name is None: - return self._stix_fallback._get_glyph( - fontname, font_class, sym, fontsize, math) - - return font, num, symbol_name, fontsize, slanted + if font is not None and font.get_char_index(num) != 0: + return font, num, slanted + else: + return self._stix_fallback._get_glyph(fontname, font_class, sym) # The Bakoma fonts contain many pre-sized alternatives for the # delimiters. The AutoSizedChar class will use these alternatives @@ -451,19 +474,15 @@ class UnicodeFonts(TruetypeFonts): This class will "fallback" on the Bakoma fonts when a required symbol can not be found in the font. """ - use_cmex = True # Unused; delete once mathtext becomes private. def __init__(self, *args, **kwargs): # This must come first so the backend's owner is set correctly fallback_rc = mpl.rcParams['mathtext.fallback'] - if mpl.rcParams['mathtext.fallback_to_cm'] is not None: - fallback_rc = ('cm' if mpl.rcParams['mathtext.fallback_to_cm'] - else None) font_cls = {'stix': StixFonts, 'stixsans': StixSansFonts, 'cm': BakomaFonts }.get(fallback_rc) - self.cm_fallback = font_cls(*args, **kwargs) if font_cls else None + self._fallback_font = font_cls(*args, **kwargs) if font_cls else None super().__init__(*args, **kwargs) self.fontmap = {} @@ -476,7 +495,7 @@ def __init__(self, *args, **kwargs): self.fontmap['ex'] = font # include STIX sized alternatives for glyphs if fallback is STIX - if isinstance(self.cm_fallback, StixFonts): + if isinstance(self._fallback_font, StixFonts): stixsizedaltfonts = { 0: 'STIXGeneral', 1: 'STIXSizeOneSym', @@ -495,14 +514,14 @@ def __init__(self, *args, **kwargs): def _map_virtual_font(self, fontname, font_class, uniindex): return fontname, uniindex - def _get_glyph(self, fontname, font_class, sym, fontsize, math=True): + def _get_glyph(self, fontname, font_class, sym): try: - uniindex = get_unicode_index(sym, math) + uniindex = get_unicode_index(sym) found_symbol = True except ValueError: uniindex = ord('?') found_symbol = False - _log.warning("No TeX to unicode mapping for {!a}.".format(sym)) + _log.warning("No TeX to Unicode mapping for {!a}.".format(sym)) fontname, uniindex = self._map_virtual_font( fontname, font_class, uniindex) @@ -522,56 +541,56 @@ def _get_glyph(self, fontname, font_class, sym, fontsize, math=True): found_symbol = False font = self._get_font(new_fontname) if font is not None: + if font.family_name == "cmr10" and uniindex == 0x2212: + # minus sign exists in cmsy10 (not cmr10) + font = get_font( + cbook._get_data_path("fonts/ttf/cmsy10.ttf")) + uniindex = 0xa1 glyphindex = font.get_char_index(uniindex) if glyphindex != 0: found_symbol = True if not found_symbol: - if self.cm_fallback: + if self._fallback_font: if (fontname in ('it', 'regular') - and isinstance(self.cm_fallback, StixFonts)): + and isinstance(self._fallback_font, StixFonts)): fontname = 'rm' - g = self.cm_fallback._get_glyph(fontname, font_class, - sym, fontsize) - fname = g[0].family_name - if fname in list(BakomaFonts._fontmap.values()): - fname = "Computer Modern" - _log.info("Substituting symbol %s from %s", sym, fname) + g = self._fallback_font._get_glyph(fontname, font_class, sym) + family = g[0].family_name + if family in list(BakomaFonts._fontmap.values()): + family = "Computer Modern" + _log.info("Substituting symbol %s from %s", sym, family) return g else: if (fontname in ('it', 'regular') and isinstance(self, StixFonts)): - return self._get_glyph('rm', font_class, sym, fontsize) + return self._get_glyph('rm', font_class, sym) _log.warning("Font {!r} does not have a glyph for {!a} " "[U+{:x}], substituting with a dummy " "symbol.".format(new_fontname, sym, uniindex)) - fontname = 'rm' - font = self._get_font(fontname) + font = self._get_font('rm') uniindex = 0xA4 # currency char, for lack of anything better - glyphindex = font.get_char_index(uniindex) slanted = False - symbol_name = font.get_glyph_name(glyphindex) - return font, uniindex, symbol_name, fontsize, slanted + return font, uniindex, slanted def get_sized_alternatives_for_symbol(self, fontname, sym): - if self.cm_fallback: - return self.cm_fallback.get_sized_alternatives_for_symbol( + if self._fallback_font: + return self._fallback_font.get_sized_alternatives_for_symbol( fontname, sym) return [(fontname, sym)] class DejaVuFonts(UnicodeFonts): - use_cmex = False # Unused; delete once mathtext becomes private. def __init__(self, *args, **kwargs): # This must come first so the backend's owner is set correctly if isinstance(self, DejaVuSerifFonts): - self.cm_fallback = StixFonts(*args, **kwargs) + self._fallback_font = StixFonts(*args, **kwargs) else: - self.cm_fallback = StixSansFonts(*args, **kwargs) + self._fallback_font = StixSansFonts(*args, **kwargs) self.bakoma = BakomaFonts(*args, **kwargs) TruetypeFonts.__init__(self, *args, **kwargs) self.fontmap = {} @@ -588,11 +607,10 @@ def __init__(self, *args, **kwargs): self.fontmap[key] = fullpath self.fontmap[name] = fullpath - def _get_glyph(self, fontname, font_class, sym, fontsize, math=True): + def _get_glyph(self, fontname, font_class, sym): # Override prime symbol to use Bakoma. if sym == r'\prime': - return self.bakoma._get_glyph( - fontname, font_class, sym, fontsize, math) + return self.bakoma._get_glyph(fontname, font_class, sym) else: # check whether the glyph is available in the display font uniindex = get_unicode_index(sym) @@ -600,11 +618,9 @@ def _get_glyph(self, fontname, font_class, sym, fontsize, math=True): if font is not None: glyphindex = font.get_char_index(uniindex) if glyphindex != 0: - return super()._get_glyph( - 'ex', font_class, sym, fontsize, math) + return super()._get_glyph('ex', font_class, sym) # otherwise return regular glyph - return super()._get_glyph( - fontname, font_class, sym, fontsize, math) + return super()._get_glyph(fontname, font_class, sym) class DejaVuSerifFonts(DejaVuFonts): @@ -667,8 +683,7 @@ class StixFonts(UnicodeFonts): 4: 'STIXSizeFourSym', 5: 'STIXSizeFiveSym', } - use_cmex = False # Unused; delete once mathtext becomes private. - cm_fallback = False + _fallback_font = False _sans = False def __init__(self, *args, **kwargs): @@ -718,6 +733,10 @@ def _map_virtual_font(self, fontname, font_class, uniindex): uniindex = 0x1 fontname = mpl.rcParams['mathtext.default'] + # Fix some incorrect glyphs. + if fontname in ('rm', 'it'): + uniindex = stix_glyph_fixes.get(uniindex, uniindex) + # Handle private use area glyphs if fontname in ('it', 'rm', 'bf') and 0xe000 <= uniindex <= 0xf8ff: fontname = 'nonuni' + fontname @@ -753,164 +772,6 @@ class StixSansFonts(StixFonts): _sans = True -class StandardPsFonts(Fonts): - """ - Use the standard postscript fonts for rendering to backend_ps - - Unlike the other font classes, BakomaFont and UnicodeFont, this - one requires the Ps backend. - """ - basepath = str(cbook._get_data_path('fonts/afm')) - - fontmap = { - 'cal': 'pzcmi8a', # Zapf Chancery - 'rm': 'pncr8a', # New Century Schoolbook - 'tt': 'pcrr8a', # Courier - 'it': 'pncri8a', # New Century Schoolbook Italic - 'sf': 'phvr8a', # Helvetica - 'bf': 'pncb8a', # New Century Schoolbook Bold - None: 'psyr', # Symbol - } - - def __init__(self, default_font_prop, mathtext_backend=None): - if mathtext_backend is None: - # Circular import, can be dropped after public access to - # StandardPsFonts is removed and mathtext_backend made a required - # parameter. - from . import mathtext - mathtext_backend = mathtext.MathtextBackendPath() - super().__init__(default_font_prop, mathtext_backend) - self.glyphd = {} - self.fonts = {} - - filename = findfont(default_font_prop, fontext='afm', - directory=self.basepath) - if filename is None: - filename = findfont('Helvetica', fontext='afm', - directory=self.basepath) - with open(filename, 'rb') as fd: - default_font = AFM(fd) - default_font.fname = filename - - self.fonts['default'] = default_font - self.fonts['regular'] = default_font - - pswriter = _api.deprecated("3.4")(property(lambda self: StringIO())) - - def _get_font(self, font): - if font in self.fontmap: - basename = self.fontmap[font] - else: - basename = font - - cached_font = self.fonts.get(basename) - if cached_font is None: - fname = os.path.join(self.basepath, basename + ".afm") - with open(fname, 'rb') as fd: - cached_font = AFM(fd) - cached_font.fname = fname - self.fonts[basename] = cached_font - self.fonts[cached_font.get_fontname()] = cached_font - return cached_font - - def _get_info(self, fontname, font_class, sym, fontsize, dpi, math=True): - """Load the cmfont, metrics and glyph with caching.""" - key = fontname, sym, fontsize, dpi - tup = self.glyphd.get(key) - - if tup is not None: - return tup - - # Only characters in the "Letter" class should really be italicized. - # This class includes greek letters, so we're ok - if (fontname == 'it' and - (len(sym) > 1 - or not unicodedata.category(sym).startswith("L"))): - fontname = 'rm' - - found_symbol = False - - if sym in latex_to_standard: - fontname, num = latex_to_standard[sym] - glyph = chr(num) - found_symbol = True - elif len(sym) == 1: - glyph = sym - num = ord(glyph) - found_symbol = True - else: - _log.warning( - "No TeX to built-in Postscript mapping for {!r}".format(sym)) - - slanted = (fontname == 'it') - font = self._get_font(fontname) - - if found_symbol: - try: - symbol_name = font.get_name_char(glyph) - except KeyError: - _log.warning( - "No glyph in standard Postscript font {!r} for {!r}" - .format(font.get_fontname(), sym)) - found_symbol = False - - if not found_symbol: - glyph = '?' - num = ord(glyph) - symbol_name = font.get_name_char(glyph) - - offset = 0 - - scale = 0.001 * fontsize - - xmin, ymin, xmax, ymax = [val * scale - for val in font.get_bbox_char(glyph)] - metrics = types.SimpleNamespace( - advance = font.get_width_char(glyph) * scale, - width = font.get_width_char(glyph) * scale, - height = font.get_height_char(glyph) * scale, - xmin = xmin, - xmax = xmax, - ymin = ymin+offset, - ymax = ymax+offset, - # iceberg is the equivalent of TeX's "height" - iceberg = ymax + offset, - slanted = slanted - ) - - self.glyphd[key] = types.SimpleNamespace( - font = font, - fontsize = fontsize, - postscript_name = font.get_fontname(), - metrics = metrics, - symbol_name = symbol_name, - num = num, - glyph = glyph, - offset = offset - ) - - return self.glyphd[key] - - def get_kern(self, font1, fontclass1, sym1, fontsize1, - font2, fontclass2, sym2, fontsize2, dpi): - if font1 == font2 and fontsize1 == fontsize2: - info1 = self._get_info(font1, fontclass1, sym1, fontsize1, dpi) - info2 = self._get_info(font2, fontclass2, sym2, fontsize2, dpi) - font = info1.font - return (font.get_kern_dist(info1.glyph, info2.glyph) - * 0.001 * fontsize1) - return super().get_kern(font1, fontclass1, sym1, fontsize1, - font2, fontclass2, sym2, fontsize2, dpi) - - def get_xheight(self, font, fontsize, dpi): - font = self._get_font(font) - return font.get_xheight() * 0.001 * fontsize - - def get_underline_thickness(self, font, fontsize, dpi): - font = self._get_font(font) - return font.get_underline_thickness() * 0.001 * fontsize - - ############################################################################## # TeX-LIKE BOX MODEL @@ -923,8 +784,8 @@ def get_underline_thickness(self, font, fontsize, dpi): # # The most relevant "chapters" are: # Data structures for boxes and their friends -# Shipping pages out (Ship class) -# Packaging (hpack and vpack) +# Shipping pages out (ship()) +# Packaging (hpack() and vpack()) # Data structures for math mode # Subroutines for math mode # Typesetting math formulas @@ -935,10 +796,8 @@ def get_underline_thickness(self, font, fontsize, dpi): # Note that (as TeX) y increases downward, unlike many other parts of # matplotlib. -# How much text shrinks when going to the next-smallest level. GROW_FACTOR -# must be the inverse of SHRINK_FACTOR. +# How much text shrinks when going to the next-smallest level. SHRINK_FACTOR = 0.7 -GROW_FACTOR = 1 / SHRINK_FACTOR # The number of different sizes of chars to use, beyond which they will not # get any smaller NUM_SIZE_LEVELS = 6 @@ -966,16 +825,16 @@ class FontConstantsBase: # superscript is present sub2 = 0.5 - # Percentage of x-height that sub/supercripts are offset relative to the + # Percentage of x-height that sub/superscripts are offset relative to the # nucleus edge for non-slanted nuclei delta = 0.025 # Additional percentage of last character height above 2/3 of the - # x-height that supercripts are offset relative to the subscript + # x-height that superscripts are offset relative to the subscript # for slanted nuclei delta_slanted = 0.2 - # Percentage of x-height that supercripts and subscripts are offset for + # Percentage of x-height that superscripts and subscripts are offset for # integrals delta_integral = 0.1 @@ -1042,12 +901,11 @@ class DejaVuSansFontConstants(FontConstantsBase): def _get_font_constant_set(state): constants = _font_constant_mapping.get( - state.font_output._get_font(state.font).family_name, - FontConstantsBase) + state.fontset._get_font(state.font).family_name, FontConstantsBase) # STIX sans isn't really its own fonts, just different code points # in the STIX fonts, so we have to detect this one separately. if (constants is STIXFontConstants and - isinstance(state.font_output, StixSansFonts)): + isinstance(state.fontset, StixSansFonts)): return STIXSansFontConstants return constants @@ -1059,7 +917,7 @@ def __init__(self): self.size = 0 def __repr__(self): - return self.__class__.__name__ + return type(self).__name__ def get_kerning(self, next): return 0.0 @@ -1071,15 +929,8 @@ def shrink(self): """ self.size += 1 - def grow(self): - """ - Grows one level larger. There is no limit to how big - something can get. - """ - self.size -= 1 - - def render(self, x, y): - pass + def render(self, output, x, y): + """Render this node.""" class Box(Node): @@ -1098,13 +949,7 @@ def shrink(self): self.height *= SHRINK_FACTOR self.depth *= SHRINK_FACTOR - def grow(self): - super().grow() - self.width *= GROW_FACTOR - self.height *= GROW_FACTOR - self.depth *= GROW_FACTOR - - def render(self, x1, y1, x2, y2): + def render(self, output, x1, y1, x2, y2): pass @@ -1135,15 +980,14 @@ class Char(Node): `Hlist`. """ - def __init__(self, c, state, math=True): + def __init__(self, c, state): super().__init__() self.c = c - self.font_output = state.font_output + self.fontset = state.fontset self.font = state.font self.font_class = state.font_class self.fontsize = state.fontsize self.dpi = state.dpi - self.math = math # The real width, height and depth will be set during the # pack phase, after we know the real fontsize self._update_metrics() @@ -1152,9 +996,8 @@ def __repr__(self): return '`%s`' % self.c def _update_metrics(self): - metrics = self._metrics = self.font_output.get_metrics( - self.font, self.font_class, self.c, self.fontsize, self.dpi, - self.math) + metrics = self._metrics = self.fontset.get_metrics( + self.font, self.font_class, self.c, self.fontsize, self.dpi) if self.c == ' ': self.width = metrics.advance else: @@ -1175,18 +1018,15 @@ def get_kerning(self, next): advance = self._metrics.advance - self.width kern = 0. if isinstance(next, Char): - kern = self.font_output.get_kern( + kern = self.fontset.get_kern( self.font, self.font_class, self.c, self.fontsize, next.font, next.font_class, next.c, next.fontsize, self.dpi) return advance + kern - def render(self, x, y): - """ - Render the character to the canvas - """ - self.font_output.render_glyph( - x, y, + def render(self, output, x, y): + self.fontset.render_glyph( + output, x, y, self.font, self.font_class, self.c, self.fontsize, self.dpi) def shrink(self): @@ -1197,13 +1037,6 @@ def shrink(self): self.height *= SHRINK_FACTOR self.depth *= SHRINK_FACTOR - def grow(self): - super().grow() - self.fontsize *= GROW_FACTOR - self.width *= GROW_FACTOR - self.height *= GROW_FACTOR - self.depth *= GROW_FACTOR - class Accent(Char): """ @@ -1212,7 +1045,7 @@ class Accent(Char): TrueType fonts. """ def _update_metrics(self): - metrics = self._metrics = self.font_output.get_metrics( + metrics = self._metrics = self.fontset.get_metrics( self.font, self.font_class, self.c, self.fontsize, self.dpi) self.width = metrics.xmax - metrics.xmin self.height = metrics.ymax - metrics.ymin @@ -1222,16 +1055,9 @@ def shrink(self): super().shrink() self._update_metrics() - def grow(self): - super().grow() - self._update_metrics() - - def render(self, x, y): - """ - Render the character to the canvas. - """ - self.font_output.render_glyph( - x - self._metrics.xmin, y + self._metrics.ymin, + def render(self, output, x, y): + self.fontset.render_glyph( + output, x - self._metrics.xmin, y + self._metrics.ymin, self.font, self.font_class, self.c, self.fontsize, self.dpi) @@ -1248,27 +1074,16 @@ def __init__(self, elements): self.glue_order = 0 # The order of infinity (0 - 3) for the glue def __repr__(self): - return '[%s <%.02f %.02f %.02f %.02f> %s]' % ( + return '%s[%s]' % ( super().__repr__(), self.width, self.height, self.depth, self.shift_amount, - ' '.join([repr(x) for x in self.children])) - - @staticmethod - def _determine_order(totals): - """ - Determine the highest order of glue used by the members of this list. - - Helper function used by vpack and hpack. - """ - for i in range(len(totals))[::-1]: - if totals[i] != 0: - return i - return 0 + ', '.join([repr(x) for x in self.children])) def _set_glue(self, x, sign, totals, error_type): - o = self._determine_order(totals) - self.glue_order = o + self.glue_order = o = next( + # Highest order of glue used by the members of this list. + (i for i in range(len(totals))[::-1] if totals[i] != 0), 0) self.glue_sign = sign if totals[o] != 0.: self.glue_set = x / totals[o] @@ -1278,7 +1093,7 @@ def _set_glue(self, x, sign, totals, error_type): if o == 0: if len(self.children): _log.warning("%s %s: %r", - error_type, self.__class__.__name__, self) + error_type, type(self).__name__, self) def shrink(self): for child in self.children: @@ -1288,13 +1103,6 @@ def shrink(self): self.shift_amount *= SHRINK_FACTOR self.glue_set *= SHRINK_FACTOR - def grow(self): - for child in self.children: - child.grow() - super().grow() - self.shift_amount *= GROW_FACTOR - self.glue_set *= GROW_FACTOR - class Hlist(List): """A horizontal list of boxes.""" @@ -1303,7 +1111,7 @@ def __init__(self, elements, w=0., m='additional', do_kern=True): super().__init__(elements) if do_kern: self.kern() - self.hpack() + self.hpack(w=w, m=m) def kern(self): """ @@ -1404,9 +1212,9 @@ def hpack(self, w=0., m='additional'): self.glue_ratio = 0. return if x > 0.: - self._set_glue(x, 1, total_stretch, "Overfull") + self._set_glue(x, 1, total_stretch, "Overful") else: - self._set_glue(x, -1, total_shrink, "Underfull") + self._set_glue(x, -1, total_shrink, "Underful") class Vlist(List): @@ -1414,7 +1222,7 @@ class Vlist(List): def __init__(self, elements, h=0., m='additional'): super().__init__(elements) - self.vpack() + self.vpack(h=h, m=m) def vpack(self, h=0., m='additional', l=np.inf): """ @@ -1426,8 +1234,8 @@ def vpack(self, h=0., m='additional', l=np.inf): h : float, default: 0 A height. m : {'exactly', 'additional'}, default: 'additional' - Whether to produce a box whose height is 'exactly' *w*; or a box - with the natural height of the contents, plus *w* ('additional'). + Whether to produce a box whose height is 'exactly' *h*; or a box + with the natural height of the contents, plus *h* ('additional'). l : float, default: np.inf The maximum height. @@ -1483,9 +1291,9 @@ def vpack(self, h=0., m='additional', l=np.inf): return if x > 0.: - self._set_glue(x, 1, total_stretch, "Overfull") + self._set_glue(x, 1, total_stretch, "Overful") else: - self._set_glue(x, -1, total_shrink, "Underfull") + self._set_glue(x, -1, total_shrink, "Underful") class Rule(Box): @@ -1501,10 +1309,10 @@ class Rule(Box): def __init__(self, width, height, depth, state): super().__init__(width, height, depth) - self.font_output = state.font_output + self.fontset = state.fontset - def render(self, x, y, w, h): - self.font_output.render_rect_filled(x, y, x + w, y + h) + def render(self, output, x, y, w, h): + self.fontset.render_rect_filled(output, x, y, x + w, y + h) class Hrule(Rule): @@ -1512,8 +1320,7 @@ class Hrule(Rule): def __init__(self, state, thickness=None): if thickness is None: - thickness = state.font_output.get_underline_thickness( - state.font, state.fontsize, state.dpi) + thickness = state.get_current_underline_thickness() height = depth = thickness * 0.5 super().__init__(np.inf, height, depth, state) @@ -1522,8 +1329,7 @@ class Vrule(Rule): """Convenience class to create a vertical rule.""" def __init__(self, state): - thickness = state.font_output.get_underline_thickness( - state.font, state.fontsize, state.dpi) + thickness = state.get_current_underline_thickness() super().__init__(thickness, np.inf, np.inf, state) @@ -1549,10 +1355,7 @@ class Glue(Node): it's easier to stick to what TeX does.) """ - glue_subtype = _api.deprecated("3.3")(property(lambda self: "normal")) - - @_api.delete_parameter("3.3", "copy") - def __init__(self, glue_type, copy=False): + def __init__(self, glue_type): super().__init__() if isinstance(glue_type, str): glue_spec = _GlueSpec._named[glue_type] @@ -1568,56 +1371,6 @@ def shrink(self): g = self.glue_spec self.glue_spec = g._replace(width=g.width * SHRINK_FACTOR) - def grow(self): - super().grow() - g = self.glue_spec - self.glue_spec = g._replace(width=g.width * GROW_FACTOR) - - -# Some convenient ways to get common kinds of glue - - -@_api.deprecated("3.3", alternative="Glue('fil')") -class Fil(Glue): - def __init__(self): - super().__init__('fil') - - -@_api.deprecated("3.3", alternative="Glue('fill')") -class Fill(Glue): - def __init__(self): - super().__init__('fill') - - -@_api.deprecated("3.3", alternative="Glue('filll')") -class Filll(Glue): - def __init__(self): - super().__init__('filll') - - -@_api.deprecated("3.3", alternative="Glue('neg_fil')") -class NegFil(Glue): - def __init__(self): - super().__init__('neg_fil') - - -@_api.deprecated("3.3", alternative="Glue('neg_fill')") -class NegFill(Glue): - def __init__(self): - super().__init__('neg_fill') - - -@_api.deprecated("3.3", alternative="Glue('neg_filll')") -class NegFilll(Glue): - def __init__(self): - super().__init__('neg_filll') - - -@_api.deprecated("3.3", alternative="Glue('ss')") -class SsGlue(Glue): - def __init__(self): - super().__init__('ss') - class HCentered(Hlist): """ @@ -1665,25 +1418,6 @@ def shrink(self): if self.size < NUM_SIZE_LEVELS: self.width *= SHRINK_FACTOR - def grow(self): - super().grow() - self.width *= GROW_FACTOR - - -class SubSuperCluster(Hlist): - """ - A hack to get around that fact that this code does a two-pass parse like - TeX. This lets us store enough information in the hlist itself, namely the - nucleus, sub- and super-script, such that if another script follows that - needs to be attached, it can be reconfigured on the fly. - """ - - def __init__(self): - self.nucleus = None - self.sub = None - self.super = None - super().__init__([]) - class AutoHeightChar(Hlist): """ @@ -1695,10 +1429,10 @@ class AutoHeightChar(Hlist): """ def __init__(self, c, height, depth, state, always=False, factor=None): - alternatives = state.font_output.get_sized_alternatives_for_symbol( + alternatives = state.fontset.get_sized_alternatives_for_symbol( state.font, c) - xHeight = state.font_output.get_xheight( + xHeight = state.fontset.get_xheight( state.font, state.fontsize, state.dpi) state = state.copy() @@ -1734,7 +1468,7 @@ class AutoWidthChar(Hlist): """ def __init__(self, c, width, state, always=False, char_class=Char): - alternatives = state.font_output.get_sized_alternatives_for_symbol( + alternatives = state.fontset.get_sized_alternatives_for_symbol( state.font, c) state = state.copy() @@ -1752,81 +1486,72 @@ def __init__(self, c, width, state, always=False, char_class=Char): self.width = char.width -class Ship: +def ship(box, xy=(0, 0)): """ - Ship boxes to output once they have been set up, this sends them to output. + Ship out *box* at offset *xy*, converting it to an `Output`. - Since boxes can be inside of boxes inside of boxes, the main work of `Ship` + Since boxes can be inside of boxes inside of boxes, the main work of `ship` is done by two mutually recursive routines, `hlist_out` and `vlist_out`, which traverse the `Hlist` nodes and `Vlist` nodes inside of horizontal and vertical boxes. The global variables used in TeX to store state as it - processes have become member variables here. + processes have become local variables here. """ + ox, oy = xy + cur_v = 0. + cur_h = 0. + off_h = ox + off_v = oy + box.height + output = Output(box) - def __call__(self, ox, oy, box): - self.max_push = 0 # Deepest nesting of push commands so far - self.cur_s = 0 - self.cur_v = 0. - self.cur_h = 0. - self.off_h = ox - self.off_v = oy + box.height - self.hlist_out(box) - - @staticmethod def clamp(value): - if value < -1000000000.: - return -1000000000. - if value > 1000000000.: - return 1000000000. - return value - - def hlist_out(self, box): - cur_g = 0 - cur_glue = 0. - glue_order = box.glue_order - glue_sign = box.glue_sign - base_line = self.cur_v - left_edge = self.cur_h - self.cur_s += 1 - self.max_push = max(self.cur_s, self.max_push) - clamp = self.clamp + return -1e9 if value < -1e9 else +1e9 if value > +1e9 else value + + def hlist_out(box): + nonlocal cur_v, cur_h, off_h, off_v + + cur_g = 0 + cur_glue = 0. + glue_order = box.glue_order + glue_sign = box.glue_sign + base_line = cur_v + left_edge = cur_h for p in box.children: if isinstance(p, Char): - p.render(self.cur_h + self.off_h, self.cur_v + self.off_v) - self.cur_h += p.width + p.render(output, cur_h + off_h, cur_v + off_v) + cur_h += p.width elif isinstance(p, Kern): - self.cur_h += p.width + cur_h += p.width elif isinstance(p, List): # node623 if len(p.children) == 0: - self.cur_h += p.width + cur_h += p.width else: - edge = self.cur_h - self.cur_v = base_line + p.shift_amount + edge = cur_h + cur_v = base_line + p.shift_amount if isinstance(p, Hlist): - self.hlist_out(p) + hlist_out(p) else: # p.vpack(box.height + box.depth, 'exactly') - self.vlist_out(p) - self.cur_h = edge + p.width - self.cur_v = base_line + vlist_out(p) + cur_h = edge + p.width + cur_v = base_line elif isinstance(p, Box): # node624 rule_height = p.height - rule_depth = p.depth - rule_width = p.width + rule_depth = p.depth + rule_width = p.width if np.isinf(rule_height): rule_height = box.height if np.isinf(rule_depth): rule_depth = box.depth if rule_height > 0 and rule_width > 0: - self.cur_v = base_line + rule_depth - p.render(self.cur_h + self.off_h, - self.cur_v + self.off_v, + cur_v = base_line + rule_depth + p.render(output, + cur_h + off_h, cur_v + off_v, rule_width, rule_height) - self.cur_v = base_line - self.cur_h += rule_width + cur_v = base_line + cur_h += rule_width elif isinstance(p, Glue): # node625 glue_spec = p.glue_spec @@ -1840,38 +1565,36 @@ def hlist_out(self, box): cur_glue += glue_spec.shrink cur_g = round(clamp(box.glue_set * cur_glue)) rule_width += cur_g - self.cur_h += rule_width - self.cur_s -= 1 - - def vlist_out(self, box): - cur_g = 0 - cur_glue = 0. - glue_order = box.glue_order - glue_sign = box.glue_sign - self.cur_s += 1 - self.max_push = max(self.max_push, self.cur_s) - left_edge = self.cur_h - self.cur_v -= box.height - top_edge = self.cur_v - clamp = self.clamp + cur_h += rule_width + + def vlist_out(box): + nonlocal cur_v, cur_h, off_h, off_v + + cur_g = 0 + cur_glue = 0. + glue_order = box.glue_order + glue_sign = box.glue_sign + left_edge = cur_h + cur_v -= box.height + top_edge = cur_v for p in box.children: if isinstance(p, Kern): - self.cur_v += p.width + cur_v += p.width elif isinstance(p, List): if len(p.children) == 0: - self.cur_v += p.height + p.depth + cur_v += p.height + p.depth else: - self.cur_v += p.height - self.cur_h = left_edge + p.shift_amount - save_v = self.cur_v + cur_v += p.height + cur_h = left_edge + p.shift_amount + save_v = cur_v p.width = box.width if isinstance(p, Hlist): - self.hlist_out(p) + hlist_out(p) else: - self.vlist_out(p) - self.cur_v = save_v + p.depth - self.cur_h = left_edge + vlist_out(p) + cur_v = save_v + p.depth + cur_h = left_edge elif isinstance(p, Box): rule_height = p.height rule_depth = p.depth @@ -1880,9 +1603,9 @@ def vlist_out(self, box): rule_width = box.width rule_height += rule_depth if rule_height > 0 and rule_depth > 0: - self.cur_v += rule_height - p.render(self.cur_h + self.off_h, - self.cur_v + self.off_v, + cur_v += rule_height + p.render(output, + cur_h + off_h, cur_v + off_v, rule_width, rule_height) elif isinstance(p, Glue): glue_spec = p.glue_spec @@ -1896,14 +1619,13 @@ def vlist_out(self, box): cur_glue += glue_spec.shrink cur_g = round(clamp(box.glue_set * cur_glue)) rule_height += cur_g - self.cur_v += rule_height + cur_v += rule_height elif isinstance(p, Char): raise RuntimeError( "Internal mathtext error: Char node found in vlist") - self.cur_s -= 1 - -ship = Ship() + hlist_out(box) + return output ############################################################################## @@ -1915,9 +1637,69 @@ def Error(msg): def raise_error(s, loc, toks): raise ParseFatalException(s, loc, msg) - empty = Empty() - empty.setParseAction(raise_error) - return empty + return Empty().setParseAction(raise_error) + + +class ParserState: + """ + Parser state. + + States are pushed and popped from a stack as necessary, and the "current" + state is always at the top of the stack. + + Upon entering and leaving a group { } or math/non-math, the stack is pushed + and popped accordingly. + """ + + def __init__(self, fontset, font, font_class, fontsize, dpi): + self.fontset = fontset + self._font = font + self.font_class = font_class + self.fontsize = fontsize + self.dpi = dpi + + def copy(self): + return copy.copy(self) + + @property + def font(self): + return self._font + + @font.setter + def font(self, name): + if name in ('rm', 'it', 'bf'): + self.font_class = name + self._font = name + + def get_current_underline_thickness(self): + """Return the underline thickness for this state.""" + return self.fontset.get_underline_thickness( + self.font, self.fontsize, self.dpi) + + +def cmd(expr, args): + r""" + Helper to define TeX commands. + + ``cmd("\cmd", args)`` is equivalent to + ``"\cmd" - (args | Error("Expected \cmd{arg}{...}"))`` where the names in + the error message are taken from element names in *args*. If *expr* + already includes arguments (e.g. "\cmd{arg}{...}"), then they are stripped + when constructing the parse element, but kept (and *expr* is used as is) in + the error message. + """ + + def names(elt): + if isinstance(elt, ParseExpression): + for expr in elt.exprs: + yield from names(expr) + elif elt.resultsName: + yield elt.resultsName + + csname = expr.split("{", 1)[0] + err = (csname + "".join("{%s}" % name for name in names(args)) + if expr == csname else expr) + return csname - (args | Error(f"Expected {err}")) class Parser: @@ -1930,51 +1712,53 @@ class Parser: """ class _MathStyle(enum.Enum): - DISPLAYSTYLE = enum.auto() - TEXTSTYLE = enum.auto() - SCRIPTSTYLE = enum.auto() - SCRIPTSCRIPTSTYLE = enum.auto() - - _binary_operators = set(''' - + * - - \\pm \\sqcap \\rhd - \\mp \\sqcup \\unlhd - \\times \\vee \\unrhd - \\div \\wedge \\oplus - \\ast \\setminus \\ominus - \\star \\wr \\otimes - \\circ \\diamond \\oslash - \\bullet \\bigtriangleup \\odot - \\cdot \\bigtriangledown \\bigcirc - \\cap \\triangleleft \\dagger - \\cup \\triangleright \\ddagger - \\uplus \\lhd \\amalg'''.split()) - - _relation_symbols = set(''' + DISPLAYSTYLE = 0 + TEXTSTYLE = 1 + SCRIPTSTYLE = 2 + SCRIPTSCRIPTSTYLE = 3 + + _binary_operators = set( + '+ * - \N{MINUS SIGN}' + r''' + \pm \sqcap \rhd + \mp \sqcup \unlhd + \times \vee \unrhd + \div \wedge \oplus + \ast \setminus \ominus + \star \wr \otimes + \circ \diamond \oslash + \bullet \bigtriangleup \odot + \cdot \bigtriangledown \bigcirc + \cap \triangleleft \dagger + \cup \triangleright \ddagger + \uplus \lhd \amalg + \dotplus \dotminus'''.split()) + + _relation_symbols = set(r''' = < > : - \\leq \\geq \\equiv \\models - \\prec \\succ \\sim \\perp - \\preceq \\succeq \\simeq \\mid - \\ll \\gg \\asymp \\parallel - \\subset \\supset \\approx \\bowtie - \\subseteq \\supseteq \\cong \\Join - \\sqsubset \\sqsupset \\neq \\smile - \\sqsubseteq \\sqsupseteq \\doteq \\frown - \\in \\ni \\propto \\vdash - \\dashv \\dots \\dotplus \\doteqdot'''.split()) - - _arrow_symbols = set(''' - \\leftarrow \\longleftarrow \\uparrow - \\Leftarrow \\Longleftarrow \\Uparrow - \\rightarrow \\longrightarrow \\downarrow - \\Rightarrow \\Longrightarrow \\Downarrow - \\leftrightarrow \\longleftrightarrow \\updownarrow - \\Leftrightarrow \\Longleftrightarrow \\Updownarrow - \\mapsto \\longmapsto \\nearrow - \\hookleftarrow \\hookrightarrow \\searrow - \\leftharpoonup \\rightharpoonup \\swarrow - \\leftharpoondown \\rightharpoondown \\nwarrow - \\rightleftharpoons \\leadsto'''.split()) + \leq \geq \equiv \models + \prec \succ \sim \perp + \preceq \succeq \simeq \mid + \ll \gg \asymp \parallel + \subset \supset \approx \bowtie + \subseteq \supseteq \cong \Join + \sqsubset \sqsupset \neq \smile + \sqsubseteq \sqsupseteq \doteq \frown + \in \ni \propto \vdash + \dashv \dots \doteqdot'''.split()) + + _arrow_symbols = set(r''' + \leftarrow \longleftarrow \uparrow + \Leftarrow \Longleftarrow \Uparrow + \rightarrow \longrightarrow \downarrow + \Rightarrow \Longrightarrow \Downarrow + \leftrightarrow \longleftrightarrow \updownarrow + \Leftrightarrow \Longleftrightarrow \Updownarrow + \mapsto \longmapsto \nearrow + \hookleftarrow \hookrightarrow \searrow + \leftharpoonup \rightharpoonup \swarrow + \leftharpoondown \rightharpoondown \nwarrow + \rightleftharpoons \leadsto'''.split()) _spaced_symbols = _binary_operators | _relation_symbols | _arrow_symbols @@ -1985,8 +1769,7 @@ class _MathStyle(enum.Enum): \bigwedge \bigodot \bigotimes \bigoplus \biguplus '''.split()) - _overunder_functions = set( - "lim liminf limsup sup max min".split()) + _overunder_functions = set("lim liminf limsup sup max min".split()) _dropsub_symbols = set(r'''\int \oint'''.split()) @@ -1997,208 +1780,142 @@ class _MathStyle(enum.Enum): liminf sin cos exp limsup sinh cosh gcd ln sup cot hom log tan coth inf max tanh""".split()) - _ambi_delim = set(""" - | \\| / \\backslash \\uparrow \\downarrow \\updownarrow \\Uparrow - \\Downarrow \\Updownarrow . \\vert \\Vert \\\\|""".split()) - - _left_delim = set(r"( [ \{ < \lfloor \langle \lceil".split()) - - _right_delim = set(r") ] \} > \rfloor \rangle \rceil".split()) + _ambi_delims = set(r""" + | \| / \backslash \uparrow \downarrow \updownarrow \Uparrow + \Downarrow \Updownarrow . \vert \Vert""".split()) + _left_delims = set(r"( [ \{ < \lfloor \langle \lceil".split()) + _right_delims = set(r") ] \} > \rfloor \rangle \rceil".split()) + _delims = _left_delims | _right_delims | _ambi_delims def __init__(self): p = types.SimpleNamespace() - # All forward declarations are here + + def set_names_and_parse_actions(): + for key, val in vars(p).items(): + if not key.startswith('_'): + # Set names on everything -- very useful for debugging + val.setName(key) + # Set actions + if hasattr(self, key): + val.setParseAction(getattr(self, key)) + + # Root definitions. + + # In TeX parlance, a csname is a control sequence name (a "\foo"). + def csnames(group, names): + ends_with_alpha = [] + ends_with_nonalpha = [] + for name in names: + if name[-1].isalpha(): + ends_with_alpha.append(name) + else: + ends_with_nonalpha.append(name) + return Regex(r"\\(?P<{}>(?:{})(?![A-Za-z]){})".format( + group, + "|".join(map(re.escape, ends_with_alpha)), + "".join(f"|{s}" for s in map(re.escape, ends_with_nonalpha)), + )) + + p.float_literal = Regex(r"[-+]?([0-9]+\.?[0-9]*|\.[0-9]+)") + p.space = oneOf(self._space_widths)("space") + + p.style_literal = oneOf( + [str(e.value) for e in self._MathStyle])("style_literal") + + p.symbol = Regex( + r"[a-zA-Z0-9 +\-*/<>=:,.;!\?&'@()\[\]|\U00000080-\U0001ffff]" + r"|\\[%${}\[\]_|]" + + r"|\\(?:{})(?![A-Za-z])".format( + "|".join(map(re.escape, tex2uni))) + )("sym").leaveWhitespace() + p.unknown_symbol = Regex(r"\\[A-Za-z]*")("name") + + p.font = csnames("font", self._fontnames) + p.start_group = ( + Optional(r"\math" + oneOf(self._fontnames)("font")) + "{") + p.end_group = Literal("}") + + p.delim = oneOf(self._delims) + + set_names_and_parse_actions() # for root definitions. + + # Mutually recursive definitions. (Minimizing the number of Forward + # elements is important for speed.) p.accent = Forward() - p.ambi_delim = Forward() - p.apostrophe = Forward() p.auto_delim = Forward() p.binom = Forward() - p.bslash = Forward() - p.c_over_c = Forward() p.customspace = Forward() - p.end_group = Forward() - p.float_literal = Forward() - p.font = Forward() p.frac = Forward() p.dfrac = Forward() p.function = Forward() p.genfrac = Forward() p.group = Forward() - p.int_literal = Forward() - p.latexfont = Forward() - p.lbracket = Forward() - p.left_delim = Forward() - p.lbrace = Forward() - p.main = Forward() - p.math = Forward() - p.math_string = Forward() - p.non_math = Forward() p.operatorname = Forward() p.overline = Forward() p.overset = Forward() p.placeable = Forward() - p.rbrace = Forward() - p.rbracket = Forward() p.required_group = Forward() - p.right_delim = Forward() - p.right_delim_safe = Forward() p.simple = Forward() - p.simple_group = Forward() - p.single_symbol = Forward() - p.accentprefixed = Forward() - p.space = Forward() + p.optional_group = Forward() p.sqrt = Forward() - p.start_group = Forward() p.subsuper = Forward() - p.subsuperop = Forward() - p.symbol = Forward() - p.symbol_name = Forward() p.token = Forward() p.underset = Forward() - p.unknown_symbol = Forward() - - # Set names on everything -- very useful for debugging - for key, val in vars(p).items(): - if not key.startswith('_'): - val.setName(key) - - p.float_literal <<= Regex(r"[-+]?([0-9]+\.?[0-9]*|\.[0-9]+)") - p.int_literal <<= Regex("[-+]?[0-9]+") - - p.lbrace <<= Literal('{').suppress() - p.rbrace <<= Literal('}').suppress() - p.lbracket <<= Literal('[').suppress() - p.rbracket <<= Literal(']').suppress() - p.bslash <<= Literal('\\') - - p.space <<= oneOf(list(self._space_widths)) - p.customspace <<= ( - Suppress(Literal(r'\hspace')) - - ((p.lbrace + p.float_literal + p.rbrace) - | Error(r"Expected \hspace{n}")) - ) - - unicode_range = "\U00000080-\U0001ffff" - p.single_symbol <<= Regex( - r"([a-zA-Z0-9 +\-*/<>=:,.;!\?&'@()\[\]|%s])|(\\[%%${}\[\]_|])" % - unicode_range) - p.accentprefixed <<= Suppress(p.bslash) + oneOf(self._accentprefixed) - p.symbol_name <<= ( - Combine(p.bslash + oneOf(list(tex2uni))) - + FollowedBy(Regex("[^A-Za-z]").leaveWhitespace() | StringEnd()) - ) - p.symbol <<= (p.single_symbol | p.symbol_name).leaveWhitespace() - - p.apostrophe <<= Regex("'+") - - p.c_over_c <<= ( - Suppress(p.bslash) - + oneOf(list(self._char_over_chars)) - ) - - p.accent <<= Group( - Suppress(p.bslash) - + oneOf([*self._accent_map, *self._wide_accents]) - - p.placeable - ) - p.function <<= ( - Suppress(p.bslash) - + oneOf(list(self._function_names)) - ) + set_names_and_parse_actions() # for mutually recursive definitions. - p.start_group <<= Optional(p.latexfont) + p.lbrace - p.end_group <<= p.rbrace.copy() - p.simple_group <<= Group(p.lbrace + ZeroOrMore(p.token) + p.rbrace) - p.required_group <<= Group(p.lbrace + OneOrMore(p.token) + p.rbrace) - p.group <<= Group( - p.start_group + ZeroOrMore(p.token) + p.end_group - ) + p.customspace <<= cmd(r"\hspace", "{" + p.float_literal("space") + "}") - p.font <<= Suppress(p.bslash) + oneOf(list(self._fontnames)) - p.latexfont <<= ( - Suppress(p.bslash) - + oneOf(['math' + x for x in self._fontnames]) - ) + p.accent <<= ( + csnames("accent", [*self._accent_map, *self._wide_accents]) + - p.placeable("sym")) - p.frac <<= Group( - Suppress(Literal(r"\frac")) - - ((p.required_group + p.required_group) - | Error(r"Expected \frac{num}{den}")) - ) + p.function <<= csnames("name", self._function_names) + p.operatorname <<= cmd( + r"\operatorname", + "{" + ZeroOrMore(p.simple | p.unknown_symbol)("name") + "}") - p.dfrac <<= Group( - Suppress(Literal(r"\dfrac")) - - ((p.required_group + p.required_group) - | Error(r"Expected \dfrac{num}{den}")) - ) + p.group <<= p.start_group + ZeroOrMore(p.token)("group") + p.end_group - p.binom <<= Group( - Suppress(Literal(r"\binom")) - - ((p.required_group + p.required_group) - | Error(r"Expected \binom{num}{den}")) - ) + p.optional_group <<= "{" + ZeroOrMore(p.token)("group") + "}" + p.required_group <<= "{" + OneOrMore(p.token)("group") + "}" - p.ambi_delim <<= oneOf(list(self._ambi_delim)) - p.left_delim <<= oneOf(list(self._left_delim)) - p.right_delim <<= oneOf(list(self._right_delim)) - p.right_delim_safe <<= oneOf([*(self._right_delim - {'}'}), r'\}']) - - p.genfrac <<= Group( - Suppress(Literal(r"\genfrac")) - - (((p.lbrace - + Optional(p.ambi_delim | p.left_delim, default='') - + p.rbrace) - + (p.lbrace - + Optional(p.ambi_delim | p.right_delim_safe, default='') - + p.rbrace) - + (p.lbrace + p.float_literal + p.rbrace) - + p.simple_group + p.required_group + p.required_group) - | Error("Expected " - r"\genfrac{ldelim}{rdelim}{rulesize}{style}{num}{den}")) - ) + p.frac <<= cmd( + r"\frac", p.required_group("num") + p.required_group("den")) + p.dfrac <<= cmd( + r"\dfrac", p.required_group("num") + p.required_group("den")) + p.binom <<= cmd( + r"\binom", p.required_group("num") + p.required_group("den")) - p.sqrt <<= Group( - Suppress(Literal(r"\sqrt")) - - ((Group(Optional( - p.lbracket + OneOrMore(~p.rbracket + p.token) + p.rbracket)) - + p.required_group) - | Error("Expected \\sqrt{value}")) - ) + p.genfrac <<= cmd( + r"\genfrac", + "{" + Optional(p.delim)("ldelim") + "}" + + "{" + Optional(p.delim)("rdelim") + "}" + + "{" + p.float_literal("rulesize") + "}" + + "{" + Optional(p.style_literal)("style") + "}" + + p.required_group("num") + + p.required_group("den")) - p.overline <<= Group( - Suppress(Literal(r"\overline")) - - (p.required_group | Error("Expected \\overline{value}")) - ) - - p.overset <<= Group( - Suppress(Literal(r"\overset")) - - ((p.simple_group + p.simple_group) - | Error("Expected \\overset{body}{annotation}")) - ) - - p.underset <<= Group( - Suppress(Literal(r"\underset")) - - ((p.simple_group + p.simple_group) - | Error("Expected \\underset{body}{annotation}")) - ) + p.sqrt <<= cmd( + r"\sqrt{value}", + Optional("[" + OneOrMore(NotAny("]") + p.token)("root") + "]") + + p.required_group("value")) - p.unknown_symbol <<= Combine(p.bslash + Regex("[A-Za-z]*")) + p.overline <<= cmd(r"\overline", p.required_group("body")) - p.operatorname <<= Group( - Suppress(Literal(r"\operatorname")) - - ((p.lbrace + ZeroOrMore(p.simple | p.unknown_symbol) + p.rbrace) - | Error("Expected \\operatorname{value}")) - ) + p.overset <<= cmd( + r"\overset", + p.optional_group("annotation") + p.optional_group("body")) + p.underset <<= cmd( + r"\underset", + p.optional_group("annotation") + p.optional_group("body")) p.placeable <<= ( - p.accentprefixed # Must be before accent so named symbols that are - # prefixed with an accent name work - | p.accent # Must be before symbol as all accents are symbols - | p.symbol # Must be third to catch all named symbols and single + p.accent # Must be before symbol as all accents are symbols + | p.symbol # Must be second to catch all named symbols and single # chars not in a group - | p.c_over_c | p.function + | p.operatorname | p.group | p.frac | p.dfrac @@ -2208,7 +1925,6 @@ def __init__(self): | p.underset | p.sqrt | p.overline - | p.operatorname ) p.simple <<= ( @@ -2218,14 +1934,12 @@ def __init__(self): | p.subsuper ) - p.subsuperop <<= oneOf(["_", "^"]) - - p.subsuper <<= Group( - (Optional(p.placeable) - + OneOrMore(p.subsuperop - p.placeable) - + Optional(p.apostrophe)) - | (p.placeable + Optional(p.apostrophe)) - | p.apostrophe + p.subsuper <<= ( + (Optional(p.placeable)("nucleus") + + OneOrMore(oneOf(["_", "^"]) - p.placeable)("subsuper") + + Regex("'*")("apostrophes")) + | Regex("'+")("apostrophes") + | (p.placeable("nucleus") + Regex("'*")("apostrophes")) ) p.token <<= ( @@ -2235,34 +1949,26 @@ def __init__(self): ) p.auto_delim <<= ( - Suppress(Literal(r"\left")) - - ((p.left_delim | p.ambi_delim) - | Error("Expected a delimiter")) - + Group(ZeroOrMore(p.simple | p.auto_delim)) - + Suppress(Literal(r"\right")) - - ((p.right_delim | p.ambi_delim) - | Error("Expected a delimiter")) + r"\left" - (p.delim("left") | Error("Expected a delimiter")) + + ZeroOrMore(p.simple | p.auto_delim)("mid") + + r"\right" - (p.delim("right") | Error("Expected a delimiter")) ) - p.math <<= OneOrMore(p.token) - - p.math_string <<= QuotedString('$', '\\', unquoteResults=False) - - p.non_math <<= Regex(r"(?:(?:\\[$])|[^$])*").leaveWhitespace() - - p.main <<= ( + # Leaf definitions. + p.math = OneOrMore(p.token) + p.math_string = QuotedString('$', '\\', unquoteResults=False) + p.non_math = Regex(r"(?:(?:\\[$])|[^$])*").leaveWhitespace() + p.main = ( p.non_math + ZeroOrMore(p.math_string + p.non_math) + StringEnd() ) - - # Set actions - for key, val in vars(p).items(): - if not key.startswith('_'): - if hasattr(self, key): - val.setParseAction(getattr(self, key)) + set_names_and_parse_actions() # for leaf definitions. self._expression = p.main self._math_expression = p.math + # To add space to nucleus operators after sub/superscripts + self._in_subscript_or_superscript = False + def parse(self, s, fonts_object, fontsize, dpi): """ Parse expression *s* using the given *fonts_object* for @@ -2271,7 +1977,7 @@ def parse(self, s, fonts_object, fontsize, dpi): Returns the parse tree of `Node` instances. """ self._state_stack = [ - self.State(fonts_object, 'default', 'rm', fontsize, dpi)] + ParserState(fonts_object, 'default', 'rm', fontsize, dpi)] self._em_width_cache = {} try: result = self._expression.parseString(s) @@ -2281,46 +1987,12 @@ def parse(self, s, fonts_object, fontsize, dpi): " " * (err.column - 1) + "^", str(err)])) from err self._state_stack = None + self._in_subscript_or_superscript = False + # prevent operator spacing from leaking into a new expression self._em_width_cache = {} self._expression.resetCache() return result[0] - # The state of the parser is maintained in a stack. Upon - # entering and leaving a group { } or math/non-math, the stack - # is pushed and popped accordingly. The current state always - # exists in the top element of the stack. - class State: - """ - Stores the state of the parser. - - States are pushed and popped from a stack as necessary, and - the "current" state is always at the top of the stack. - """ - def __init__(self, font_output, font, font_class, fontsize, dpi): - self.font_output = font_output - self._font = font - self.font_class = font_class - self.fontsize = fontsize - self.dpi = dpi - - def copy(self): - return Parser.State( - self.font_output, - self.font, - self.font_class, - self.fontsize, - self.dpi) - - @property - def font(self): - return self._font - - @font.setter - def font(self, name): - if name in ('rm', 'it', 'bf'): - self.font_class = name - self._font = name - def get_state(self): """Get the current `State` of the parser.""" return self._state_stack[-1] @@ -2346,21 +2018,27 @@ def math(self, s, loc, toks): def non_math(self, s, loc, toks): s = toks[0].replace(r'\$', '$') - symbols = [Char(c, self.get_state(), math=False) for c in s] + symbols = [Char(c, self.get_state()) for c in s] hlist = Hlist(symbols) # We're going into math now, so set font to 'it' self.push_state() self.get_state().font = mpl.rcParams['mathtext.default'] return [hlist] + float_literal = staticmethod(pyparsing_common.convertToFloat) + def _make_space(self, percentage): - # All spaces are relative to em width + # In TeX, an em (the unit usually used to measure horizontal lengths) + # is not the width of the character 'm'; it is the same in different + # font styles (e.g. roman or italic). Mathtext, however, uses 'm' in + # the italic style so that horizontal spaces don't depend on the + # current font style. state = self.get_state() key = (state.font, state.fontsize, state.dpi) width = self._em_width_cache.get(key) if width is None: - metrics = state.font_output.get_metrics( - state.font, mpl.rcParams['mathtext.default'], 'm', + metrics = state.fontset.get_metrics( + 'it', mpl.rcParams['mathtext.default'], 'm', state.fontsize, state.dpi) width = metrics.advance self._em_width_cache[key] = width @@ -2382,16 +2060,22 @@ def _make_space(self, percentage): } def space(self, s, loc, toks): - tok, = toks - num = self._space_widths[tok] + num = self._space_widths[toks["space"]] box = self._make_space(num) return [box] def customspace(self, s, loc, toks): - return [self._make_space(float(toks[0]))] + return [self._make_space(toks["space"])] def symbol(self, s, loc, toks): - c, = toks + c = toks["sym"] + if c == "-": + # "U+2212 minus sign is the preferred representation of the unary + # and binary minus sign rather than the ASCII-derived U+002D + # hyphen-minus, because minus sign is unambiguous and because it + # is rendered with a more desirable length, usually longer than a + # hyphen." (https://www.unicode.org/reports/tr25/) + c = "\N{MINUS SIGN}" try: char = Char(c, self.get_state()) except ValueError as err: @@ -2405,7 +2089,7 @@ def symbol(self, s, loc, toks): # Binary operators at start of string should not be spaced if (c in self._binary_operators and (len(s[:loc].split()) == 0 or prev_char == '{' or - prev_char in self._left_delim)): + prev_char in self._left_delims)): return [char] else: return [Hlist([self._make_space(0.2), @@ -2413,70 +2097,23 @@ def symbol(self, s, loc, toks): self._make_space(0.2)], do_kern=True)] elif c in self._punctuation_symbols: + prev_char = next((c for c in s[:loc][::-1] if c != ' '), '') + next_char = next((c for c in s[loc + 1:] if c != ' '), '') # Do not space commas between brackets if c == ',': - prev_char = next((c for c in s[:loc][::-1] if c != ' '), '') - next_char = next((c for c in s[loc + 1:] if c != ' '), '') if prev_char == '{' and next_char == '}': return [char] # Do not space dots as decimal separators - if c == '.' and s[loc - 1].isdigit() and s[loc + 1].isdigit(): + if c == '.' and prev_char.isdigit() and next_char.isdigit(): return [char] else: return [Hlist([char, self._make_space(0.2)], do_kern=True)] return [char] - accentprefixed = symbol - def unknown_symbol(self, s, loc, toks): - c, = toks - raise ParseFatalException(s, loc, "Unknown symbol: %s" % c) - - _char_over_chars = { - # The first 2 entries in the tuple are (font, char, sizescale) for - # the two symbols under and over. The third element is the space - # (in multiples of underline height) - r'AA': (('it', 'A', 1.0), (None, '\\circ', 0.5), 0.0), - } - - def c_over_c(self, s, loc, toks): - sym, = toks - state = self.get_state() - thickness = state.font_output.get_underline_thickness( - state.font, state.fontsize, state.dpi) - - under_desc, over_desc, space = \ - self._char_over_chars.get(sym, (None, None, 0.0)) - if under_desc is None: - raise ParseFatalException("Error parsing symbol") - - over_state = state.copy() - if over_desc[0] is not None: - over_state.font = over_desc[0] - over_state.fontsize *= over_desc[2] - over = Accent(over_desc[1], over_state) - - under_state = state.copy() - if under_desc[0] is not None: - under_state.font = under_desc[0] - under_state.fontsize *= under_desc[2] - under = Char(under_desc[1], under_state) - - width = max(over.width, under.width) - - over_centered = HCentered([over]) - over_centered.hpack(width, 'exactly') - - under_centered = HCentered([under]) - under_centered.hpack(width, 'exactly') - - return Vlist([ - over_centered, - Vbox(0., thickness * space), - under_centered - ]) + raise ParseFatalException(s, loc, f"Unknown symbol: {toks['name']}") _accent_map = { r'hat': r'\circumflexaccent', @@ -2487,6 +2124,8 @@ def c_over_c(self, s, loc, toks): r'tilde': r'\combiningtilde', r'dot': r'\combiningdotabove', r'ddot': r'\combiningdiaeresis', + r'dddot': r'\combiningthreedotsabove', + r'ddddot': r'\combiningfourdotsabove', r'vec': r'\combiningrightarrowabove', r'"': r'\combiningdiaeresis', r"`": r'\combininggraveaccent', @@ -2501,17 +2140,11 @@ def c_over_c(self, s, loc, toks): _wide_accents = set(r"widehat widetilde widebar".split()) - # make a lambda and call it to get the namespace right - _accentprefixed = (lambda am: [ - p for p in tex2uni - if any(p.startswith(a) and a != p for a in am) - ])(set(_accent_map)) - def accent(self, s, loc, toks): state = self.get_state() - thickness = state.font_output.get_underline_thickness( - state.font, state.fontsize, state.dpi) - (accent, sym), = toks + thickness = state.get_current_underline_thickness() + accent = toks["accent"] + sym = toks["sym"] if accent in self._wide_accents: accent_box = AutoWidthChar( '\\' + accent, sym.width, state, char_class=Accent) @@ -2530,7 +2163,7 @@ def accent(self, s, loc, toks): def function(self, s, loc, toks): hlist = self.operatorname(s, loc, toks) - hlist.function_name, = toks + hlist.function_name = toks["name"] return hlist def operatorname(self, s, loc, toks): @@ -2539,7 +2172,8 @@ def operatorname(self, s, loc, toks): state.font = 'rm' hlist_list = [] # Change the font of Chars, but leave Kerns alone - for c in toks[0]: + name = toks["name"] + for c in name: if isinstance(c, Char): c.font = 'rm' c._update_metrics() @@ -2548,38 +2182,47 @@ def operatorname(self, s, loc, toks): hlist_list.append(Char(c, state)) else: hlist_list.append(c) - next_char_loc = loc + len(toks[0]) + 1 - if isinstance(toks[0], ParseResults): + next_char_loc = loc + len(name) + 1 + if isinstance(name, ParseResults): next_char_loc += len('operatorname{}') next_char = next((c for c in s[next_char_loc:] if c != ' '), '') - delimiters = self._left_delim | self._ambi_delim | self._right_delim - delimiters |= {'^', '_'} + delimiters = self._delims | {'^', '_'} if (next_char not in delimiters and - toks[0] not in self._overunder_functions): + name not in self._overunder_functions): # Add thin space except when followed by parenthesis, bracket, etc. hlist_list += [self._make_space(self._space_widths[r'\,'])] self.pop_state() + # if followed by a super/subscript, set flag to true + # This flag tells subsuper to add space after this operator + if next_char in {'^', '_'}: + self._in_subscript_or_superscript = True + else: + self._in_subscript_or_superscript = False + return Hlist(hlist_list) def start_group(self, s, loc, toks): self.push_state() # Deal with LaTeX-style font tokens - if len(toks): - self.get_state().font = toks[0][4:] + if toks.get("font"): + self.get_state().font = toks.get("font") return [] def group(self, s, loc, toks): - grp = Hlist(toks[0]) + grp = Hlist(toks.get("group", [])) return [grp] - required_group = simple_group = group + + def required_group(self, s, loc, toks): + return Hlist(toks.get("group", [])) + + optional_group = required_group def end_group(self, s, loc, toks): self.pop_state() return [] def font(self, s, loc, toks): - name, = toks - self.get_state().font = name + self.get_state().font = toks["font"] return [] def is_overunder(self, nucleus): @@ -2603,72 +2246,36 @@ def is_between_brackets(self, s, loc): return False def subsuper(self, s, loc, toks): - assert len(toks) == 1 - - nucleus = None - sub = None - super = None - - # Pick all of the apostrophes out, including first apostrophes that - # have been parsed as characters - napostrophes = 0 - new_toks = [] - for tok in toks[0]: - if isinstance(tok, str) and tok not in ('^', '_'): - napostrophes += len(tok) - elif isinstance(tok, Char) and tok.c == "'": - napostrophes += 1 - else: - new_toks.append(tok) - toks = new_toks - - if len(toks) == 0: - assert napostrophes - nucleus = Hbox(0.0) - elif len(toks) == 1: - if not napostrophes: - return toks[0] # .asList() - else: - nucleus = toks[0] - elif len(toks) in (2, 3): - # single subscript or superscript - nucleus = toks[0] if len(toks) == 3 else Hbox(0.0) - op, next = toks[-2:] + nucleus = toks.get("nucleus", Hbox(0)) + subsuper = toks.get("subsuper", []) + napostrophes = len(toks.get("apostrophes", [])) + + if not subsuper and not napostrophes: + return nucleus + + sub = super = None + while subsuper: + op, arg, *subsuper = subsuper if op == '_': - sub = next - else: - super = next - elif len(toks) in (4, 5): - # subscript and superscript - nucleus = toks[0] if len(toks) == 5 else Hbox(0.0) - op1, next1, op2, next2 = toks[-4:] - if op1 == op2: - if op1 == '_': + if sub is not None: raise ParseFatalException("Double subscript") - else: - raise ParseFatalException("Double superscript") - if op1 == '_': - sub = next1 - super = next2 + sub = arg else: - super = next1 - sub = next2 - else: - raise ParseFatalException( - "Subscript/superscript sequence is too long. " - "Use braces { } to remove ambiguity.") + if super is not None: + raise ParseFatalException("Double superscript") + super = arg state = self.get_state() - rule_thickness = state.font_output.get_underline_thickness( + rule_thickness = state.fontset.get_underline_thickness( state.font, state.fontsize, state.dpi) - xHeight = state.font_output.get_xheight( + xHeight = state.fontset.get_xheight( state.font, state.fontsize, state.dpi) if napostrophes: if super is None: super = Hlist([]) for i in range(napostrophes): - super.children.extend(self.symbol(s, loc, ['\\prime'])) + super.children.extend(self.symbol(s, loc, {"sym": "\\prime"})) # kern() and hpack() needed to get the metrics right after # extending super.kern() @@ -2698,9 +2305,9 @@ def subsuper(self, s, loc, toks): hlist = HCentered([sub]) hlist.hpack(width, 'exactly') vlist.extend([Vbox(0, vgap), hlist]) - shift = hlist.height + vgap + shift = hlist.height + vgap + nucleus.depth vlist = Vlist(vlist) - vlist.shift_amount = shift + nucleus.depth + vlist.shift_amount = shift result = Hlist([vlist]) return [result] @@ -2789,18 +2396,22 @@ def subsuper(self, s, loc, toks): if not self.is_dropsub(last_char): x.width += constants.script_space * xHeight - result = Hlist([nucleus, x]) + # Do we need to add a space after the nucleus? + # To find out, check the flag set by operatorname + spaced_nucleus = [nucleus, x] + if self._in_subscript_or_superscript: + spaced_nucleus += [self._make_space(self._space_widths[r'\,'])] + self._in_subscript_or_superscript = False + + result = Hlist(spaced_nucleus) return [result] def _genfrac(self, ldelim, rdelim, rule, style, num, den): state = self.get_state() - thickness = state.font_output.get_underline_thickness( - state.font, state.fontsize, state.dpi) - - rule = float(rule) + thickness = state.get_current_underline_thickness() - if style is not self._MathStyle.DISPLAYSTYLE: + for _ in range(style.value): num.shrink() den.shrink() cnum = HCentered([num]) @@ -2817,7 +2428,7 @@ def _genfrac(self, ldelim, rdelim, rule, style, num, den): # Shift so the fraction line sits in the middle of the # equals sign - metrics = state.font_output.get_metrics( + metrics = state.fontset.get_metrics( state.font, mpl.rcParams['mathtext.default'], '=', state.fontsize, state.dpi) shift = (cden.height - @@ -2834,37 +2445,36 @@ def _genfrac(self, ldelim, rdelim, rule, style, num, den): return self._auto_sized_delimiter(ldelim, result, rdelim) return result + def style_literal(self, s, loc, toks): + return self._MathStyle(int(toks["style_literal"])) + def genfrac(self, s, loc, toks): - args, = toks - return self._genfrac(*args) + return self._genfrac( + toks.get("ldelim", ""), toks.get("rdelim", ""), + toks["rulesize"], toks.get("style", self._MathStyle.TEXTSTYLE), + toks["num"], toks["den"]) def frac(self, s, loc, toks): - state = self.get_state() - thickness = state.font_output.get_underline_thickness( - state.font, state.fontsize, state.dpi) - (num, den), = toks - return self._genfrac('', '', thickness, self._MathStyle.TEXTSTYLE, - num, den) + return self._genfrac( + "", "", self.get_state().get_current_underline_thickness(), + self._MathStyle.TEXTSTYLE, toks["num"], toks["den"]) def dfrac(self, s, loc, toks): - state = self.get_state() - thickness = state.font_output.get_underline_thickness( - state.font, state.fontsize, state.dpi) - (num, den), = toks - return self._genfrac('', '', thickness, self._MathStyle.DISPLAYSTYLE, - num, den) + return self._genfrac( + "", "", self.get_state().get_current_underline_thickness(), + self._MathStyle.DISPLAYSTYLE, toks["num"], toks["den"]) def binom(self, s, loc, toks): - (num, den), = toks - return self._genfrac('(', ')', 0.0, self._MathStyle.TEXTSTYLE, - num, den) + return self._genfrac( + "(", ")", 0, + self._MathStyle.TEXTSTYLE, toks["num"], toks["den"]) - def _genset(self, state, annotation, body, overunder): - thickness = state.font_output.get_underline_thickness( - state.font, state.fontsize, state.dpi) + def _genset(self, s, loc, toks): + annotation = toks["annotation"] + body = toks["body"] + thickness = self.get_state().get_current_underline_thickness() annotation.shrink() - cannotation = HCentered([annotation]) cbody = HCentered([body]) width = max(cannotation.width, cbody.width) @@ -2872,16 +2482,14 @@ def _genset(self, state, annotation, body, overunder): cbody.hpack(width, 'exactly') vgap = thickness * 3 - if overunder == "under": + if s[loc + 1] == "u": # \underset vlist = Vlist([cbody, # body Vbox(0, vgap), # space cannotation # annotation ]) # Shift so the body sits in the same vertical position - shift_amount = cbody.depth + cannotation.height + vgap - - vlist.shift_amount = shift_amount - else: + vlist.shift_amount = cbody.depth + cannotation.height + vgap + else: # \overset vlist = Vlist([cannotation, # annotation Vbox(0, vgap), # space cbody # body @@ -2891,11 +2499,13 @@ def _genset(self, state, annotation, body, overunder): # an Hlist and extend it with an Hbox(0, horizontal_gap) return vlist + overset = underset = _genset + def sqrt(self, s, loc, toks): - (root, body), = toks + root = toks.get("root") + body = toks["value"] state = self.get_state() - thickness = state.font_output.get_underline_thickness( - state.font, state.fontsize, state.dpi) + thickness = state.get_current_underline_thickness() # Determine the height of the body, and add a little extra to # the height so it doesn't seem cramped @@ -2932,11 +2542,10 @@ def sqrt(self, s, loc, toks): return [hlist] def overline(self, s, loc, toks): - (body,), = toks + body = toks["body"] state = self.get_state() - thickness = state.font_output.get_underline_thickness( - state.font, state.fontsize, state.dpi) + thickness = state.get_current_underline_thickness() height = body.height - body.shift_amount + thickness * 3.0 depth = body.depth + body.shift_amount @@ -2951,24 +2560,6 @@ def overline(self, s, loc, toks): hlist = Hlist([rightside]) return [hlist] - def overset(self, s, loc, toks): - assert len(toks) == 1 - assert len(toks[0]) == 2 - - state = self.get_state() - annotation, body = toks[0] - - return self._genset(state, annotation, body, overunder="over") - - def underset(self, s, loc, toks): - assert len(toks) == 1 - assert len(toks[0]) == 2 - - state = self.get_state() - annotation, body = toks[0] - - return self._genset(state, annotation, body, overunder="under") - def _auto_sized_delimiter(self, front, middle, back): state = self.get_state() if len(middle): @@ -2992,6 +2583,8 @@ def _auto_sized_delimiter(self, front, middle, back): return hlist def auto_delim(self, s, loc, toks): - front, middle, back = toks - - return self._auto_sized_delimiter(front, middle.asList(), back) + return self._auto_sized_delimiter( + toks["left"], + # if "mid" in toks ... can be removed when requiring pyparsing 3. + toks["mid"].asList() if "mid" in toks else [], + toks["right"]) diff --git a/lib/matplotlib/_mathtext_data.py b/lib/matplotlib/_mathtext_data.py index e17f1e05903f..8dac9301ed81 100644 --- a/lib/matplotlib/_mathtext_data.py +++ b/lib/matplotlib/_mathtext_data.py @@ -132,7 +132,7 @@ ']' : ('cmr10', 0x5d), '*' : ('cmsy10', 0xa4), - '-' : ('cmsy10', 0xa1), + '\N{MINUS SIGN}' : ('cmsy10', 0xa1), '\\Downarrow' : ('cmsy10', 0x2b), '\\Im' : ('cmsy10', 0x3d), '\\Leftarrow' : ('cmsy10', 0x28), @@ -236,173 +236,6 @@ '\\_' : ('cmtt10', 0x5f) } -latex_to_cmex = { # Unused; delete once mathtext becomes private. - r'\__sqrt__' : 112, - r'\bigcap' : 92, - r'\bigcup' : 91, - r'\bigodot' : 75, - r'\bigoplus' : 77, - r'\bigotimes' : 79, - r'\biguplus' : 93, - r'\bigvee' : 95, - r'\bigwedge' : 94, - r'\coprod' : 97, - r'\int' : 90, - r'\leftangle' : 173, - r'\leftbrace' : 169, - r'\oint' : 73, - r'\prod' : 89, - r'\rightangle' : 174, - r'\rightbrace' : 170, - r'\sum' : 88, - r'\widehat' : 98, - r'\widetilde' : 101, -} - -latex_to_standard = { - r'\cong' : ('psyr', 64), - r'\Delta' : ('psyr', 68), - r'\Phi' : ('psyr', 70), - r'\Gamma' : ('psyr', 89), - r'\alpha' : ('psyr', 97), - r'\beta' : ('psyr', 98), - r'\chi' : ('psyr', 99), - r'\delta' : ('psyr', 100), - r'\varepsilon' : ('psyr', 101), - r'\phi' : ('psyr', 102), - r'\gamma' : ('psyr', 103), - r'\eta' : ('psyr', 104), - r'\iota' : ('psyr', 105), - r'\varphi' : ('psyr', 106), - r'\kappa' : ('psyr', 108), - r'\nu' : ('psyr', 110), - r'\pi' : ('psyr', 112), - r'\theta' : ('psyr', 113), - r'\rho' : ('psyr', 114), - r'\sigma' : ('psyr', 115), - r'\tau' : ('psyr', 116), - r'\upsilon' : ('psyr', 117), - r'\varpi' : ('psyr', 118), - r'\omega' : ('psyr', 119), - r'\xi' : ('psyr', 120), - r'\psi' : ('psyr', 121), - r'\zeta' : ('psyr', 122), - r'\sim' : ('psyr', 126), - r'\leq' : ('psyr', 163), - r'\infty' : ('psyr', 165), - r'\clubsuit' : ('psyr', 167), - r'\diamondsuit' : ('psyr', 168), - r'\heartsuit' : ('psyr', 169), - r'\spadesuit' : ('psyr', 170), - r'\leftrightarrow' : ('psyr', 171), - r'\leftarrow' : ('psyr', 172), - r'\uparrow' : ('psyr', 173), - r'\rightarrow' : ('psyr', 174), - r'\downarrow' : ('psyr', 175), - r'\pm' : ('psyr', 176), - r'\geq' : ('psyr', 179), - r'\times' : ('psyr', 180), - r'\propto' : ('psyr', 181), - r'\partial' : ('psyr', 182), - r'\bullet' : ('psyr', 183), - r'\div' : ('psyr', 184), - r'\neq' : ('psyr', 185), - r'\equiv' : ('psyr', 186), - r'\approx' : ('psyr', 187), - r'\ldots' : ('psyr', 188), - r'\aleph' : ('psyr', 192), - r'\Im' : ('psyr', 193), - r'\Re' : ('psyr', 194), - r'\wp' : ('psyr', 195), - r'\otimes' : ('psyr', 196), - r'\oplus' : ('psyr', 197), - r'\oslash' : ('psyr', 198), - r'\cap' : ('psyr', 199), - r'\cup' : ('psyr', 200), - r'\supset' : ('psyr', 201), - r'\supseteq' : ('psyr', 202), - r'\subset' : ('psyr', 204), - r'\subseteq' : ('psyr', 205), - r'\in' : ('psyr', 206), - r'\notin' : ('psyr', 207), - r'\angle' : ('psyr', 208), - r'\nabla' : ('psyr', 209), - r'\textregistered' : ('psyr', 210), - r'\copyright' : ('psyr', 211), - r'\texttrademark' : ('psyr', 212), - r'\Pi' : ('psyr', 213), - r'\prod' : ('psyr', 213), - r'\surd' : ('psyr', 214), - r'\__sqrt__' : ('psyr', 214), - r'\cdot' : ('psyr', 215), - r'\urcorner' : ('psyr', 216), - r'\vee' : ('psyr', 217), - r'\wedge' : ('psyr', 218), - r'\Leftrightarrow' : ('psyr', 219), - r'\Leftarrow' : ('psyr', 220), - r'\Uparrow' : ('psyr', 221), - r'\Rightarrow' : ('psyr', 222), - r'\Downarrow' : ('psyr', 223), - r'\Diamond' : ('psyr', 224), - r'\Sigma' : ('psyr', 229), - r'\sum' : ('psyr', 229), - r'\forall' : ('psyr', 34), - r'\exists' : ('psyr', 36), - r'\lceil' : ('psyr', 233), - r'\lbrace' : ('psyr', 123), - r'\Psi' : ('psyr', 89), - r'\bot' : ('psyr', 0o136), - r'\Omega' : ('psyr', 0o127), - r'\leftbracket' : ('psyr', 0o133), - r'\rightbracket' : ('psyr', 0o135), - r'\leftbrace' : ('psyr', 123), - r'\leftparen' : ('psyr', 0o50), - r'\prime' : ('psyr', 0o242), - r'\sharp' : ('psyr', 0o43), - r'\slash' : ('psyr', 0o57), - r'\Lambda' : ('psyr', 0o114), - r'\neg' : ('psyr', 0o330), - r'\Upsilon' : ('psyr', 0o241), - r'\rightbrace' : ('psyr', 0o175), - r'\rfloor' : ('psyr', 0o373), - r'\lambda' : ('psyr', 0o154), - r'\to' : ('psyr', 0o256), - r'\Xi' : ('psyr', 0o130), - r'\emptyset' : ('psyr', 0o306), - r'\lfloor' : ('psyr', 0o353), - r'\rightparen' : ('psyr', 0o51), - r'\rceil' : ('psyr', 0o371), - r'\ni' : ('psyr', 0o47), - r'\epsilon' : ('psyr', 0o145), - r'\Theta' : ('psyr', 0o121), - r'\langle' : ('psyr', 0o341), - r'\leftangle' : ('psyr', 0o341), - r'\rangle' : ('psyr', 0o361), - r'\rightangle' : ('psyr', 0o361), - r'\rbrace' : ('psyr', 0o175), - r'\circ' : ('psyr', 0o260), - r'\diamond' : ('psyr', 0o340), - r'\mu' : ('psyr', 0o155), - r'\mid' : ('psyr', 0o352), - r'\imath' : ('pncri8a', 105), - r'\%' : ('pncr8a', 37), - r'\$' : ('pncr8a', 36), - r'\{' : ('pncr8a', 123), - r'\}' : ('pncr8a', 125), - r'\backslash' : ('pncr8a', 92), - r'\ast' : ('pncr8a', 42), - r'\#' : ('pncr8a', 35), - - r'\circumflexaccent' : ('pncri8a', 124), # for \hat - r'\combiningbreve' : ('pncri8a', 81), # for \breve - r'\combininggraveaccent' : ('pncri8a', 114), # for \grave - r'\combiningacuteaccent' : ('pncri8a', 63), # for \accute - r'\combiningdiaeresis' : ('pncri8a', 91), # for \ddot - r'\combiningtilde' : ('pncri8a', 75), # for \tilde - r'\combiningrightarrowabove' : ('pncri8a', 110), # for \vec - r'\combiningdotabove' : ('pncri8a', 26), # for \dot -} - # Automatically generated. type12uni = { @@ -1116,6 +949,7 @@ 'cwopencirclearrow' : 8635, 'geqq' : 8807, 'rightleftarrows' : 8644, + 'aa' : 229, 'ac' : 8766, 'ae' : 230, 'int' : 8747, @@ -1166,6 +1000,8 @@ 'combiningtilde' : 771, 'combiningrightarrowabove' : 8407, 'combiningdotabove' : 775, + 'combiningthreedotsabove' : 8411, + 'combiningfourdotsabove' : 8412, 'to' : 8594, 'succeq' : 8829, 'emptyset' : 8709, @@ -1382,3 +1218,11 @@ (0x0061, 0x007a, 'rm', 0x1d68a) # a-z ], } + + +# Fix some incorrect glyphs. +stix_glyph_fixes = { + # Cap and Cup glyphs are swapped. + 0x22d2: 0x22d3, + 0x22d3: 0x22d2, +} diff --git a/lib/matplotlib/_pylab_helpers.py b/lib/matplotlib/_pylab_helpers.py index 27904dd84b6f..d32a69d4ff99 100644 --- a/lib/matplotlib/_pylab_helpers.py +++ b/lib/matplotlib/_pylab_helpers.py @@ -4,7 +4,6 @@ import atexit from collections import OrderedDict -import gc class Gcf: @@ -65,7 +64,7 @@ def destroy(cls, num): if hasattr(manager, "_cidgcf"): manager.canvas.mpl_disconnect(manager._cidgcf) manager.destroy() - gc.collect(1) + del manager, num @classmethod def destroy_fig(cls, fig): @@ -78,14 +77,10 @@ def destroy_fig(cls, fig): @classmethod def destroy_all(cls): """Destroy all figures.""" - # Reimport gc in case the module globals have already been removed - # during interpreter shutdown. - import gc for manager in list(cls.figs.values()): manager.canvas.mpl_disconnect(manager._cidgcf) manager.destroy() cls.figs.clear() - gc.collect(1) @classmethod def has_fignum(cls, num): diff --git a/lib/matplotlib/_text_helpers.py b/lib/matplotlib/_text_helpers.py new file mode 100644 index 000000000000..18bfb550c90b --- /dev/null +++ b/lib/matplotlib/_text_helpers.py @@ -0,0 +1,74 @@ +""" +Low-level text helper utilities. +""" + +import dataclasses + +from . import _api +from .ft2font import KERNING_DEFAULT, LOAD_NO_HINTING + + +LayoutItem = dataclasses.make_dataclass( + "LayoutItem", ["ft_object", "char", "glyph_idx", "x", "prev_kern"]) + + +def warn_on_missing_glyph(codepoint): + _api.warn_external( + "Glyph {} ({}) missing from current font.".format( + codepoint, + chr(codepoint).encode("ascii", "namereplace").decode("ascii"))) + block = ("Hebrew" if 0x0590 <= codepoint <= 0x05ff else + "Arabic" if 0x0600 <= codepoint <= 0x06ff else + "Devanagari" if 0x0900 <= codepoint <= 0x097f else + "Bengali" if 0x0980 <= codepoint <= 0x09ff else + "Gurmukhi" if 0x0a00 <= codepoint <= 0x0a7f else + "Gujarati" if 0x0a80 <= codepoint <= 0x0aff else + "Oriya" if 0x0b00 <= codepoint <= 0x0b7f else + "Tamil" if 0x0b80 <= codepoint <= 0x0bff else + "Telugu" if 0x0c00 <= codepoint <= 0x0c7f else + "Kannada" if 0x0c80 <= codepoint <= 0x0cff else + "Malayalam" if 0x0d00 <= codepoint <= 0x0d7f else + "Sinhala" if 0x0d80 <= codepoint <= 0x0dff else + None) + if block: + _api.warn_external( + f"Matplotlib currently does not support {block} natively.") + + +def layout(string, font, *, kern_mode=KERNING_DEFAULT): + """ + Render *string* with *font*. For each character in *string*, yield a + (glyph-index, x-position) pair. When such a pair is yielded, the font's + glyph is set to the corresponding character. + + Parameters + ---------- + string : str + The string to be rendered. + font : FT2Font + The font. + kern_mode : int + A FreeType kerning mode. + + Yields + ------ + glyph_index : int + x_position : float + """ + x = 0 + prev_glyph_idx = None + char_to_font = font._get_fontmap(string) + base_font = font + for char in string: + # This has done the fallback logic + font = char_to_font.get(char, base_font) + glyph_idx = font.get_char_index(ord(char)) + kern = ( + base_font.get_kerning(prev_glyph_idx, glyph_idx, kern_mode) / 64 + if prev_glyph_idx is not None else 0. + ) + x += kern + glyph = font.load_glyph(glyph_idx, flags=LOAD_NO_HINTING) + yield LayoutItem(font, char, glyph_idx, x, kern) + x += glyph.linearHoriAdvance / 65536 + prev_glyph_idx = glyph_idx diff --git a/lib/matplotlib/_text_layout.py b/lib/matplotlib/_text_layout.py deleted file mode 100644 index c7a87e848688..000000000000 --- a/lib/matplotlib/_text_layout.py +++ /dev/null @@ -1,44 +0,0 @@ -""" -Text layouting utilities. -""" - -import dataclasses - -from .ft2font import KERNING_DEFAULT, LOAD_NO_HINTING - - -LayoutItem = dataclasses.make_dataclass( - "LayoutItem", ["char", "glyph_idx", "x", "prev_kern"]) - - -def layout(string, font, *, kern_mode=KERNING_DEFAULT): - """ - Render *string* with *font*. For each character in *string*, yield a - (glyph-index, x-position) pair. When such a pair is yielded, the font's - glyph is set to the corresponding character. - - Parameters - ---------- - string : str - The string to be rendered. - font : FT2Font - The font. - kern_mode : int - A FreeType kerning mode. - - Yields - ------ - glyph_index : int - x_position : float - """ - x = 0 - prev_glyph_idx = None - for char in string: - glyph_idx = font.get_char_index(ord(char)) - kern = (font.get_kerning(prev_glyph_idx, glyph_idx, kern_mode) / 64 - if prev_glyph_idx is not None else 0.) - x += kern - glyph = font.load_glyph(glyph_idx, flags=LOAD_NO_HINTING) - yield LayoutItem(char, glyph_idx, x, kern) - x += glyph.linearHoriAdvance / 65536 - prev_glyph_idx = glyph_idx diff --git a/lib/matplotlib/_tight_bbox.py b/lib/matplotlib/_tight_bbox.py new file mode 100644 index 000000000000..b2147b960735 --- /dev/null +++ b/lib/matplotlib/_tight_bbox.py @@ -0,0 +1,84 @@ +""" +Helper module for the *bbox_inches* parameter in `.Figure.savefig`. +""" + +from matplotlib.transforms import Bbox, TransformedBbox, Affine2D + + +def adjust_bbox(fig, bbox_inches, fixed_dpi=None): + """ + Temporarily adjust the figure so that only the specified area + (bbox_inches) is saved. + + It modifies fig.bbox, fig.bbox_inches, + fig.transFigure._boxout, and fig.patch. While the figure size + changes, the scale of the original figure is conserved. A + function which restores the original values are returned. + """ + origBbox = fig.bbox + origBboxInches = fig.bbox_inches + orig_layout = fig.get_layout_engine() + fig.set_layout_engine(None) + _boxout = fig.transFigure._boxout + + old_aspect = [] + locator_list = [] + sentinel = object() + for ax in fig.axes: + locator_list.append(ax.get_axes_locator()) + current_pos = ax.get_position(original=False).frozen() + ax.set_axes_locator(lambda a, r, _pos=current_pos: _pos) + # override the method that enforces the aspect ratio on the Axes + if 'apply_aspect' in ax.__dict__: + old_aspect.append(ax.apply_aspect) + else: + old_aspect.append(sentinel) + ax.apply_aspect = lambda pos=None: None + + def restore_bbox(): + for ax, loc, aspect in zip(fig.axes, locator_list, old_aspect): + ax.set_axes_locator(loc) + if aspect is sentinel: + # delete our no-op function which un-hides the original method + del ax.apply_aspect + else: + ax.apply_aspect = aspect + + fig.bbox = origBbox + fig.bbox_inches = origBboxInches + fig.set_layout_engine(orig_layout) + fig.transFigure._boxout = _boxout + fig.transFigure.invalidate() + fig.patch.set_bounds(0, 0, 1, 1) + + if fixed_dpi is None: + fixed_dpi = fig.dpi + tr = Affine2D().scale(fixed_dpi) + dpi_scale = fixed_dpi / fig.dpi + + fig.bbox_inches = Bbox.from_bounds(0, 0, *bbox_inches.size) + x0, y0 = tr.transform(bbox_inches.p0) + w1, h1 = fig.bbox.size * dpi_scale + fig.transFigure._boxout = Bbox.from_bounds(-x0, -y0, w1, h1) + fig.transFigure.invalidate() + + fig.bbox = TransformedBbox(fig.bbox_inches, tr) + + fig.patch.set_bounds(x0 / w1, y0 / h1, + fig.bbox.width / w1, fig.bbox.height / h1) + + return restore_bbox + + +def process_figure_for_rasterizing(fig, bbox_inches_restore, fixed_dpi=None): + """ + A function that needs to be called when figure dpi changes during the + drawing (e.g., rasterizing). It recovers the bbox and re-adjust it with + the new dpi. + """ + + bbox_inches, restore_bbox = bbox_inches_restore + restore_bbox() + r = adjust_bbox(fig, bbox_inches, fixed_dpi) + + return bbox_inches, r diff --git a/lib/matplotlib/_tight_layout.py b/lib/matplotlib/_tight_layout.py new file mode 100644 index 000000000000..192c2dcfdcb9 --- /dev/null +++ b/lib/matplotlib/_tight_layout.py @@ -0,0 +1,340 @@ +""" +Routines to adjust subplot params so that subplots are +nicely fit in the figure. In doing so, only axis labels, tick labels, axes +titles and offsetboxes that are anchored to axes are currently considered. + +Internally, this module assumes that the margins (left margin, etc.) which are +differences between ``Axes.get_tightbbox`` and ``Axes.bbox`` are independent of +Axes position. This may fail if ``Axes.adjustable`` is ``datalim`` as well as +such cases as when left or right margin are affected by xlabel. +""" + +import numpy as np + +import matplotlib as mpl +from matplotlib import _api, artist as martist +from matplotlib.font_manager import FontProperties +from matplotlib.transforms import Bbox + + +def _auto_adjust_subplotpars( + fig, renderer, shape, span_pairs, subplot_list, + ax_bbox_list=None, pad=1.08, h_pad=None, w_pad=None, rect=None): + """ + Return a dict of subplot parameters to adjust spacing between subplots + or ``None`` if resulting axes would have zero height or width. + + Note that this function ignores geometry information of subplot itself, but + uses what is given by the *shape* and *subplot_list* parameters. Also, the + results could be incorrect if some subplots have ``adjustable=datalim``. + + Parameters + ---------- + shape : tuple[int, int] + Number of rows and columns of the grid. + span_pairs : list[tuple[slice, slice]] + List of rowspans and colspans occupied by each subplot. + subplot_list : list of subplots + List of subplots that will be used to calculate optimal subplot_params. + pad : float + Padding between the figure edge and the edges of subplots, as a + fraction of the font size. + h_pad, w_pad : float + Padding (height/width) between edges of adjacent subplots, as a + fraction of the font size. Defaults to *pad*. + rect : tuple + (left, bottom, right, top), default: None. + """ + rows, cols = shape + + font_size_inch = (FontProperties( + size=mpl.rcParams["font.size"]).get_size_in_points() / 72) + pad_inch = pad * font_size_inch + vpad_inch = h_pad * font_size_inch if h_pad is not None else pad_inch + hpad_inch = w_pad * font_size_inch if w_pad is not None else pad_inch + + if len(span_pairs) != len(subplot_list) or len(subplot_list) == 0: + raise ValueError + + if rect is None: + margin_left = margin_bottom = margin_right = margin_top = None + else: + margin_left, margin_bottom, _right, _top = rect + margin_right = 1 - _right if _right else None + margin_top = 1 - _top if _top else None + + vspaces = np.zeros((rows + 1, cols)) + hspaces = np.zeros((rows, cols + 1)) + + if ax_bbox_list is None: + ax_bbox_list = [ + Bbox.union([ax.get_position(original=True) for ax in subplots]) + for subplots in subplot_list] + + for subplots, ax_bbox, (rowspan, colspan) in zip( + subplot_list, ax_bbox_list, span_pairs): + if all(not ax.get_visible() for ax in subplots): + continue + + bb = [] + for ax in subplots: + if ax.get_visible(): + bb += [martist._get_tightbbox_for_layout_only(ax, renderer)] + + tight_bbox_raw = Bbox.union(bb) + tight_bbox = fig.transFigure.inverted().transform_bbox(tight_bbox_raw) + + hspaces[rowspan, colspan.start] += ax_bbox.xmin - tight_bbox.xmin # l + hspaces[rowspan, colspan.stop] += tight_bbox.xmax - ax_bbox.xmax # r + vspaces[rowspan.start, colspan] += tight_bbox.ymax - ax_bbox.ymax # t + vspaces[rowspan.stop, colspan] += ax_bbox.ymin - tight_bbox.ymin # b + + fig_width_inch, fig_height_inch = fig.get_size_inches() + + # margins can be negative for axes with aspect applied, so use max(, 0) to + # make them nonnegative. + if not margin_left: + margin_left = max(hspaces[:, 0].max(), 0) + pad_inch/fig_width_inch + suplabel = fig._supylabel + if suplabel and suplabel.get_in_layout(): + rel_width = fig.transFigure.inverted().transform_bbox( + suplabel.get_window_extent(renderer)).width + margin_left += rel_width + pad_inch/fig_width_inch + if not margin_right: + margin_right = max(hspaces[:, -1].max(), 0) + pad_inch/fig_width_inch + if not margin_top: + margin_top = max(vspaces[0, :].max(), 0) + pad_inch/fig_height_inch + if fig._suptitle and fig._suptitle.get_in_layout(): + rel_height = fig.transFigure.inverted().transform_bbox( + fig._suptitle.get_window_extent(renderer)).height + margin_top += rel_height + pad_inch/fig_height_inch + if not margin_bottom: + margin_bottom = max(vspaces[-1, :].max(), 0) + pad_inch/fig_height_inch + suplabel = fig._supxlabel + if suplabel and suplabel.get_in_layout(): + rel_height = fig.transFigure.inverted().transform_bbox( + suplabel.get_window_extent(renderer)).height + margin_bottom += rel_height + pad_inch/fig_height_inch + + if margin_left + margin_right >= 1: + _api.warn_external('Tight layout not applied. The left and right ' + 'margins cannot be made large enough to ' + 'accommodate all axes decorations.') + return None + if margin_bottom + margin_top >= 1: + _api.warn_external('Tight layout not applied. The bottom and top ' + 'margins cannot be made large enough to ' + 'accommodate all axes decorations.') + return None + + kwargs = dict(left=margin_left, + right=1 - margin_right, + bottom=margin_bottom, + top=1 - margin_top) + + if cols > 1: + hspace = hspaces[:, 1:-1].max() + hpad_inch / fig_width_inch + # axes widths: + h_axes = (1 - margin_right - margin_left - hspace * (cols - 1)) / cols + if h_axes < 0: + _api.warn_external('Tight layout not applied. tight_layout ' + 'cannot make axes width small enough to ' + 'accommodate all axes decorations') + return None + else: + kwargs["wspace"] = hspace / h_axes + if rows > 1: + vspace = vspaces[1:-1, :].max() + vpad_inch / fig_height_inch + v_axes = (1 - margin_top - margin_bottom - vspace * (rows - 1)) / rows + if v_axes < 0: + _api.warn_external('Tight layout not applied. tight_layout ' + 'cannot make axes height small enough to ' + 'accommodate all axes decorations.') + return None + else: + kwargs["hspace"] = vspace / v_axes + + return kwargs + + +@_api.deprecated("3.5") +def auto_adjust_subplotpars( + fig, renderer, nrows_ncols, num1num2_list, subplot_list, + ax_bbox_list=None, pad=1.08, h_pad=None, w_pad=None, rect=None): + """ + Return a dict of subplot parameters to adjust spacing between subplots + or ``None`` if resulting axes would have zero height or width. + + Note that this function ignores geometry information of subplot + itself, but uses what is given by the *nrows_ncols* and *num1num2_list* + parameters. Also, the results could be incorrect if some subplots have + ``adjustable=datalim``. + + Parameters + ---------- + nrows_ncols : tuple[int, int] + Number of rows and number of columns of the grid. + num1num2_list : list[tuple[int, int]] + List of numbers specifying the area occupied by the subplot + subplot_list : list of subplots + List of subplots that will be used to calculate optimal subplot_params. + pad : float + Padding between the figure edge and the edges of subplots, as a + fraction of the font size. + h_pad, w_pad : float + Padding (height/width) between edges of adjacent subplots, as a + fraction of the font size. Defaults to *pad*. + rect : tuple + (left, bottom, right, top), default: None. + """ + nrows, ncols = nrows_ncols + span_pairs = [] + for n1, n2 in num1num2_list: + if n2 is None: + n2 = n1 + span_pairs.append((slice(n1 // ncols, n2 // ncols + 1), + slice(n1 % ncols, n2 % ncols + 1))) + return _auto_adjust_subplotpars( + fig, renderer, nrows_ncols, num1num2_list, subplot_list, + ax_bbox_list, pad, h_pad, w_pad, rect) + + +def get_subplotspec_list(axes_list, grid_spec=None): + """ + Return a list of subplotspec from the given list of axes. + + For an instance of axes that does not support subplotspec, None is inserted + in the list. + + If grid_spec is given, None is inserted for those not from the given + grid_spec. + """ + subplotspec_list = [] + for ax in axes_list: + axes_or_locator = ax.get_axes_locator() + if axes_or_locator is None: + axes_or_locator = ax + + if hasattr(axes_or_locator, "get_subplotspec"): + subplotspec = axes_or_locator.get_subplotspec() + if subplotspec is not None: + subplotspec = subplotspec.get_topmost_subplotspec() + gs = subplotspec.get_gridspec() + if grid_spec is not None: + if gs != grid_spec: + subplotspec = None + elif gs.locally_modified_subplot_params(): + subplotspec = None + else: + subplotspec = None + + subplotspec_list.append(subplotspec) + + return subplotspec_list + + +def get_tight_layout_figure(fig, axes_list, subplotspec_list, renderer, + pad=1.08, h_pad=None, w_pad=None, rect=None): + """ + Return subplot parameters for tight-layouted-figure with specified padding. + + Parameters + ---------- + fig : Figure + axes_list : list of Axes + subplotspec_list : list of `.SubplotSpec` + The subplotspecs of each axes. + renderer : renderer + pad : float + Padding between the figure edge and the edges of subplots, as a + fraction of the font size. + h_pad, w_pad : float + Padding (height/width) between edges of adjacent subplots. Defaults to + *pad*. + rect : tuple (left, bottom, right, top), default: None. + rectangle in normalized figure coordinates + that the whole subplots area (including labels) will fit into. + Defaults to using the entire figure. + + Returns + ------- + subplotspec or None + subplotspec kwargs to be passed to `.Figure.subplots_adjust` or + None if tight_layout could not be accomplished. + """ + + # Multiple axes can share same subplotspec (e.g., if using axes_grid1); + # we need to group them together. + ss_to_subplots = {ss: [] for ss in subplotspec_list} + for ax, ss in zip(axes_list, subplotspec_list): + ss_to_subplots[ss].append(ax) + ss_to_subplots.pop(None, None) # Skip subplotspec == None. + if not ss_to_subplots: + return {} + subplot_list = list(ss_to_subplots.values()) + ax_bbox_list = [ss.get_position(fig) for ss in ss_to_subplots] + + max_nrows = max(ss.get_gridspec().nrows for ss in ss_to_subplots) + max_ncols = max(ss.get_gridspec().ncols for ss in ss_to_subplots) + + span_pairs = [] + for ss in ss_to_subplots: + # The intent here is to support axes from different gridspecs where + # one's nrows (or ncols) is a multiple of the other (e.g. 2 and 4), + # but this doesn't actually work because the computed wspace, in + # relative-axes-height, corresponds to different physical spacings for + # the 2-row grid and the 4-row grid. Still, this code is left, mostly + # for backcompat. + rows, cols = ss.get_gridspec().get_geometry() + div_row, mod_row = divmod(max_nrows, rows) + div_col, mod_col = divmod(max_ncols, cols) + if mod_row != 0: + _api.warn_external('tight_layout not applied: number of rows ' + 'in subplot specifications must be ' + 'multiples of one another.') + return {} + if mod_col != 0: + _api.warn_external('tight_layout not applied: number of ' + 'columns in subplot specifications must be ' + 'multiples of one another.') + return {} + span_pairs.append(( + slice(ss.rowspan.start * div_row, ss.rowspan.stop * div_row), + slice(ss.colspan.start * div_col, ss.colspan.stop * div_col))) + + kwargs = _auto_adjust_subplotpars(fig, renderer, + shape=(max_nrows, max_ncols), + span_pairs=span_pairs, + subplot_list=subplot_list, + ax_bbox_list=ax_bbox_list, + pad=pad, h_pad=h_pad, w_pad=w_pad) + + # kwargs can be none if tight_layout fails... + if rect is not None and kwargs is not None: + # if rect is given, the whole subplots area (including + # labels) will fit into the rect instead of the + # figure. Note that the rect argument of + # *auto_adjust_subplotpars* specify the area that will be + # covered by the total area of axes.bbox. Thus we call + # auto_adjust_subplotpars twice, where the second run + # with adjusted rect parameters. + + left, bottom, right, top = rect + if left is not None: + left += kwargs["left"] + if bottom is not None: + bottom += kwargs["bottom"] + if right is not None: + right -= (1 - kwargs["right"]) + if top is not None: + top -= (1 - kwargs["top"]) + + kwargs = _auto_adjust_subplotpars(fig, renderer, + shape=(max_nrows, max_ncols), + span_pairs=span_pairs, + subplot_list=subplot_list, + ax_bbox_list=ax_bbox_list, + pad=pad, h_pad=h_pad, w_pad=w_pad, + rect=(left, bottom, right, top)) + + return kwargs diff --git a/lib/matplotlib/_type1font.py b/lib/matplotlib/_type1font.py new file mode 100644 index 000000000000..0413cb0016a0 --- /dev/null +++ b/lib/matplotlib/_type1font.py @@ -0,0 +1,877 @@ +""" +A class representing a Type 1 font. + +This version reads pfa and pfb files and splits them for embedding in +pdf files. It also supports SlantFont and ExtendFont transformations, +similarly to pdfTeX and friends. There is no support yet for subsetting. + +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}) + +Sources: + +* Adobe Technical Note #5040, Supporting Downloadable PostScript + Language Fonts. + +* Adobe Type 1 Font Format, Adobe Systems Incorporated, third printing, + v1.1, 1993. ISBN 0-201-57044-0. +""" + +import binascii +import functools +import logging +import re +import string +import struct + +import numpy as np + +from matplotlib.cbook import _format_approx +from . import _api + +_log = logging.getLogger(__name__) + + +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: + """ + A class representing a Type-1 font, for use by backends. + + Attributes + ---------- + 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]``. + + prop : dict[str, Any] + 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', '_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): + """ + Initialize a Type-1 font. + + Parameters + ---------- + input : str or 3-tuple + Either a pfb file name, or a 3-tuple of already-decoded Type-1 + font `~.Type1Font.parts`. + """ + if isinstance(input, tuple) and len(input) == 3: + self.parts = input + else: + with open(input, 'rb') as file: + data = self._read(file) + 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): + """Read the font from a file, decoding into usable parts.""" + rawdata = file.read() + if not rawdata.startswith(b'\x80'): + return rawdata + + data = b'' + while rawdata: + if not rawdata.startswith(b'\x80'): + raise RuntimeError('Broken pfb file (expected byte 128, ' + 'got %d)' % rawdata[0]) + type = rawdata[1] + if type in (1, 2): + length, = struct.unpack('> 8)) + key = ((key+byte) * 52845 + 22719) & 0xffff + + return bytes(plaintext[ndiscard:]) + + @staticmethod + def _encrypt(plaintext, key, ndiscard=4): + """ + 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 + 'eexec' and 'charstring', which map to the key specified for the + corresponding part of Type-1 fonts. + + The ndiscard argument should be an integer, usually 4. That + number of bytes is prepended to the plaintext before encryption. + This function prepends NUL bytes for reproducibility, even though + the original algorithm uses random bytes, presumably to avoid + cryptanalysis. + """ + + key = _api.check_getitem({'eexec': 55665, 'charstring': 4330}, key=key) + ciphertext = [] + for byte in b'\0' * ndiscard + plaintext: + c = byte ^ (key >> 8) + ciphertext.append(c) + key = ((key + c) * 52845 + 22719) & 0xffff + + return bytes(ciphertext) + + 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. + """ + # Start with reasonable defaults + prop = {'Weight': 'Regular', 'ItalicAngle': 0.0, 'isFixedPitch': False, + 'UnderlinePosition': -100, 'UnderlineThickness': 50} + 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: + prop['FontName'] = (prop.get('FullName') or + prop.get('FamilyName') or + 'Unknown') + if 'FullName' not in prop: + prop['FullName'] = prop['FontName'] + if 'FamilyName' not in prop: + 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 + + 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() + + @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): + """ + Return a new font that is slanted and/or extended. + + Parameters + ---------- + effects : dict + A dict with optional entries: + + - 'slant' : float, default: 0 + Tangent of the angle that the font is to be slanted to the + right. Negative values slant to the left. + - 'extend' : float, default: 1 + Scaling factor for the font width. Values less than 1 condense + the glyphs. + + Returns + ------- + `Type1Font` + """ + 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/matplotlib/_version.py b/lib/matplotlib/_version.py deleted file mode 100644 index 08dc9b6117f2..000000000000 --- a/lib/matplotlib/_version.py +++ /dev/null @@ -1,460 +0,0 @@ - -# This file helps to compute a version number in source trees obtained from -# git-archive tarball (such as those provided by githubs download-from-tag -# feature). Distribution tarballs (built by setup.py sdist) and build -# directories (produced by setup.py build) will contain a much shorter file -# that just contains the computed version number. - -# This file is released into the public domain. Generated by -# versioneer-0.15 (https://github.com/warner/python-versioneer) - -import errno -import os -import re -import subprocess -import sys - - -def get_keywords(): - # these strings will be replaced by git during git-archive. - # setup.py/versioneer.py will grep for the variable names, so they must - # each be defined on a line of their own. _version.py will just call - # get_keywords(). - git_refnames = "$Format:%d$" - git_full = "$Format:%H$" - keywords = {"refnames": git_refnames, "full": git_full} - return keywords - - -class VersioneerConfig: - pass - - -def get_config(): - # these strings are filled in when 'setup.py versioneer' creates - # _version.py - cfg = VersioneerConfig() - cfg.VCS = "git" - cfg.style = "pep440-post" - cfg.tag_prefix = "v" - cfg.parentdir_prefix = "matplotlib-" - cfg.versionfile_source = "lib/matplotlib/_version.py" - cfg.verbose = False - return cfg - - -class NotThisMethod(Exception): - pass - - -LONG_VERSION_PY = {} -HANDLERS = {} - - -def register_vcs_handler(vcs, method): # decorator - def decorate(f): - if vcs not in HANDLERS: - HANDLERS[vcs] = {} - HANDLERS[vcs][method] = f - return f - return decorate - - -def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False): - assert isinstance(commands, list) - p = None - for c in commands: - try: - dispcmd = str([c] + args) - # remember shell=False, so use git.cmd on windows, not just git - p = subprocess.Popen([c] + args, cwd=cwd, stdout=subprocess.PIPE, - stderr=(subprocess.PIPE if hide_stderr - else None)) - break - except EnvironmentError: - e = sys.exc_info()[1] - if e.errno == errno.ENOENT: - continue - if verbose: - print("unable to run %s" % dispcmd) - print(e) - return None - else: - if verbose: - print("unable to find command, tried %s" % (commands,)) - return None - stdout = p.communicate()[0].strip() - if sys.version_info[0] >= 3: - stdout = stdout.decode() - if p.returncode != 0: - if verbose: - print("unable to run %s (error)" % dispcmd) - return None - return stdout - - -def versions_from_parentdir(parentdir_prefix, root, verbose): - # Source tarballs conventionally unpack into a directory that includes - # both the project name and a version string. - dirname = os.path.basename(root) - if not dirname.startswith(parentdir_prefix): - if verbose: - print("guessing rootdir is '%s', but '%s' doesn't start with " - "prefix '%s'" % (root, dirname, parentdir_prefix)) - raise NotThisMethod("rootdir doesn't start with parentdir_prefix") - return {"version": dirname[len(parentdir_prefix):], - "full-revisionid": None, - "dirty": False, "error": None} - - -@register_vcs_handler("git", "get_keywords") -def git_get_keywords(versionfile_abs): - # the code embedded in _version.py can just fetch the value of these - # keywords. When used from setup.py, we don't want to import _version.py, - # so we do it with a regexp instead. This function is not used from - # _version.py. - keywords = {} - try: - f = open(versionfile_abs, "r") - for line in f.readlines(): - if line.strip().startswith("git_refnames ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["refnames"] = mo.group(1) - if line.strip().startswith("git_full ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["full"] = mo.group(1) - f.close() - except EnvironmentError: - pass - return keywords - - -@register_vcs_handler("git", "keywords") -def git_versions_from_keywords(keywords, tag_prefix, verbose): - if not keywords: - raise NotThisMethod("no keywords at all, weird") - refnames = keywords["refnames"].strip() - if refnames.startswith("$Format"): - if verbose: - print("keywords are unexpanded, not using") - raise NotThisMethod("unexpanded keywords, not a git-archive tarball") - refs = set([r.strip() for r in refnames.strip("()").split(",")]) - # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of - # just "foo-1.0". If we see a "tag: " prefix, prefer those. - TAG = "tag: " - tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) - if not tags: - # Either we're using git < 1.8.3, or there really are no tags. We use - # a heuristic: assume all version tags have a digit. The old git %d - # expansion behaves like git log --decorate=short and strips out the - # refs/heads/ and refs/tags/ prefixes that would let us distinguish - # between branches and tags. By ignoring refnames without digits, we - # filter out many common branch names like "release" and - # "stabilization", as well as "HEAD" and "master". - tags = set([r for r in refs if re.search(r'\d', r)]) - if verbose: - print("discarding '%s', no digits" % ",".join(refs-tags)) - if verbose: - print("likely tags: %s" % ",".join(sorted(tags))) - for ref in sorted(tags): - # sorting will prefer e.g. "2.0" over "2.0rc1" - if ref.startswith(tag_prefix): - r = ref[len(tag_prefix):] - if verbose: - print("picking %s" % r) - return {"version": r, - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": None - } - # no suitable tags, so version is "0+unknown", but full hex is still there - if verbose: - print("no suitable tags, using unknown + full revision id") - return {"version": "0+unknown", - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": "no suitable tags"} - - -@register_vcs_handler("git", "pieces_from_vcs") -def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): - # this runs 'git' from the root of the source tree. This only gets called - # if the git-archive 'subst' keywords were *not* expanded, and - # _version.py hasn't already been rewritten with a short version string, - # meaning we're inside a checked out source tree. - - if not os.path.exists(os.path.join(root, ".git")): - if verbose: - print("no .git in %s" % root) - raise NotThisMethod("no .git directory") - - GITS = ["git"] - if sys.platform == "win32": - GITS = ["git.cmd", "git.exe"] - # if there is a tag, this yields TAG-NUM-gHEX[-dirty] - # if there are no tags, this yields HEX[-dirty] (no NUM) - describe_out = run_command(GITS, ["describe", "--tags", "--dirty", - "--always", "--long"], - cwd=root) - # --long was added in git-1.5.5 - if describe_out is None: - raise NotThisMethod("'git describe' failed") - describe_out = describe_out.strip() - full_out = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) - if full_out is None: - raise NotThisMethod("'git rev-parse' failed") - full_out = full_out.strip() - - pieces = {} - pieces["long"] = full_out - pieces["short"] = full_out[:7] # maybe improved later - pieces["error"] = None - - # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] - # TAG might have hyphens. - git_describe = describe_out - - # look for -dirty suffix - dirty = git_describe.endswith("-dirty") - pieces["dirty"] = dirty - if dirty: - git_describe = git_describe[:git_describe.rindex("-dirty")] - - # now we have TAG-NUM-gHEX or HEX - - if "-" in git_describe: - # TAG-NUM-gHEX - mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) - if not mo: - # unparsable. Maybe git-describe is misbehaving? - pieces["error"] = ("unable to parse git-describe output: '%s'" - % describe_out) - return pieces - - # tag - full_tag = mo.group(1) - if not full_tag.startswith(tag_prefix): - if verbose: - fmt = "tag '%s' doesn't start with prefix '%s'" - print(fmt % (full_tag, tag_prefix)) - pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" - % (full_tag, tag_prefix)) - return pieces - pieces["closest-tag"] = full_tag[len(tag_prefix):] - - # distance: number of commits since tag - pieces["distance"] = int(mo.group(2)) - - # commit: short hex revision ID - pieces["short"] = mo.group(3) - - else: - # HEX: no tags - pieces["closest-tag"] = None - count_out = run_command(GITS, ["rev-list", "HEAD", "--count"], - cwd=root) - pieces["distance"] = int(count_out) # total number of commits - - return pieces - - -def plus_or_dot(pieces): - if "+" in pieces.get("closest-tag", ""): - return "." - return "+" - - -def render_pep440(pieces): - # now build up version string, with post-release "local version - # identifier". Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you - # get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty - - # exceptions: - # 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] - - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += plus_or_dot(pieces) - rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = "0+untagged.%d.g%s" % (pieces["distance"], - pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def render_pep440_pre(pieces): - # TAG[.post.devDISTANCE] . No -dirty - - # exceptions: - # 1: no tags. 0.post.devDISTANCE - - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"]: - rendered += ".post.dev%d" % pieces["distance"] - else: - # exception #1 - rendered = "0.post.dev%d" % pieces["distance"] - return rendered - - -def render_pep440_post(pieces): - # TAG[.postDISTANCE[.dev0]+gHEX] . The ".dev0" means dirty. Note that - # .dev0 sorts backwards (a dirty tree will appear "older" than the - # corresponding clean one), but you shouldn't be releasing software with - # -dirty anyways. - - # exceptions: - # 1: no tags. 0.postDISTANCE[.dev0] - - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += "g%s" % pieces["short"] - else: - # exception #1 - rendered = "0.post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += "+g%s" % pieces["short"] - return rendered - - -def render_pep440_old(pieces): - # TAG[.postDISTANCE[.dev0]] . The ".dev0" means dirty. - - # exceptions: - # 1: no tags. 0.postDISTANCE[.dev0] - - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - else: - # exception #1 - rendered = "0.post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - return rendered - - -def render_git_describe(pieces): - # TAG[-DISTANCE-gHEX][-dirty], like 'git describe --tags --dirty - # --always' - - # exceptions: - # 1: no tags. HEX[-dirty] (note: no 'g' prefix) - - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"]: - rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render_git_describe_long(pieces): - # TAG-DISTANCE-gHEX[-dirty], like 'git describe --tags --dirty - # --always -long'. The distance/hash is unconditional. - - # exceptions: - # 1: no tags. HEX[-dirty] (note: no 'g' prefix) - - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render(pieces, style): - if pieces["error"]: - return {"version": "unknown", - "full-revisionid": pieces.get("long"), - "dirty": None, - "error": pieces["error"]} - - if not style or style == "default": - style = "pep440" # the default - - if style == "pep440": - rendered = render_pep440(pieces) - elif style == "pep440-pre": - rendered = render_pep440_pre(pieces) - elif style == "pep440-post": - rendered = render_pep440_post(pieces) - elif style == "pep440-old": - rendered = render_pep440_old(pieces) - elif style == "git-describe": - rendered = render_git_describe(pieces) - elif style == "git-describe-long": - rendered = render_git_describe_long(pieces) - else: - raise ValueError("unknown style '%s'" % style) - - return {"version": rendered, "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], "error": None} - - -def get_versions(): - # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have - # __file__, we can work backwards from there to the root. Some - # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which - # case we can only use expanded keywords. - - cfg = get_config() - verbose = cfg.verbose - - try: - return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, - verbose) - except NotThisMethod: - pass - - try: - root = os.path.realpath(__file__) - # versionfile_source is the relative path from the top of the source - # tree (where the .git directory might live) to this file. Invert - # this to find the root from __file__. - for _ in cfg.versionfile_source.split('/'): - root = os.path.dirname(root) - except NameError: - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to find root of source tree"} - - try: - pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) - return render(pieces, cfg.style) - except NotThisMethod: - pass - - try: - if cfg.parentdir_prefix: - return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) - except NotThisMethod: - pass - - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to compute version"} diff --git a/lib/matplotlib/afm.py b/lib/matplotlib/afm.py index 0106664f5ccc..d95c88a0e2b4 100644 --- a/lib/matplotlib/afm.py +++ b/lib/matplotlib/afm.py @@ -1,532 +1,3 @@ -""" -A python interface to Adobe Font Metrics Files. - -Although a number of other python implementations exist, and may be more -complete than this, it was decided not to go with them because they were -either: - -1) copyrighted or used a non-BSD compatible license -2) had too many dependencies and a free standing lib was needed -3) did more than needed and it was easier to write afresh rather than - figure out how to get just what was needed. - -It is pretty easy to use, and has no external dependencies: - ->>> import matplotlib as mpl ->>> from pathlib import Path ->>> afm_path = Path(mpl.get_data_path(), 'fonts', 'afm', 'ptmr8a.afm') ->>> ->>> from matplotlib.afm import AFM ->>> with afm_path.open('rb') as fh: -... afm = AFM(fh) ->>> afm.string_width_height('What the heck?') -(6220.0, 694) ->>> afm.get_fontname() -'Times-Roman' ->>> afm.get_kern_dist('A', 'f') -0 ->>> afm.get_kern_dist('A', 'y') --92.0 ->>> afm.get_bbox_char('!') -[130, -9, 238, 676] - -As in the Adobe Font Metrics File Format Specification, all dimensions -are given in units of 1/1000 of the scale factor (point size) of the font -being used. -""" - -from collections import namedtuple -import logging -import re - -from ._mathtext_data import uni2type1 - - -_log = logging.getLogger(__name__) - - -def _to_int(x): - # Some AFM files have floats where we are expecting ints -- there is - # probably a better way to handle this (support floats, round rather than - # truncate). But I don't know what the best approach is now and this - # change to _to_int should at least prevent Matplotlib from crashing on - # these. JDH (2009-11-06) - return int(float(x)) - - -def _to_float(x): - # Some AFM files use "," instead of "." as decimal separator -- this - # shouldn't be ambiguous (unless someone is wicked enough to use "," as - # thousands separator...). - if isinstance(x, bytes): - # Encoding doesn't really matter -- if we have codepoints >127 the call - # to float() will error anyways. - x = x.decode('latin-1') - return float(x.replace(',', '.')) - - -def _to_str(x): - return x.decode('utf8') - - -def _to_list_of_ints(s): - s = s.replace(b',', b' ') - return [_to_int(val) for val in s.split()] - - -def _to_list_of_floats(s): - return [_to_float(val) for val in s.split()] - - -def _to_bool(s): - if s.lower().strip() in (b'false', b'0', b'no'): - return False - else: - return True - - -def _parse_header(fh): - """ - Read the font metrics header (up to the char metrics) and returns - a dictionary mapping *key* to *val*. *val* will be converted to the - appropriate python type as necessary; e.g.: - - * 'False'->False - * '0'->0 - * '-168 -218 1000 898'-> [-168, -218, 1000, 898] - - Dictionary keys are - - StartFontMetrics, FontName, FullName, FamilyName, Weight, - ItalicAngle, IsFixedPitch, FontBBox, UnderlinePosition, - UnderlineThickness, Version, Notice, EncodingScheme, CapHeight, - XHeight, Ascender, Descender, StartCharMetrics - """ - header_converters = { - b'StartFontMetrics': _to_float, - b'FontName': _to_str, - b'FullName': _to_str, - b'FamilyName': _to_str, - b'Weight': _to_str, - b'ItalicAngle': _to_float, - b'IsFixedPitch': _to_bool, - b'FontBBox': _to_list_of_ints, - b'UnderlinePosition': _to_float, - b'UnderlineThickness': _to_float, - b'Version': _to_str, - # Some AFM files have non-ASCII characters (which are not allowed by - # the spec). Given that there is actually no public API to even access - # this field, just return it as straight bytes. - b'Notice': lambda x: x, - b'EncodingScheme': _to_str, - b'CapHeight': _to_float, # Is the second version a mistake, or - b'Capheight': _to_float, # do some AFM files contain 'Capheight'? -JKS - b'XHeight': _to_float, - b'Ascender': _to_float, - b'Descender': _to_float, - b'StdHW': _to_float, - b'StdVW': _to_float, - b'StartCharMetrics': _to_int, - b'CharacterSet': _to_str, - b'Characters': _to_int, - } - d = {} - first_line = True - for line in fh: - line = line.rstrip() - if line.startswith(b'Comment'): - continue - lst = line.split(b' ', 1) - key = lst[0] - if first_line: - # AFM spec, Section 4: The StartFontMetrics keyword - # [followed by a version number] must be the first line in - # the file, and the EndFontMetrics keyword must be the - # last non-empty line in the file. We just check the - # first header entry. - if key != b'StartFontMetrics': - raise RuntimeError('Not an AFM file') - first_line = False - if len(lst) == 2: - val = lst[1] - else: - val = b'' - try: - converter = header_converters[key] - except KeyError: - _log.error('Found an unknown keyword in AFM header (was %r)' % key) - continue - try: - d[key] = converter(val) - except ValueError: - _log.error('Value error parsing header in AFM: %s, %s', key, val) - continue - if key == b'StartCharMetrics': - break - else: - raise RuntimeError('Bad parse') - return d - - -CharMetrics = namedtuple('CharMetrics', 'width, name, bbox') -CharMetrics.__doc__ = """ - Represents the character metrics of a single character. - - Notes - ----- - The fields do currently only describe a subset of character metrics - information defined in the AFM standard. - """ -CharMetrics.width.__doc__ = """The character width (WX).""" -CharMetrics.name.__doc__ = """The character name (N).""" -CharMetrics.bbox.__doc__ = """ - The bbox of the character (B) as a tuple (*llx*, *lly*, *urx*, *ury*).""" - - -def _parse_char_metrics(fh): - """ - Parse the given filehandle for character metrics information and return - the information as dicts. - - It is assumed that the file cursor is on the line behind - 'StartCharMetrics'. - - Returns - ------- - ascii_d : dict - A mapping "ASCII num of the character" to `.CharMetrics`. - name_d : dict - A mapping "character name" to `.CharMetrics`. - - Notes - ----- - This function is incomplete per the standard, but thus far parses - all the sample afm files tried. - """ - required_keys = {'C', 'WX', 'N', 'B'} - - ascii_d = {} - name_d = {} - for line in fh: - # We are defensively letting values be utf8. The spec requires - # ascii, but there are non-compliant fonts in circulation - line = _to_str(line.rstrip()) # Convert from byte-literal - if line.startswith('EndCharMetrics'): - return ascii_d, name_d - # Split the metric line into a dictionary, keyed by metric identifiers - vals = dict(s.strip().split(' ', 1) for s in line.split(';') if s) - # There may be other metrics present, but only these are needed - if not required_keys.issubset(vals): - raise RuntimeError('Bad char metrics line: %s' % line) - num = _to_int(vals['C']) - wx = _to_float(vals['WX']) - name = vals['N'] - bbox = _to_list_of_floats(vals['B']) - bbox = list(map(int, bbox)) - metrics = CharMetrics(wx, name, bbox) - # Workaround: If the character name is 'Euro', give it the - # corresponding character code, according to WinAnsiEncoding (see PDF - # Reference). - if name == 'Euro': - num = 128 - elif name == 'minus': - num = ord("\N{MINUS SIGN}") # 0x2212 - if num != -1: - ascii_d[num] = metrics - name_d[name] = metrics - raise RuntimeError('Bad parse') - - -def _parse_kern_pairs(fh): - """ - Return a kern pairs dictionary; keys are (*char1*, *char2*) tuples and - values are the kern pair value. For example, a kern pairs line like - ``KPX A y -50`` - - will be represented as:: - - d[ ('A', 'y') ] = -50 - - """ - - line = next(fh) - if not line.startswith(b'StartKernPairs'): - raise RuntimeError('Bad start of kern pairs data: %s' % line) - - d = {} - for line in fh: - line = line.rstrip() - if not line: - continue - if line.startswith(b'EndKernPairs'): - next(fh) # EndKernData - return d - vals = line.split() - if len(vals) != 4 or vals[0] != b'KPX': - raise RuntimeError('Bad kern pairs line: %s' % line) - c1, c2, val = _to_str(vals[1]), _to_str(vals[2]), _to_float(vals[3]) - d[(c1, c2)] = val - raise RuntimeError('Bad kern pairs parse') - - -CompositePart = namedtuple('CompositePart', 'name, dx, dy') -CompositePart.__doc__ = """ - Represents the information on a composite element of a composite char.""" -CompositePart.name.__doc__ = """Name of the part, e.g. 'acute'.""" -CompositePart.dx.__doc__ = """x-displacement of the part from the origin.""" -CompositePart.dy.__doc__ = """y-displacement of the part from the origin.""" - - -def _parse_composites(fh): - """ - Parse the given filehandle for composites information return them as a - dict. - - It is assumed that the file cursor is on the line behind 'StartComposites'. - - Returns - ------- - dict - A dict mapping composite character names to a parts list. The parts - list is a list of `.CompositePart` entries describing the parts of - the composite. - - Examples - -------- - A composite definition line:: - - CC Aacute 2 ; PCC A 0 0 ; PCC acute 160 170 ; - - will be represented as:: - - composites['Aacute'] = [CompositePart(name='A', dx=0, dy=0), - CompositePart(name='acute', dx=160, dy=170)] - - """ - composites = {} - for line in fh: - line = line.rstrip() - if not line: - continue - if line.startswith(b'EndComposites'): - return composites - vals = line.split(b';') - cc = vals[0].split() - name, numParts = cc[1], _to_int(cc[2]) - pccParts = [] - for s in vals[1:-1]: - pcc = s.split() - part = CompositePart(pcc[1], _to_float(pcc[2]), _to_float(pcc[3])) - pccParts.append(part) - composites[name] = pccParts - - raise RuntimeError('Bad composites parse') - - -def _parse_optional(fh): - """ - Parse the optional fields for kern pair data and composites. - - Returns - ------- - kern_data : dict - A dict containing kerning information. May be empty. - See `._parse_kern_pairs`. - composites : dict - A dict containing composite information. May be empty. - See `._parse_composites`. - """ - optional = { - b'StartKernData': _parse_kern_pairs, - b'StartComposites': _parse_composites, - } - - d = {b'StartKernData': {}, - b'StartComposites': {}} - for line in fh: - line = line.rstrip() - if not line: - continue - key = line.split()[0] - - if key in optional: - d[key] = optional[key](fh) - - return d[b'StartKernData'], d[b'StartComposites'] - - -class AFM: - - def __init__(self, fh): - """Parse the AFM file in file object *fh*.""" - self._header = _parse_header(fh) - self._metrics, self._metrics_by_name = _parse_char_metrics(fh) - self._kern, self._composite = _parse_optional(fh) - - def get_bbox_char(self, c, isord=False): - if not isord: - c = ord(c) - return self._metrics[c].bbox - - def string_width_height(self, s): - """ - Return the string width (including kerning) and string height - as a (*w*, *h*) tuple. - """ - if not len(s): - return 0, 0 - total_width = 0 - namelast = None - miny = 1e9 - maxy = 0 - for c in s: - if c == '\n': - continue - wx, name, bbox = self._metrics[ord(c)] - - total_width += wx + self._kern.get((namelast, name), 0) - l, b, w, h = bbox - miny = min(miny, b) - maxy = max(maxy, b + h) - - namelast = name - - return total_width, maxy - miny - - def get_str_bbox_and_descent(self, s): - """Return the string bounding box and the maximal descent.""" - if not len(s): - return 0, 0, 0, 0, 0 - total_width = 0 - namelast = None - miny = 1e9 - maxy = 0 - left = 0 - if not isinstance(s, str): - s = _to_str(s) - for c in s: - if c == '\n': - continue - name = uni2type1.get(ord(c), f"uni{ord(c):04X}") - try: - wx, _, bbox = self._metrics_by_name[name] - except KeyError: - name = 'question' - wx, _, bbox = self._metrics_by_name[name] - total_width += wx + self._kern.get((namelast, name), 0) - l, b, w, h = bbox - left = min(left, l) - miny = min(miny, b) - maxy = max(maxy, b + h) - - namelast = name - - return left, miny, total_width, maxy - miny, -miny - - def get_str_bbox(self, s): - """Return the string bounding box.""" - return self.get_str_bbox_and_descent(s)[:4] - - def get_name_char(self, c, isord=False): - """Get the name of the character, i.e., ';' is 'semicolon'.""" - if not isord: - c = ord(c) - return self._metrics[c].name - - def get_width_char(self, c, isord=False): - """ - Get the width of the character from the character metric WX field. - """ - if not isord: - c = ord(c) - return self._metrics[c].width - - def get_width_from_char_name(self, name): - """Get the width of the character from a type1 character name.""" - return self._metrics_by_name[name].width - - def get_height_char(self, c, isord=False): - """Get the bounding box (ink) height of character *c* (space is 0).""" - if not isord: - c = ord(c) - return self._metrics[c].bbox[-1] - - def get_kern_dist(self, c1, c2): - """ - Return the kerning pair distance (possibly 0) for chars *c1* and *c2*. - """ - name1, name2 = self.get_name_char(c1), self.get_name_char(c2) - return self.get_kern_dist_from_name(name1, name2) - - def get_kern_dist_from_name(self, name1, name2): - """ - Return the kerning pair distance (possibly 0) for chars - *name1* and *name2*. - """ - return self._kern.get((name1, name2), 0) - - def get_fontname(self): - """Return the font name, e.g., 'Times-Roman'.""" - return self._header[b'FontName'] - - @property - def postscript_name(self): # For consistency with FT2Font. - return self.get_fontname() - - def get_fullname(self): - """Return the font full name, e.g., 'Times-Roman'.""" - name = self._header.get(b'FullName') - if name is None: # use FontName as a substitute - name = self._header[b'FontName'] - return name - - def get_familyname(self): - """Return the font family name, e.g., 'Times'.""" - name = self._header.get(b'FamilyName') - if name is not None: - return name - - # FamilyName not specified so we'll make a guess - name = self.get_fullname() - extras = (r'(?i)([ -](regular|plain|italic|oblique|bold|semibold|' - r'light|ultralight|extra|condensed))+$') - return re.sub(extras, '', name) - - @property - def family_name(self): - """The font family name, e.g., 'Times'.""" - return self.get_familyname() - - def get_weight(self): - """Return the font weight, e.g., 'Bold' or 'Roman'.""" - return self._header[b'Weight'] - - def get_angle(self): - """Return the fontangle as float.""" - return self._header[b'ItalicAngle'] - - def get_capheight(self): - """Return the cap height as float.""" - return self._header[b'CapHeight'] - - def get_xheight(self): - """Return the xheight as float.""" - return self._header[b'XHeight'] - - def get_underline_thickness(self): - """Return the underline thickness as float.""" - return self._header[b'UnderlineThickness'] - - def get_horizontal_stem_width(self): - """ - Return the standard horizontal stem width as float, or *None* if - not specified in AFM file. - """ - return self._header.get(b'StdHW', None) - - def get_vertical_stem_width(self): - """ - Return the standard vertical stem width as float, or *None* if - not specified in AFM file. - """ - return self._header.get(b'StdVW', None) +from matplotlib._afm import * # noqa: F401, F403 +from matplotlib import _api +_api.warn_deprecated("3.6", name=__name__, obj_type="module") diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py index af9b9b82b3e0..6f6aede7ecbc 100644 --- a/lib/matplotlib/animation.py +++ b/lib/matplotlib/animation.py @@ -1,7 +1,8 @@ # TODO: # * Documentation -- this will need a new section of the User's Guide. # Both for Animations and just timers. -# - Also need to update http://www.scipy.org/Cookbook/Matplotlib/Animations +# - Also need to update +# https://scipy-cookbook.readthedocs.io/items/Matplotlib_Animations.html # * Blit # * Currently broken with Qt4 for widgets that don't start on screen # * Still a few edge cases that aren't working correctly @@ -16,6 +17,7 @@ # * Can blit be enabled for movies? # * Need to consider event sources to allow clicking through multiple figures + import abc import base64 import contextlib @@ -42,21 +44,16 @@ _log = logging.getLogger(__name__) # Process creation flag for subprocess to prevent it raising a terminal -# window. See for example: -# https://stackoverflow.com/questions/24130623/using-python-subprocess-popen-cant-prevent-exe-stopped-working-prompt -if sys.platform == 'win32': - subprocess_creation_flags = CREATE_NO_WINDOW = 0x08000000 -else: - # Apparently None won't work here - subprocess_creation_flags = 0 +# window. See for example https://stackoverflow.com/q/24130623/ +subprocess_creation_flags = ( + subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0) # Other potential writing methods: # * http://pymedia.org/ # * libming (produces swf) python wrappers: https://github.com/libming/libming # * Wrap x264 API: -# (http://stackoverflow.com/questions/2940671/ -# how-to-encode-series-of-images-into-h264-using-x264-api-c-c ) +# (https://stackoverflow.com/q/2940671/) def adjusted_figsize(w, h, dpi, n): @@ -156,19 +153,17 @@ def __getitem__(self, name): class AbstractMovieWriter(abc.ABC): """ - Abstract base class for writing movies. Fundamentally, what a MovieWriter - does is provide is a way to grab frames by calling grab_frame(). - - setup() is called to start the process and finish() is called afterwards. + Abstract base class for writing movies, providing a way to grab frames by + calling `~AbstractMovieWriter.grab_frame`. - This class is set up to provide for writing movie frame data to a pipe. - saving() is provided as a context manager to facilitate this process as:: + `setup` is called to start the process and `finish` is called afterwards. + `saving` is provided as a context manager to facilitate this process as :: with moviewriter.saving(fig, outfile='myfile.mp4', dpi=100): # Iterate over frames moviewriter.grab_frame(**savefig_kwargs) - The use of the context manager ensures that setup() and finish() are + The use of the context manager ensures that `setup` and `finish` are performed as necessary. An instance of a concrete subclass of this class can be given as the @@ -261,9 +256,6 @@ class MovieWriter(AbstractMovieWriter): # stored. Third-party writers cannot meaningfully set these as they cannot # extend rcParams with new keys. - exec_key = _api.deprecate_privatize_attribute("3.3") - args_key = _api.deprecate_privatize_attribute("3.3") - # Pipe-based writers only support RGBA, but file-based ones support more # formats. supported_formats = ["rgba"] @@ -339,29 +331,6 @@ def _run(self): def finish(self): """Finish any processing for writing the movie.""" - overridden_cleanup = _api.deprecate_method_override( - __class__.cleanup, self, since="3.4", alternative="finish()") - if overridden_cleanup is not None: - overridden_cleanup() - else: - self._cleanup() # Inline _cleanup() once cleanup() is removed. - - def grab_frame(self, **savefig_kwargs): - # docstring inherited - _log.debug('MovieWriter.grab_frame: Grabbing frame.') - # Readjust the figure size in case it has been changed by the user. - # All frames must have the same size to save the movie correctly. - self.fig.set_size_inches(self._w, self._h) - # Save the figure data to the sink, using the frame format and dpi. - self.fig.savefig(self._proc.stdin, format=self.frame_format, - dpi=self.dpi, **savefig_kwargs) - - def _args(self): - """Assemble list of encoder-specific command-line arguments.""" - return NotImplementedError("args needs to be implemented by subclass.") - - def _cleanup(self): # Inline to finish() once cleanup() is removed. - """Clean-up and collect the process used to write the movie file.""" out, err = self._proc.communicate() # Use the encoding/errors that universal_newlines would use. out = TextIOWrapper(BytesIO(out)).read() @@ -378,9 +347,19 @@ def _cleanup(self): # Inline to finish() once cleanup() is removed. raise subprocess.CalledProcessError( self._proc.returncode, self._proc.args, out, err) - @_api.deprecated("3.4") - def cleanup(self): - self._cleanup() + def grab_frame(self, **savefig_kwargs): + # docstring inherited + _log.debug('MovieWriter.grab_frame: Grabbing frame.') + # Readjust the figure size in case it has been changed by the user. + # All frames must have the same size to save the movie correctly. + self.fig.set_size_inches(self._w, self._h) + # Save the figure data to the sink, using the frame format and dpi. + self.fig.savefig(self._proc.stdin, format=self.frame_format, + dpi=self.dpi, **savefig_kwargs) + + def _args(self): + """Assemble list of encoder-specific command-line arguments.""" + return NotImplementedError("args needs to be implemented by subclass.") @classmethod def bin_path(cls): @@ -407,9 +386,7 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.frame_format = mpl.rcParams['animation.frame_format'] - @_api.delete_parameter("3.3", "clear_temp") - def setup(self, fig, outfile, dpi=None, frame_prefix=None, - clear_temp=True): + def setup(self, fig, outfile, dpi=None, frame_prefix=None): """ Setup for writing the movie file. @@ -423,13 +400,10 @@ def setup(self, fig, outfile, dpi=None, frame_prefix=None, The dpi of the output file. This, with the figure size, controls the size in pixels of the resulting movie file. frame_prefix : str, optional - The filename prefix to use for temporary files. If None (the + The filename prefix to use for temporary files. If *None* (the default), files are written to a temporary directory which is - deleted by `cleanup` (regardless of the value of *clear_temp*). - clear_temp : bool, optional - If the temporary files should be deleted after stitching - the final result. Setting this to ``False`` can be useful for - debugging. Defaults to ``True``. + deleted by `cleanup`; if not *None*, no temporary files are + deleted. """ self.fig = fig self.outfile = outfile @@ -444,7 +418,6 @@ def setup(self, fig, outfile, dpi=None, frame_prefix=None, else: self._tmpdir = None self.temp_prefix = frame_prefix - self._clear_temp = clear_temp self._frame_counter = 0 # used for generating sequential file names self._temp_paths = list() self.fname_format_str = '%s%%07d.%s' @@ -453,15 +426,6 @@ def __del__(self): if self._tmpdir: self._tmpdir.cleanup() - @_api.deprecated("3.3") - @property - def clear_temp(self): - return self._clear_temp - - @clear_temp.setter - def clear_temp(self, value): - self._clear_temp = value - @property def frame_format(self): """ @@ -488,10 +452,9 @@ def _base_temp_name(self): def grab_frame(self, **savefig_kwargs): # docstring inherited - # Overloaded to explicitly close temp file. # Creates a filename for saving using basename and counter. path = Path(self._base_temp_name() % self._frame_counter) - self._temp_paths.append(path) # Record the filename for later cleanup. + self._temp_paths.append(path) # Record the filename for later use. self._frame_counter += 1 # Ensures each created name is unique. _log.debug('FileMovieWriter.grab_frame: Grabbing frame %d to path=%s', self._frame_counter, path) @@ -502,20 +465,15 @@ def grab_frame(self, **savefig_kwargs): def finish(self): # Call run here now that all frame grabbing is done. All temp files # are available to be assembled. - self._run() - super().finish() # Will call clean-up - - def _cleanup(self): # Inline to finish() once cleanup() is removed. - super()._cleanup() - if self._tmpdir: - _log.debug('MovieWriter: clearing temporary path=%s', self._tmpdir) - self._tmpdir.cleanup() - else: - if self._clear_temp: - _log.debug('MovieWriter: clearing temporary paths=%s', - self._temp_paths) - for path in self._temp_paths: - path.unlink() + try: + self._run() + super().finish() + finally: + if self._tmpdir: + _log.debug( + 'MovieWriter: clearing temporary path=%s', self._tmpdir + ) + self._tmpdir.cleanup() @writers.register('pillow') @@ -532,7 +490,6 @@ def grab_frame(self, **savefig_kwargs): buf = BytesIO() self.fig.savefig( buf, **{**savefig_kwargs, "format": "rgba", "dpi": self.dpi}) - renderer = self.fig.canvas.get_renderer() self._frames.append(Image.frombuffer( "RGBA", self.frame_size, buf.getbuffer(), "raw", "RGBA", 0, 1)) @@ -548,8 +505,8 @@ class FFMpegBase: """ Mixin class for FFMpeg output. - To be useful this must be multiply-inherited from with a - `MovieWriterBase` sub-class. + This is a base class for the concrete `FFMpegWriter` and `FFMpegFileWriter` + classes. """ _exec_key = 'animation.ffmpeg_path' @@ -583,17 +540,6 @@ def output_args(self): return args + ['-y', self.outfile] - @classmethod - def isAvailable(cls): - return ( - super().isAvailable() - # Ubuntu 12.04 ships a broken ffmpeg binary which we shouldn't use. - # NOTE: when removed, remove the same method in AVConvBase. - and b'LibAv' not in subprocess.run( - [cls.bin_path()], creationflags=subprocess_creation_flags, - stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, - stderr=subprocess.PIPE).stderr) - # Combine FFMpeg options with pipe-based writing @writers.register('ffmpeg') @@ -652,67 +598,47 @@ def _args(self): return [self.bin_path(), *args, *self.output_args] -# Base class of avconv information. AVConv has identical arguments to FFMpeg. -@_api.deprecated('3.3') -class AVConvBase(FFMpegBase): - """ - Mixin class for avconv output. - - To be useful this must be multiply-inherited from with a - `MovieWriterBase` sub-class. - """ - - _exec_key = 'animation.avconv_path' - _args_key = 'animation.avconv_args' - - # NOTE : should be removed when the same method is removed in FFMpegBase. - isAvailable = classmethod(MovieWriter.isAvailable.__func__) - - -# Combine AVConv options with pipe-based writing -@writers.register('avconv') -class AVConvWriter(AVConvBase, FFMpegWriter): - """ - Pipe-based avconv writer. - - Frames are streamed directly to avconv via a pipe and written in a single - pass. - """ - - -# Combine AVConv options with file-based writing -@writers.register('avconv_file') -class AVConvFileWriter(AVConvBase, FFMpegFileWriter): - """ - File-based avconv writer. - - Frames are written to temporary files on disk and then stitched - together at the end. - """ - - # Base class for animated GIFs with ImageMagick class ImageMagickBase: """ Mixin class for ImageMagick output. - To be useful this must be multiply-inherited from with a - `MovieWriterBase` sub-class. + This is a base class for the concrete `ImageMagickWriter` and + `ImageMagickFileWriter` classes, which define an ``input_names`` attribute + (or property) specifying the input names passed to ImageMagick. """ _exec_key = 'animation.convert_path' _args_key = 'animation.convert_args' + @_api.deprecated("3.6") @property def delay(self): return 100. / self.fps + @_api.deprecated("3.6") @property def output_args(self): extra_args = (self.extra_args if self.extra_args is not None else mpl.rcParams[self._args_key]) return [*extra_args, self.outfile] + def _args(self): + # ImageMagick does not recognize "raw". + fmt = "rgba" if self.frame_format == "raw" else self.frame_format + extra_args = (self.extra_args if self.extra_args is not None + else mpl.rcParams[self._args_key]) + return [ + self.bin_path(), + "-size", "%ix%i" % self.frame_size, + "-depth", "8", + "-delay", str(100 / self.fps), + "-loop", "0", + f"{fmt}:{self.input_names}", + *extra_args, + self.outfile, + ] + @classmethod def bin_path(cls): binpath = super().bin_path() @@ -734,18 +660,13 @@ def isAvailable(cls): @writers.register('imagemagick') class ImageMagickWriter(ImageMagickBase, MovieWriter): """ - Pipe-based animated gif. + Pipe-based animated gif writer. Frames are streamed directly to ImageMagick via a pipe and written in a single pass. - """ - def _args(self): - return ([self.bin_path(), - '-size', '%ix%i' % self.frame_size, '-depth', '8', - '-delay', str(self.delay), '-loop', '0', - '%s:-' % self.frame_format] - + self.output_args) + + input_names = "-" # stdin # Combine ImageMagick options with temp file-based writing @@ -759,15 +680,8 @@ class ImageMagickFileWriter(ImageMagickBase, FileMovieWriter): """ supported_formats = ['png', 'jpeg', 'tiff', 'raw', 'rgba'] - - def _args(self): - # Force format: ImageMagick does not recognize 'raw'. - fmt = 'rgba:' if self.frame_format == 'raw' else '' - return ([self.bin_path(), - '-size', '%ix%i' % self.frame_size, '-depth', '8', - '-delay', str(self.delay), '-loop', '0', - '%s%s*.%s' % (fmt, self.temp_prefix, self.frame_format)] - + self.output_args) + input_names = property( + lambda self: f'{self.temp_prefix}*.{self.frame_format}') # Taken directly from jakevdp's JSAnimation package at @@ -795,8 +709,6 @@ class HTMLWriter(FileMovieWriter): """Writer for JavaScript-based HTML movies.""" supported_formats = ['png', 'jpeg', 'tiff', 'svg'] - args_key = _api.deprecated("3.3")(property( - lambda self: 'animation.html_args')) @classmethod def isAvailable(cls): @@ -824,7 +736,7 @@ def __init__(self, fps=30, codec=None, bitrate=None, extra_args=None, super().__init__(fps, codec, bitrate, extra_args, metadata) - def setup(self, fig, outfile, dpi, frame_dir=None): + def setup(self, fig, outfile, dpi=None, frame_dir=None): outfile = Path(outfile) _api.check_in_list(['.html', '.htm'], outfile_extension=outfile.suffix) @@ -893,19 +805,13 @@ def finish(self): # duplicate the temporary file clean up logic from # FileMovieWriter.cleanup. We can not call the inherited - # versions of finished or cleanup because both assume that + # versions of finish or cleanup because both assume that # there is a subprocess that we either need to call to merge # many frames together or that there is a subprocess call that # we need to clean up. if self._tmpdir: _log.debug('MovieWriter: clearing temporary path=%s', self._tmpdir) self._tmpdir.cleanup() - else: - if self._clear_temp: - _log.debug('MovieWriter: clearing temporary paths=%s', - self._temp_paths) - for path in self._temp_paths: - path.unlink() class Animation: @@ -972,9 +878,11 @@ def __del__(self): if not getattr(self, '_draw_was_started', True): warnings.warn( 'Animation was deleted without rendering anything. This is ' - 'most likely unintended. To prevent deletion, assign the ' - 'Animation to a variable that exists for as long as you need ' - 'the Animation.') + 'most likely not intended. To prevent deletion, assign the ' + 'Animation to a variable, e.g. `anim`, that exists until you ' + 'output the Animation using `plt.show()` or ' + '`anim.save()`.' + ) def _start(self, *args): """ @@ -1238,11 +1146,11 @@ def _blit_draw(self, artists): # Handles blitted drawing, which renders only the artists given instead # of the entire figure. updated_ax = {a.axes for a in artists} - # Enumerate artists to cache axes' backgrounds. We do not draw + # Enumerate artists to cache Axes backgrounds. We do not draw # artists yet to not cache foreground from plots with shared axes for ax in updated_ax: # If we haven't cached the background for the current view of this - # axes object, do so now. This might not always be reliable, but + # Axes object, do so now. This might not always be reliable, but # it's an attempt to automate the process. cur_view = ax._get_view() view, bg = self._blit_cache.get(ax, (object(), None)) @@ -1252,12 +1160,12 @@ def _blit_draw(self, artists): # Make a separate pass to draw foreground. for a in artists: a.axes.draw_artist(a) - # After rendering all the needed artists, blit each axes individually. + # After rendering all the needed artists, blit each Axes individually. for ax in updated_ax: ax.figure.canvas.blit(ax.bbox) def _blit_clear(self, artists): - # Get a list of the axes that need clearing from the artists that + # Get a list of the Axes that need clearing from the artists that # have been drawn. Grab the appropriate saved background from the # cache and restore. axes = {a.axes for a in artists} @@ -1272,13 +1180,19 @@ def _blit_clear(self, artists): self._blit_cache.pop(ax) def _setup_blit(self): - # Setting up the blit requires: a cache of the background for the - # axes + # Setting up the blit requires: a cache of the background for the Axes self._blit_cache = dict() self._drawn_artists = [] + # _post_draw needs to be called first to initialize the renderer + self._post_draw(None, self._blit) + # Then we need to clear the Frame for the initial draw + # This is typically handled in _on_resize because QT and Tk + # emit a resize event on launch, but the macosx backend does not, + # thus we force it here for everyone for consistency + self._init_draw() + # Connect to future resize events self._resize_id = self._fig.canvas.mpl_connect('resize_event', self._on_resize) - self._post_draw(None, self._blit) def _on_resize(self, event): # On resize, we need to disable the resize event handling so we don't @@ -1381,7 +1295,20 @@ def to_html5_video(self, embed_limit=None): return 'Video too large to embed.' def to_jshtml(self, fps=None, embed_frames=True, default_mode=None): - """Generate HTML representation of the animation""" + """ + Generate HTML representation of the animation. + + Parameters + ---------- + fps : int, optional + Movie frame rate (per second). If not set, the frame rate from + the animation's frame interval. + embed_frames : bool, optional + default_mode : str, optional + What to do when the animation ends. Must be one of ``{'loop', + 'once', 'reflect'}``. Defaults to ``'loop'`` if ``self.repeat`` + is True, otherwise ``'once'``. + """ if fps is None and hasattr(self, '_interval'): # Convert interval in ms to frames per second fps = 1000 / self._interval @@ -1475,19 +1402,32 @@ def _step(self, *args): # delay and set the callback to one which will then set the interval # back. still_going = super()._step(*args) - if not still_going and self.repeat: - self._init_draw() - self.frame_seq = self.new_frame_seq() - self.event_source.interval = self._repeat_delay - return True - else: - self.event_source.interval = self._interval - return still_going + if not still_going: + if self.repeat: + # Restart the draw loop + self._init_draw() + self.frame_seq = self.new_frame_seq() + self.event_source.interval = self._repeat_delay + return True + else: + # We are done with the animation. Call pause to remove + # animated flags from artists that were using blitting + self.pause() + if self._blit: + # Remove the resize callback if we were blitting + self._fig.canvas.mpl_disconnect(self._resize_id) + self._fig.canvas.mpl_disconnect(self._close_id) + self.event_source = None + return False + + self.event_source.interval = self._interval + return True class ArtistAnimation(TimedAnimation): """ - Animation using a fixed set of `.Artist` objects. + `TimedAnimation` subclass that creates an animation by using a fixed + set of `.Artist` objects. Before creating an instance, all plotting should have taken place and the relevant artists saved. @@ -1563,7 +1503,8 @@ def _draw_frame(self, artists): class FuncAnimation(TimedAnimation): """ - Makes an animation by repeatedly calling a function *func*. + `TimedAnimation` subclass that makes an animation by repeatedly calling + a function *func*. .. note:: @@ -1579,12 +1520,24 @@ class FuncAnimation(TimedAnimation): func : callable The function to call at each frame. The first argument will be the next value in *frames*. Any additional positional - arguments can be supplied via the *fargs* parameter. + arguments can be supplied using `functools.partial` or via the *fargs* + parameter. The required signature is:: def func(frame, *fargs) -> iterable_of_artists + It is often more convenient to provide the arguments using + `functools.partial`. In this way it is also possible to pass keyword + arguments. To pass a function with both positional and keyword + arguments, set all arguments as keyword arguments, just leaving the + *frame* argument unset:: + + def func(frame, art, *, y=None): + ... + + ani = FuncAnimation(fig, partial(func, art=ln, y='foo')) + If ``blit == True``, *func* must return an iterable of all artists that were modified or created. This information is used by the blitting algorithm to determine which parts of the figure have to be updated. @@ -1623,7 +1576,8 @@ def init_func() -> iterable_of_artists value is unused if ``blit == False`` and may be omitted in that case. fargs : tuple or None, optional - Additional arguments to pass to each call to *func*. + Additional arguments to pass to each call to *func*. Note: the use of + `functools.partial` is preferred over *fargs*. See *func* for details. save_count : int, default: 100 Fallback for the number of values from *frames* to cache. This is @@ -1693,7 +1647,7 @@ def iter_frames(frames=frames): self.save_count = 100 else: # itertools.islice returns an error when passed a numpy int instead - # of a native python int (http://bugs.python.org/issue30537). + # of a native python int (https://bugs.python.org/issue30537). # As a workaround, convert save_count to a native python int. self.save_count = int(self.save_count) @@ -1750,8 +1704,21 @@ def _init_draw(self): # For blitting, the init_func should return a sequence of modified # artists. if self._init_func is None: - self._draw_frame(next(self.new_frame_seq())) - + try: + frame_data = next(self.new_frame_seq()) + except StopIteration: + # we can't start the iteration, it may have already been + # exhausted by a previous save or just be 0 length. + # warn and bail. + warnings.warn( + "Can not start iterating the frames for the initial draw. " + "This can be caused by passing in a 0 length sequence " + "for *frames*.\n\n" + "If you passed *frames* as a generator " + "it may be exhausted due to a previous display or save." + ) + return + self._draw_frame(frame_data) else: self._drawn_artists = self._init_func() if self._blit: @@ -1785,7 +1752,7 @@ def _draw_frame(self, framedata): except TypeError: raise err from None - # check each item if is artist + # check each item if it's artist for i in self._drawn_artists: if not isinstance(i, mpl.artist.Artist): raise err diff --git a/lib/matplotlib/artist.py b/lib/matplotlib/artist.py index b6f13f898f50..1ca1c1d3e37e 100644 --- a/lib/matplotlib/artist.py +++ b/lib/matplotlib/artist.py @@ -1,6 +1,8 @@ -from collections import OrderedDict, namedtuple -from functools import wraps +from collections import namedtuple +import contextlib +from functools import lru_cache, wraps import inspect +from inspect import Signature, Parameter import logging from numbers import Number import re @@ -9,7 +11,9 @@ import numpy as np import matplotlib as mpl -from . import _api, cbook, docstring +from . import _api, cbook +from .colors import BoundaryNorm +from .cm import ScalarMappable from .path import Path from .transforms import (Bbox, IdentityTransform, Transform, TransformedBbox, TransformedPatchPath, TransformedPath) @@ -26,12 +30,8 @@ def allow_rasterization(draw): renderer. """ - # Axes has a second (deprecated) argument inframe for its draw method. - # args and kwargs are deprecated, but we don't wrap this in - # _api.delete_parameter for performance; the relevant deprecation - # warning will be emitted by the inner draw() call. @wraps(draw) - def draw_wrapper(artist, renderer, *args, **kwargs): + def draw_wrapper(artist, renderer): try: if artist.get_rasterized(): if renderer._raster_depth == 0 and not renderer._rasterizing: @@ -48,7 +48,7 @@ def draw_wrapper(artist, renderer, *args, **kwargs): if artist.get_agg_filter() is not None: renderer.start_filter() - return draw(artist, renderer, *args, **kwargs) + return draw(artist, renderer) finally: if artist.get_agg_filter() is not None: renderer.stop_filter(artist.get_agg_filter()) @@ -87,6 +87,12 @@ def _stale_axes_callback(self, val): _XYPair = namedtuple("_XYPair", "x y") +class _Unset: + def __repr__(self): + return "" +_UNSET = _Unset() + + class Artist: """ Abstract base class for objects that render into a FigureCanvas. @@ -96,6 +102,51 @@ class Artist: zorder = 0 + def __init_subclass__(cls): + # Inject custom set() methods into the subclass with signature and + # docstring based on the subclasses' properties. + + if not hasattr(cls.set, '_autogenerated_signature'): + # Don't overwrite cls.set if the subclass or one of its parents + # has defined a set method set itself. + # If there was no explicit definition, cls.set is inherited from + # the hierarchy of auto-generated set methods, which hold the + # flag _autogenerated_signature. + return + + cls.set = lambda self, **kwargs: Artist.set(self, **kwargs) + cls.set.__name__ = "set" + cls.set.__qualname__ = f"{cls.__qualname__}.set" + cls._update_set_signature_and_docstring() + + _PROPERTIES_EXCLUDED_FROM_SET = [ + 'navigate_mode', # not a user-facing function + 'figure', # changing the figure is such a profound operation + # that we don't want this in set() + '3d_properties', # cannot be used as a keyword due to leading digit + ] + + @classmethod + def _update_set_signature_and_docstring(cls): + """ + Update the signature of the set function to list all properties + as keyword arguments. + + Property aliases are not listed in the signature for brevity, but + are still accepted as keyword arguments. + """ + cls.set.__signature__ = Signature( + [Parameter("self", Parameter.POSITIONAL_OR_KEYWORD), + *[Parameter(prop, Parameter.KEYWORD_ONLY, default=_UNSET) + for prop in ArtistInspector(cls).get_setters() + if prop not in Artist._PROPERTIES_EXCLUDED_FROM_SET]]) + cls.set._autogenerated_signature = True + + cls.set.__doc__ = ( + "Set multiple properties at once.\n\n" + "Supported properties are\n\n" + + kwdoc(cls)) + def __init__(self): self._stale = True self.stale_callback = None @@ -112,13 +163,12 @@ def __init__(self): self._clipon = True self._label = '' self._picker = None - self._contains = None self._rasterized = False self._agg_filter = None # Normally, artist classes need to be queried for mouseover info if and # only if they override get_cursor_data. self._mouseover = type(self).get_cursor_data != Artist.get_cursor_data - self._callbacks = cbook.CallbackRegistry() + self._callbacks = cbook.CallbackRegistry(signals=["pchanged"]) try: self.axes = None except AttributeError: @@ -136,7 +186,7 @@ def __init__(self): def __getstate__(self): d = self.__dict__.copy() # remove the unpicklable remove method, this will get re-added on load - # (by the axes) if the artist lives on an axes. + # (by the Axes) if the artist lives on an Axes. d['stale_callback'] = None return d @@ -166,10 +216,8 @@ def remove(self): if hasattr(self, 'axes') and self.axes: # remove from the mouse hit list self.axes._mouseover_set.discard(self) - # mark the axes as stale self.axes.stale = True - # decouple the artist from the axes - self.axes = None + self.axes = None # decouple the artist from the Axes _ax_flag = True if self.figure: @@ -188,13 +236,13 @@ def remove(self): def have_units(self): """Return whether units are set on any axis.""" ax = self.axes - return ax and any(axis.have_units() for axis in ax._get_axis_list()) + return ax and any(axis.have_units() for axis in ax._axis_map.values()) def convert_xunits(self, x): """ Convert *x* using the unit type of the xaxis. - If the artist is not in contained in an Axes or if the xaxis does not + If the artist is not contained in an Axes or if the xaxis does not have units, *x* itself is returned. """ ax = getattr(self, 'axes', None) @@ -206,7 +254,7 @@ def convert_yunits(self, y): """ Convert *y* using the unit type of the yaxis. - If the artist is not in contained in an Axes or if the yaxis does not + If the artist is not contained in an Axes or if the yaxis does not have units, *y* itself is returned. """ ax = getattr(self, 'axes', None) @@ -251,9 +299,9 @@ def stale(self, val): if val and self.stale_callback is not None: self.stale_callback(self, val) - def get_window_extent(self, renderer): + def get_window_extent(self, renderer=None): """ - 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. @@ -271,24 +319,7 @@ def get_window_extent(self, renderer): """ return Bbox([[0, 0], [0, 0]]) - def _get_clipping_extent_bbox(self): - """ - Return a bbox with the extents of the intersection of the clip_path - and clip_box for this artist, or None if both of these are - None, or ``get_clip_on`` is False. - """ - bbox = None - if self.get_clip_on(): - clip_box = self.get_clip_box() - if clip_box is not None: - bbox = clip_box - clip_path = self.get_clip_path() - if clip_path is not None and bbox is not None: - clip_path = clip_path.get_fully_transformed_path() - bbox = Bbox.intersection(bbox, clip_path.get_extents()) - return bbox - - def get_tightbbox(self, renderer): + def get_tightbbox(self, renderer=None): """ Like `.Artist.get_window_extent`, but includes any clipping. @@ -309,7 +340,7 @@ def get_tightbbox(self, renderer): if clip_box is not None: bbox = Bbox.intersection(bbox, clip_box) clip_path = self.get_clip_path() - if clip_path is not None and bbox is not None: + if clip_path is not None: clip_path = clip_path.get_fully_transformed_path() bbox = Bbox.intersection(bbox, clip_path.get_extents()) return bbox @@ -404,10 +435,9 @@ def _default_contains(self, mouseevent, figure=None): """ Base impl. for checking whether a mouseevent happened in an artist. - 1. If the artist defines a custom checker, use it (deprecated). - 2. If the artist figure is known and the event did not occur in that + 1. If the artist figure is known and the event did not occur in that figure (by checking its ``canvas`` attribute), reject it. - 3. Otherwise, return `None, {}`, indicating that the subclass' + 2. Otherwise, return `None, {}`, indicating that the subclass' implementation should be used. Subclasses should start their definition of `contains` as follows: @@ -420,8 +450,6 @@ def _default_contains(self, mouseevent, figure=None): The *figure* kwarg is provided for the implementation of `.Figure.contains`. """ - if callable(self._contains): - return self._contains(self, mouseevent) if figure is not None and mouseevent.canvas is not figure.canvas: return False, {} return None, {} @@ -449,45 +477,6 @@ def contains(self, mouseevent): _log.warning("%r needs 'contains' method", self.__class__.__name__) return False, {} - @_api.deprecated("3.3", alternative="set_picker") - def set_contains(self, picker): - """ - Define a custom contains test for the artist. - - The provided callable replaces the default `.contains` method - of the artist. - - Parameters - ---------- - picker : callable - A custom picker function to evaluate if an event is within the - artist. The function must have the signature:: - - def contains(artist: Artist, event: MouseEvent) -> bool, dict - - that returns: - - - a bool indicating if the event is within the artist - - a dict of additional information. The dict should at least - return the same information as the default ``contains()`` - implementation of the respective artist, but may provide - additional information. - """ - if not callable(picker): - raise TypeError("picker is not a callable") - self._contains = picker - - @_api.deprecated("3.3", alternative="get_picker") - def get_contains(self): - """ - Return the custom contains function of the artist if set, or *None*. - - See Also - -------- - set_contains - """ - return self._contains - def pickable(self): """ Return whether the artist is pickable. @@ -509,6 +498,7 @@ def pick(self, mouseevent): -------- set_picker, get_picker, pickable """ + from .backend_bases import PickEvent # Circular import. # Pick self if self.pickable(): picker = self.get_picker() @@ -517,18 +507,19 @@ def pick(self, mouseevent): else: inside, prop = self.contains(mouseevent) if inside: - self.figure.canvas.pick_event(mouseevent, self, **prop) + PickEvent("pick_event", self.figure.canvas, + mouseevent, self, **prop)._process() # Pick children for a in self.get_children(): - # make sure the event happened in the same axes + # make sure the event happened in the same Axes ax = getattr(a, 'axes', None) if (mouseevent.inaxes is None or ax is None or mouseevent.inaxes == ax): # we need to check if mouseevent.inaxes is None - # because some objects associated with an axes (e.g., a + # because some objects associated with an Axes (e.g., a # tick label) can be outside the bounding box of the - # axes and inaxes will be None + # Axes and inaxes will be None # also check that ax is None so that it traverse objects # which do no have an axes property but children might a.pick(mouseevent) @@ -684,6 +675,9 @@ def set_sketch_params(self, scale=None, length=None, randomness=None): The scale factor by which the length is shrunken or expanded (default 16.0) + The PGF backend uses this argument as an RNG seed and not as + described above. Using the same seed yields the same random shape. + .. ACCEPTS: (scale: float, length: float, randomness: float) """ if scale is None: @@ -834,6 +828,30 @@ def get_in_layout(self): """ return self._in_layout + def _fully_clipped_to_axes(self): + """ + Return a boolean flag, ``True`` if the artist is clipped to the Axes + and can thus be skipped in layout calculations. Requires `get_clip_on` + is True, one of `clip_box` or `clip_path` is set, ``clip_box.extents`` + is equivalent to ``ax.bbox.extents`` (if set), and ``clip_path._patch`` + is equivalent to ``ax.patch`` (if set). + """ + # Note that ``clip_path.get_fully_transformed_path().get_extents()`` + # cannot be directly compared to ``axes.bbox.extents`` because the + # extents may be undefined (i.e. equivalent to ``Bbox.null()``) + # before the associated artist is drawn, and this method is meant + # to determine whether ``axes.get_tightbbox()`` may bypass drawing + clip_box = self.get_clip_box() + clip_path = self.get_clip_path() + return (self.axes is not None + and self.get_clip_on() + and (clip_box is not None or clip_path is not None) + and (clip_box is None + or np.all(clip_box.extents == self.axes.bbox.extents)) + and (clip_path is None + or isinstance(clip_path, TransformedPatchPath) + and clip_path._patch is self.axes.patch)) + def get_clip_on(self): """Return whether the artist uses clipping.""" return self._clipon @@ -860,7 +878,7 @@ def set_clip_on(self, b): """ Set whether the artist uses clipping. - When False artists will be visible outside of the axes which + When False artists will be visible outside of the Axes which can lead to unexpected results. Parameters @@ -919,18 +937,18 @@ def set_agg_filter(self, filter_func): Parameters ---------- filter_func : callable - A filter function, which takes a (m, n, 3) float array and a dpi - value, and returns a (m, n, 3) array. + A filter function, which takes a (m, n, depth) float array + and a dpi value, and returns a (m, n, depth) array and two + offsets from the bottom left corner of the image .. ACCEPTS: a filter function, which takes a (m, n, 3) float array - and a dpi value, and returns a (m, n, 3) array + and a dpi value, and returns a (m, n, 3) array and two offsets + from the bottom left corner of the image """ self._agg_filter = filter_func self.stale = True - @_api.delete_parameter("3.3", "args") - @_api.delete_parameter("3.3", "kwargs") - def draw(self, renderer, *args, **kwargs): + def draw(self, renderer): """ Draw the Artist (and its children) using the given renderer. @@ -1008,7 +1026,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 @@ -1035,38 +1053,6 @@ def set_in_layout(self, in_layout): """ self._in_layout = in_layout - def update(self, props): - """ - Update this artist's properties from the dict *props*. - - Parameters - ---------- - props : dict - """ - ret = [] - with cbook._setattr_cm(self, eventson=False): - for k, v in props.items(): - if k != k.lower(): - _api.warn_deprecated( - "3.3", message="Case-insensitive properties were " - "deprecated in %(since)s and support will be removed " - "%(removal)s") - k = k.lower() - # White list attributes we want to be able to update through - # art.update, art.set, setp. - if k == "axes": - ret.append(setattr(self, k, v)) - else: - func = getattr(self, f"set_{k}", None) - if not callable(func): - raise AttributeError(f"{type(self).__name__!r} object " - f"has no property {k!r}") - ret.append(func(v)) - if ret: - self.pchanged() - self.stale = True - return ret - def get_label(self): """Return the label used for this artist in the legend.""" return self._label @@ -1117,6 +1103,11 @@ def sticky_edges(self): where one usually expects no margin on the bottom edge (0) of the histogram. + Moreover, margin expansion "bumps" against sticky edges and cannot + cross them. For example, if the upper data limit is 1.0, the upper + view limit computed by simple margin application is 1.2, but there is a + sticky edge at 1.1, then the actual upper view limit will be 1.1. + This attribute cannot be assigned to; however, the ``x`` and ``y`` lists can be modified in place as needed. @@ -1149,34 +1140,70 @@ def properties(self): """Return a dictionary of all the properties of the artist.""" return ArtistInspector(self).properties() + def _update_props(self, props, errfmt): + """ + Helper for `.Artist.set` and `.Artist.update`. + + *errfmt* is used to generate error messages for invalid property + names; it get formatted with ``type(self)`` and the property name. + """ + ret = [] + with cbook._setattr_cm(self, eventson=False): + for k, v in props.items(): + # Allow attributes we want to be able to update through + # art.update, art.set, setp. + if k == "axes": + ret.append(setattr(self, k, v)) + else: + func = getattr(self, f"set_{k}", None) + if not callable(func): + raise AttributeError( + errfmt.format(cls=type(self), prop_name=k)) + ret.append(func(v)) + if ret: + self.pchanged() + self.stale = True + return ret + + def update(self, props): + """ + Update this artist's properties from the dict *props*. + + Parameters + ---------- + props : dict + """ + return self._update_props( + props, "{cls.__name__!r} object has no property {prop_name!r}") + + def _internal_update(self, kwargs): + """ + Update artist properties without prenormalizing them, but generating + errors as if calling `set`. + + The lack of prenormalization is to maintain backcompatibility. + """ + return self._update_props( + kwargs, "{cls.__name__}.set() got an unexpected keyword argument " + "{prop_name!r}") + def set(self, **kwargs): - """A property batch setter. Pass *kwargs* to set properties.""" - kwargs = cbook.normalize_kwargs(kwargs, self) - move_color_to_start = False - if "color" in kwargs: - keys = [*kwargs] - i_color = keys.index("color") - props = ["edgecolor", "facecolor"] - if any(tp.__module__ == "matplotlib.collections" - and tp.__name__ == "Collection" - for tp in type(self).__mro__): - props.append("alpha") - for other in props: - if other not in keys: - continue - i_other = keys.index(other) - if i_other < i_color: - move_color_to_start = True - _api.warn_deprecated( - "3.3", message=f"You have passed the {other!r} kwarg " - "before the 'color' kwarg. Artist.set() currently " - "reorders the properties to apply 'color' first, but " - "this is deprecated since %(since)s and will be " - "removed %(removal)s; please pass 'color' first " - "instead.") - if move_color_to_start: - kwargs = {"color": kwargs.pop("color"), **kwargs} - return self.update(kwargs) + # docstring and signature are auto-generated via + # Artist._update_set_signature_and_docstring() at the end of the + # module. + return self._internal_update(cbook.normalize_kwargs(kwargs, self)) + + @contextlib.contextmanager + def _cm_set(self, **kwargs): + """ + `.Artist.set` context-manager that restores original values at exit. + """ + orig_vals = {k: getattr(self, f"get_{k}")() for k in kwargs} + try: + self.set(**kwargs) + yield + finally: + self.set(**orig_vals) def findobj(self, match=None, include_self=True): """ @@ -1262,42 +1289,96 @@ def format_cursor_data(self, data): method yourself. The default implementation converts ints and floats and arrays of ints - and floats into a comma-separated string enclosed in square brackets. + and floats into a comma-separated string enclosed in square brackets, + unless the artist has an associated colorbar, in which case scalar + values are formatted using the colorbar's formatter. See Also -------- get_cursor_data """ - try: - data[0] - except (TypeError, IndexError): - data = [data] - data_str = ', '.join('{:0.3g}'.format(item) for item in data - if isinstance(item, Number)) - return "[" + data_str + "]" + if np.ndim(data) == 0 and isinstance(self, ScalarMappable): + # This block logically belongs to ScalarMappable, but can't be + # implemented in it because most ScalarMappable subclasses inherit + # from Artist first and from ScalarMappable second, so + # Artist.format_cursor_data would always have precedence over + # ScalarMappable.format_cursor_data. + n = self.cmap.N + if np.ma.getmask(data): + return "[]" + normed = self.norm(data) + if np.isfinite(normed): + if isinstance(self.norm, BoundaryNorm): + # not an invertible normalization mapping + cur_idx = np.argmin(np.abs(self.norm.boundaries - data)) + neigh_idx = max(0, cur_idx - 1) + # use max diff to prevent delta == 0 + delta = np.diff( + self.norm.boundaries[neigh_idx:cur_idx + 2] + ).max() - @property - def mouseover(self): - """ - If this property is set to *True*, the artist will be queried for - custom context information when the mouse cursor moves over it. + else: + # Midpoints of neighboring color intervals. + neighbors = self.norm.inverse( + (int(normed * n) + np.array([0, 1])) / n) + delta = abs(neighbors - data).max() + g_sig_digits = cbook._g_sig_digits(data, delta) + else: + g_sig_digits = 3 # Consistent with default below. + return "[{:-#.{}g}]".format(data, g_sig_digits) + else: + try: + data[0] + except (TypeError, IndexError): + data = [data] + data_str = ', '.join('{:0.3g}'.format(item) for item in data + if isinstance(item, Number)) + return "[" + data_str + "]" - See also :meth:`get_cursor_data`, :class:`.ToolCursorPosition` and - :class:`.NavigationToolbar2`. + def get_mouseover(self): + """ + Return whether this artist is queried for custom context information + when the mouse cursor moves over it. """ return self._mouseover - @mouseover.setter - def mouseover(self, val): - val = bool(val) - self._mouseover = val + def set_mouseover(self, mouseover): + """ + Set whether this artist is queried for custom context information when + the mouse cursor moves over it. + + Parameters + ---------- + mouseover : bool + + See Also + -------- + get_cursor_data + .ToolCursorPosition + .NavigationToolbar2 + """ + self._mouseover = bool(mouseover) ax = self.axes if ax: - if val: + if self._mouseover: ax._mouseover_set.add(self) else: ax._mouseover_set.discard(self) + mouseover = property(get_mouseover, set_mouseover) # backcompat. + + +def _get_tightbbox_for_layout_only(obj, *args, **kwargs): + """ + Matplotlib's `.Axes.get_tightbbox` and `.Axis.get_tightbbox` support a + *for_layout_only* kwarg; this helper tries to uses the kwarg but skips it + when encountering third-party subclasses that do not support it. + """ + try: + return obj.get_tightbbox(*args, **{**kwargs, "for_layout_only": True}) + except TypeError: + return obj.get_tightbbox(*args, **kwargs) + class ArtistInspector: """ @@ -1413,17 +1494,29 @@ def get_setters(self): continue func = getattr(self.o, name) if (not callable(func) - or len(inspect.signature(func).parameters) < 2 + or self.number_of_parameters(func) < 2 or self.is_alias(func)): continue setters.append(name[4:]) return setters - def is_alias(self, o): - """Return whether method object *o* is an alias for another method.""" - ds = inspect.getdoc(o) + @staticmethod + @lru_cache(maxsize=None) + def number_of_parameters(func): + """Return number of parameters of the callable *func*.""" + return len(inspect.signature(func).parameters) + + @staticmethod + @lru_cache(maxsize=None) + def is_alias(method): + """ + Return whether the object *method* is an alias for another method. + """ + + ds = inspect.getdoc(method) if ds is None: return False + return ds.startswith('Alias for ') def aliased_name(self, s): @@ -1437,6 +1530,22 @@ def aliased_name(self, s): aliases = ''.join(' or %s' % x for x in sorted(self.aliasd.get(s, []))) return s + aliases + _NOT_LINKABLE = { + # A set of property setter methods that are not available in our + # current docs. This is a workaround used to prevent trying to link + # these setters which would lead to "target reference not found" + # warnings during doc build. + 'matplotlib.image._ImageBase.set_alpha', + 'matplotlib.image._ImageBase.set_array', + 'matplotlib.image._ImageBase.set_data', + 'matplotlib.image._ImageBase.set_filternorm', + 'matplotlib.image._ImageBase.set_filterrad', + 'matplotlib.image._ImageBase.set_interpolation', + 'matplotlib.image._ImageBase.set_interpolation_stage', + 'matplotlib.image._ImageBase.set_resample', + 'matplotlib.text._AnnotationBase.set_annotation_clip', + } + def aliased_name_rest(self, s, target): """ Return 'PROPNAME or alias' if *s* has an alias, else return 'PROPNAME', @@ -1446,6 +1555,10 @@ def aliased_name_rest(self, s, target): alias, return 'markerfacecolor or mfc' and for the transform property, which does not, return 'transform'. """ + # workaround to prevent "reference target not found" + if target in self._NOT_LINKABLE: + return f'``{s}``' + aliases = ''.join(' or %s' % x for x in sorted(self.aliasd.get(s, []))) return ':meth:`%s <%s>`%s' % (s, target, aliases) @@ -1578,7 +1691,7 @@ def getp(obj, property=None): If *property* is 'somename', this function returns ``obj.get_somename()``. - If is is None (or unset), it *prints* all gettable properties from + If it's None (or unset), it *prints* all gettable properties from *obj*. Many properties have aliases for shorter typing, e.g. 'lw' is an alias for 'linewidth'. In the output, aliases and full property names will be listed as: @@ -1683,8 +1796,7 @@ def setp(obj, *args, file=None, **kwargs): if len(args) % 2: raise ValueError('The set args must be string, value pairs') - # put args into ordereddict to maintain order - funcvals = OrderedDict((k, v) for k, v in zip(args[::2], args[1::2])) + funcvals = dict(zip(args[::2], args[1::2])) ret = [o.update(funcvals) for o in objs] + [o.set(**kwargs) for o in objs] return list(cbook.flatten(ret)) @@ -1710,5 +1822,6 @@ def kwdoc(artist): if mpl.rcParams['docstring.hardcopy'] else 'Properties:\n' + '\n'.join(ai.pprint_setters(leadingspace=4))) - -docstring.interpd.update(Artist_kwdoc=kwdoc(Artist)) +# We defer this to the end of them module, because it needs ArtistInspector +# to be defined. +Artist._update_set_signature_and_docstring() diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 82ee7918c25d..59b685224d9c 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -7,13 +7,13 @@ import numpy as np from numpy import ma +import matplotlib as mpl import matplotlib.category # Register category unit converter as side-effect. import matplotlib.cbook as cbook import matplotlib.collections as mcoll import matplotlib.colors as mcolors import matplotlib.contour as mcontour -import matplotlib.dates # Register date unit converter as side-effect. -import matplotlib.docstring as docstring +import matplotlib.dates # noqa # Register date unit converter as side-effect. import matplotlib.image as mimage import matplotlib.legend as mlegend import matplotlib.lines as mlines @@ -30,7 +30,7 @@ import matplotlib.transforms as mtransforms import matplotlib.tri as mtri import matplotlib.units as munits -from matplotlib import _api, _preprocess_data, rcParams +from matplotlib import _api, _docstring, _preprocess_data from matplotlib.axes._base import ( _AxesBase, _TransformedBoundsLocator, _process_plot_format) from matplotlib.axes._secondary_axes import SecondaryAxis @@ -43,6 +43,7 @@ # All the other methods should go in the _AxesBase class. +@_docstring.interpd class Axes(_AxesBase): """ The `Axes` contains most of the figure elements: `~.axis.Axis`, @@ -117,9 +118,9 @@ def set_title(self, label, fontdict=None, loc=None, pad=None, *, y=None, Which title to set. y : float, default: :rc:`axes.titley` - Vertical Axes loation for the title (1.0 is the top). If - None (the default), y is determined automatically to avoid - decorators on the Axes. + Vertical Axes location for the title (1.0 is the top). If + None (the default) and :rc:`axes.titley` is also None, y is + determined automatically to avoid decorators on the Axes. pad : float, default: :rc:`axes.titlepad` The offset of the title from the top of the Axes, in points. @@ -136,10 +137,10 @@ def set_title(self, label, fontdict=None, loc=None, pad=None, *, y=None, of valid text properties. """ if loc is None: - loc = rcParams['axes.titlelocation'] + loc = mpl.rcParams['axes.titlelocation'] if y is None: - y = rcParams['axes.titley'] + y = mpl.rcParams['axes.titley'] if y is None: y = 1.0 else: @@ -151,21 +152,21 @@ def set_title(self, label, fontdict=None, loc=None, pad=None, *, y=None, 'right': self._right_title} title = _api.check_getitem(titles, loc=loc.lower()) default = { - 'fontsize': rcParams['axes.titlesize'], - 'fontweight': rcParams['axes.titleweight'], + 'fontsize': mpl.rcParams['axes.titlesize'], + 'fontweight': mpl.rcParams['axes.titleweight'], 'verticalalignment': 'baseline', 'horizontalalignment': loc.lower()} - titlecolor = rcParams['axes.titlecolor'] + titlecolor = mpl.rcParams['axes.titlecolor'] if not cbook._str_lower_equal(titlecolor, 'auto'): default["color"] = titlecolor if pad is None: - pad = rcParams['axes.titlepad'] + pad = mpl.rcParams['axes.titlepad'] self._set_title_offset_trans(float(pad)) title.set_text(label) title.update(default) if fontdict is not None: title.update(fontdict) - title.update(kwargs) + title._internal_update(kwargs) return title def get_legend_handles_labels(self, legend_handler_map=None): @@ -182,7 +183,7 @@ def get_legend_handles_labels(self, legend_handler_map=None): [self], legend_handler_map) return handles, labels - @docstring.dedent_interpd + @_docstring.dedent_interpd def legend(self, *args, **kwargs): """ Place a legend on the Axes. @@ -190,10 +191,11 @@ def legend(self, *args, **kwargs): Call signatures:: legend() - legend(labels) legend(handles, labels) + legend(handles=handles) + legend(labels) - The call signatures correspond to these three different ways to use + The call signatures correspond to the following different ways to use this method: **1. Automatic detection of elements to be shown in the legend** @@ -214,34 +216,49 @@ def legend(self, *args, **kwargs): line.set_label('Label via method') ax.legend() - Specific lines can be excluded from the automatic legend element - selection by defining a label starting with an underscore. - This is default for all artists, so calling `.Axes.legend` without - any arguments and without setting the labels manually will result in - no legend being drawn. + .. note:: + Specific artists can be excluded from the automatic legend element + selection by using a label starting with an underscore, "_". + A string starting with an underscore is the default label for all + artists, so calling `.Axes.legend` without any arguments and + without setting the labels manually will result in no legend being + drawn. - **2. Labeling existing plot elements** + **2. Explicitly listing the artists and labels in the legend** - To make a legend for lines which already exist on the Axes - (via plot for instance), simply call this function with an iterable - of strings, one for each legend item. For example:: + For full control of which artists have a legend entry, it is possible + to pass an iterable of legend artists followed by an iterable of + legend labels respectively:: - ax.plot([1, 2, 3]) - ax.legend(['A simple line']) + ax.legend([line1, line2, line3], ['label1', 'label2', 'label3']) - Note: This call signature is discouraged, because the relation between - plot elements and labels is only implicit by their order and can - easily be mixed up. + **3. Explicitly listing the artists in the legend** - **3. Explicitly defining the elements in the legend** + This is similar to 2, but the labels are taken from the artists' + label properties. Example:: - For full control of which artists have a legend entry, it is possible - to pass an iterable of legend artists followed by an iterable of - legend labels respectively:: + line1, = ax.plot([1, 2, 3], label='label1') + line2, = ax.plot([1, 2, 3], label='label2') + ax.legend(handles=[line1, line2]) + + + **4. Labeling existing plot elements** + + .. admonition:: Discouraged + + This call signature is discouraged, because the relation between + plot elements and labels is only implicit by their order and can + easily be mixed up. + + To make a legend for all artists on an Axes, call this function with + an iterable of strings, one for each legend item. For example:: + + ax.plot([1, 2, 3]) + ax.plot([5, 6, 7]) + ax.legend(['First line', 'Second line']) - ax.legend([line1, line2, line3], ['label1', 'label2', 'label3']) Parameters ---------- @@ -311,13 +328,27 @@ def inset_axes(self, bounds, *, transform=None, zorder=5, **kwargs): Defaults to `ax.transAxes`, i.e. the units of *rect* are in Axes-relative coordinates. + projection : {None, 'aitoff', 'hammer', 'lambert', 'mollweide', \ +'polar', 'rectilinear', str}, optional + The projection type of the inset `~.axes.Axes`. *str* is the name + of a custom projection, see `~matplotlib.projections`. The default + None results in a 'rectilinear' projection. + + polar : bool, default: False + If True, equivalent to projection='polar'. + + axes_class : subclass type of `~.axes.Axes`, optional + The `.axes.Axes` subclass that is instantiated. This parameter + is incompatible with *projection* and *polar*. See + :ref:`axisartist_users-guide-index` for examples. + zorder : number Defaults to 5 (same as `.Axes.legend`). Adjust higher or lower to change whether it is above or below data plotted on the parent Axes. **kwargs - Other keyword arguments are passed on to the child `.Axes`. + Other keyword arguments are passed on to the inset Axes class. Returns ------- @@ -343,7 +374,10 @@ def inset_axes(self, bounds, *, transform=None, zorder=5, **kwargs): # This puts the rectangle into figure-relative coordinates. inset_locator = _TransformedBoundsLocator(bounds, transform) bounds = inset_locator(self, None).bounds - inset_ax = Axes(self.figure, bounds, zorder=zorder, **kwargs) + projection_class, pkw = self.figure._process_projection_requirements( + bounds, **kwargs) + inset_ax = projection_class(self.figure, bounds, zorder=zorder, **pkw) + # this locator lets the axes move if in data coordinates. # it gets called in `ax.apply_aspect() (of all places) inset_ax.set_axes_locator(inset_locator) @@ -352,7 +386,7 @@ def inset_axes(self, bounds, *, transform=None, zorder=5, **kwargs): return inset_ax - @docstring.dedent_interpd + @_docstring.dedent_interpd def indicate_inset(self, bounds, inset_ax=None, *, transform=None, facecolor='none', edgecolor='0.5', alpha=0.5, zorder=4.99, **kwargs): @@ -397,7 +431,7 @@ def indicate_inset(self, bounds, inset_ax=None, *, transform=None, **kwargs Other keyword arguments are passed on to the `.Rectangle` patch: - %(Rectangle_kwdoc)s + %(Rectangle:kwdoc)s Returns ------- @@ -504,7 +538,7 @@ def indicate_inset_zoom(self, inset_ax, **kwargs): rect = (xlim[0], ylim[0], xlim[1] - xlim[0], ylim[1] - ylim[0]) return self.indicate_inset(rect, inset_ax, **kwargs) - @docstring.dedent_interpd + @_docstring.dedent_interpd def secondary_xaxis(self, location, *, functions=None, **kwargs): """ Add a second x-axis to this Axes. @@ -546,7 +580,7 @@ def invert(x): raise ValueError('secondary_xaxis location must be either ' 'a float or "top"/"bottom"') - @docstring.dedent_interpd + @_docstring.dedent_interpd def secondary_yaxis(self, location, *, functions=None, **kwargs): """ Add a second y-axis to this Axes. @@ -578,7 +612,7 @@ def secondary_yaxis(self, location, *, functions=None, **kwargs): raise ValueError('secondary_yaxis location must be either ' 'a float or "left"/"right"') - @docstring.dedent_interpd + @_docstring.dedent_interpd def text(self, x, y, s, fontdict=None, **kwargs): """ Add text to the Axes. @@ -609,7 +643,7 @@ def text(self, x, y, s, fontdict=None, **kwargs): **kwargs : `~matplotlib.text.Text` properties. Other miscellaneous text parameters. - %(Text_kwdoc)s + %(Text:kwdoc)s Examples -------- @@ -646,10 +680,14 @@ def text(self, x, y, s, fontdict=None, **kwargs): self._add_text(t) return t - @_api.rename_parameter("3.3", "s", "text") - @docstring.dedent_interpd - def annotate(self, text, xy, *args, **kwargs): - a = mtext.Annotation(text, xy, *args, **kwargs) + @_docstring.dedent_interpd + def annotate(self, text, xy, xytext=None, xycoords='data', textcoords=None, + arrowprops=None, annotation_clip=None, **kwargs): + # Signature must match Annotation. This is verified in + # test_annotate_signature(). + a = mtext.Annotation(text, xy, xytext=xytext, xycoords=xycoords, + textcoords=textcoords, arrowprops=arrowprops, + annotation_clip=annotation_clip, **kwargs) a.set_transform(mtransforms.IdentityTransform()) if 'clip_on' in kwargs: a.set_clip_path(self.patch) @@ -658,10 +696,10 @@ def annotate(self, text, xy, *args, **kwargs): annotate.__doc__ = mtext.Annotation.__init__.__doc__ #### Lines and spans - @docstring.dedent_interpd + @_docstring.dedent_interpd def axhline(self, y=0, xmin=0, xmax=1, **kwargs): """ - Add a horizontal line across the axis. + Add a horizontal line across the Axes. Parameters ---------- @@ -686,7 +724,7 @@ def axhline(self, y=0, xmin=0, xmax=1, **kwargs): Valid keyword arguments are `.Line2D` properties, with the exception of 'transform': - %(Line2D_kwdoc)s + %(Line2D:kwdoc)s See Also -------- @@ -722,10 +760,11 @@ def axhline(self, y=0, xmin=0, xmax=1, **kwargs): trans = self.get_yaxis_transform(which='grid') l = mlines.Line2D([xmin, xmax], [y, y], transform=trans, **kwargs) self.add_line(l) - self._request_autoscale_view(scalex=False, scaley=scaley) + if scaley: + self._request_autoscale_view("y") return l - @docstring.dedent_interpd + @_docstring.dedent_interpd def axvline(self, x=0, ymin=0, ymax=1, **kwargs): """ Add a vertical line across the Axes. @@ -753,7 +792,7 @@ def axvline(self, x=0, ymin=0, ymax=1, **kwargs): Valid keyword arguments are `.Line2D` properties, with the exception of 'transform': - %(Line2D_kwdoc)s + %(Line2D:kwdoc)s See Also -------- @@ -789,7 +828,8 @@ def axvline(self, x=0, ymin=0, ymax=1, **kwargs): trans = self.get_xaxis_transform(which='grid') l = mlines.Line2D([x, x], [ymin, ymax], transform=trans, **kwargs) self.add_line(l) - self._request_autoscale_view(scalex=scalex, scaley=False) + if scalex: + self._request_autoscale_view("x") return l @staticmethod @@ -800,7 +840,7 @@ def _check_no_units(vals, names): raise ValueError(f"{name} must be a single scalar value, " f"but got {val}") - @docstring.dedent_interpd + @_docstring.dedent_interpd def axline(self, xy1, xy2=None, *, slope=None, **kwargs): """ Add an infinitely long straight line. @@ -837,7 +877,7 @@ def axline(self, xy1, xy2=None, *, slope=None, **kwargs): **kwargs Valid kwargs are `.Line2D` properties - %(Line2D_kwdoc)s + %(Line2D:kwdoc)s See Also -------- @@ -866,15 +906,15 @@ def axline(self, xy1, xy2=None, *, slope=None, **kwargs): if line.get_clip_path() is None: line.set_clip_path(self.patch) if not line.get_label(): - line.set_label(f"_line{len(self.lines)}") - self.lines.append(line) - line._remove_method = self.lines.remove + line.set_label(f"_child{len(self._children)}") + self._children.append(line) + line._remove_method = self._children.remove self.update_datalim(datalim) self._request_autoscale_view() return line - @docstring.dedent_interpd + @_docstring.dedent_interpd def axhspan(self, ymin, ymax, xmin=0, xmax=1, **kwargs): """ Add a horizontal span (rectangle) across the Axes. @@ -905,7 +945,7 @@ def axhspan(self, ymin, ymax, xmin=0, xmax=1, **kwargs): ---------------- **kwargs : `~matplotlib.patches.Polygon` properties - %(Polygon_kwdoc)s + %(Polygon:kwdoc)s See Also -------- @@ -919,10 +959,10 @@ def axhspan(self, ymin, ymax, xmin=0, xmax=1, **kwargs): p = mpatches.Polygon(verts, **kwargs) p.set_transform(self.get_yaxis_transform(which="grid")) self.add_patch(p) - self._request_autoscale_view(scalex=False) + self._request_autoscale_view("y") return p - @docstring.dedent_interpd + @_docstring.dedent_interpd def axvspan(self, xmin, xmax, ymin=0, ymax=1, **kwargs): """ Add a vertical span (rectangle) across the Axes. @@ -953,7 +993,7 @@ def axvspan(self, xmin, xmax, ymin=0, ymax=1, **kwargs): ---------------- **kwargs : `~matplotlib.patches.Polygon` properties - %(Polygon_kwdoc)s + %(Polygon:kwdoc)s See Also -------- @@ -974,8 +1014,9 @@ def axvspan(self, xmin, xmax, ymin=0, ymax=1, **kwargs): verts = [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)] p = mpatches.Polygon(verts, **kwargs) p.set_transform(self.get_xaxis_transform(which="grid")) + p.get_path()._interpolation_steps = 100 self.add_patch(p) - self._request_autoscale_view(scaley=False) + self._request_autoscale_view("x") return p @_preprocess_data(replace_names=["y", "xmin", "xmax", "colors"], @@ -992,7 +1033,7 @@ def hlines(self, y, xmin, xmax, colors=None, linestyles='solid', xmin, xmax : float or array-like Respective beginning and end of each line. If scalars are - provided, all lines will have same length. + provided, all lines will have the same length. colors : list of colors, default: :rc:`lines.color` @@ -1006,6 +1047,8 @@ def hlines(self, y, xmin, xmax, colors=None, linestyles='solid', Other Parameters ---------------- + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER **kwargs : `~matplotlib.collections.LineCollection` properties. See Also @@ -1040,16 +1083,17 @@ def hlines(self, y, xmin, xmax, colors=None, linestyles='solid', lines = mcoll.LineCollection(masked_verts, colors=colors, linestyles=linestyles, label=label) self.add_collection(lines, autolim=False) - lines.update(kwargs) + lines._internal_update(kwargs) if len(y) > 0: - minx = min(xmin.min(), xmax.min()) - maxx = max(xmin.max(), xmax.max()) - miny = y.min() - maxy = y.max() - + # Extreme values of xmin/xmax/y. Using masked_verts here handles + # the case of y being a masked *object* array (as can be generated + # e.g. by errorbar()), which would make nanmin/nanmax stumble. + minx = np.nanmin(masked_verts[..., 0]) + maxx = np.nanmax(masked_verts[..., 0]) + miny = np.nanmin(masked_verts[..., 1]) + maxy = np.nanmax(masked_verts[..., 1]) corners = (minx, miny), (maxx, maxy) - self.update_datalim(corners) self._request_autoscale_view() @@ -1069,7 +1113,7 @@ def vlines(self, x, ymin, ymax, colors=None, linestyles='solid', ymin, ymax : float or array-like Respective beginning and end of each line. If scalars are - provided, all lines will have same length. + provided, all lines will have the same length. colors : list of colors, default: :rc:`lines.color` @@ -1083,6 +1127,8 @@ def vlines(self, x, ymin, ymax, colors=None, linestyles='solid', Other Parameters ---------------- + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER **kwargs : `~matplotlib.collections.LineCollection` properties. See Also @@ -1117,14 +1163,16 @@ def vlines(self, x, ymin, ymax, colors=None, linestyles='solid', lines = mcoll.LineCollection(masked_verts, colors=colors, linestyles=linestyles, label=label) self.add_collection(lines, autolim=False) - lines.update(kwargs) + lines._internal_update(kwargs) if len(x) > 0: - minx = x.min() - maxx = x.max() - miny = min(ymin.min(), ymax.min()) - maxy = max(ymin.max(), ymax.max()) - + # Extreme values of x/ymin/ymax. Using masked_verts here handles + # the case of x being a masked *object* array (as can be generated + # e.g. by errorbar()), which would make nanmin/nanmax stumble. + minx = np.nanmin(masked_verts[..., 0]) + maxx = np.nanmax(masked_verts[..., 0]) + miny = np.nanmin(masked_verts[..., 1]) + maxy = np.nanmax(masked_verts[..., 1]) corners = (minx, miny), (maxx, maxy) self.update_datalim(corners) self._request_autoscale_view() @@ -1134,7 +1182,7 @@ def vlines(self, x, ymin, ymax, colors=None, linestyles='solid', @_preprocess_data(replace_names=["positions", "lineoffsets", "linelengths", "linewidths", "colors", "linestyles"]) - @docstring.dedent_interpd + @_docstring.dedent_interpd def eventplot(self, positions, orientation='horizontal', lineoffsets=1, linelengths=1, linewidths=None, colors=None, linestyles='solid', **kwargs): @@ -1211,6 +1259,9 @@ def eventplot(self, positions, orientation='horizontal', lineoffsets=1, If *positions* is 2D, this can be a sequence with length matching the length of *positions*. + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER + **kwargs Other keyword arguments are line collection properties. See `.LineCollection` for a list of the valid properties. @@ -1231,10 +1282,11 @@ def eventplot(self, positions, orientation='horizontal', lineoffsets=1, -------- .. plot:: gallery/lines_bars_and_markers/eventplot_demo.py """ - # We do the conversion first since not all unitized data is uniform - positions, lineoffsets, linelengths = self._process_unit_info( - [("x", positions), ("y", lineoffsets), ("y", linelengths)], kwargs) + lineoffsets, linelengths = self._process_unit_info( + [("y", lineoffsets), ("y", linelengths)], kwargs) + + # fix positions, noting that it can be a list of lists: if not np.iterable(positions): positions = [positions] elif any(np.iterable(position) for position in positions): @@ -1245,6 +1297,11 @@ def eventplot(self, positions, orientation='horizontal', lineoffsets=1, if len(positions) == 0: return [] + poss = [] + for position in positions: + poss += self._process_unit_info([("x", position)], kwargs) + positions = poss + # prevent 'singular' keys from **kwargs dict from overriding the effect # of 'plural' keyword arguments (e.g. 'color' overriding 'colors') colors = cbook._local_over_kwdict(colors, kwargs, 'color') @@ -1328,7 +1385,7 @@ def eventplot(self, positions, orientation='horizontal', lineoffsets=1, color=color, linestyle=linestyle) self.add_collection(coll, autolim=False) - coll.update(kwargs) + coll._internal_update(kwargs) colls.append(coll) if len(positions) > 0: @@ -1344,10 +1401,9 @@ def eventplot(self, positions, orientation='horizontal', lineoffsets=1, minline = (lineoffsets - linelengths).min() maxline = (lineoffsets + linelengths).max() - if (orientation is not None and - orientation.lower() == "vertical"): + if orientation == "vertical": corners = (minline, minpos), (maxline, maxpos) - else: # "horizontal", None or "none" (see EventCollection) + else: # "horizontal" corners = (minpos, minline), (maxpos, maxline) self.update_datalim(corners) self._request_autoscale_view() @@ -1358,7 +1414,7 @@ def eventplot(self, positions, orientation='horizontal', lineoffsets=1, # Uses a custom implementation of data-kwarg handling in # _process_plot_var_args. - @docstring.dedent_interpd + @_docstring.dedent_interpd def plot(self, *args, scalex=True, scaley=True, data=None, **kwargs): """ Plot y versus x as lines and/or markers. @@ -1489,7 +1545,8 @@ def plot(self, *args, scalex=True, scaley=True, data=None, **kwargs): ---------------- scalex, scaley : bool, default: True These parameters determine if the view limits are adapted to the - data limits. The values are passed on to `autoscale_view`. + data limits. The values are passed on to + `~.axes.Axes.autoscale_view`. **kwargs : `.Line2D` properties, optional *kwargs* are used to specify properties like a line label (for @@ -1505,7 +1562,7 @@ def plot(self, *args, scalex=True, scaley=True, data=None, **kwargs): Here is a list of available `.Line2D` properties: - %(Line2D_kwdoc)s + %(Line2D:kwdoc)s See Also -------- @@ -1605,15 +1662,30 @@ def plot(self, *args, scalex=True, scaley=True, data=None, **kwargs): lines = [*self._get_lines(*args, data=data, **kwargs)] for line in lines: self.add_line(line) - self._request_autoscale_view(scalex=scalex, scaley=scaley) + if scalex: + self._request_autoscale_view("x") + if scaley: + self._request_autoscale_view("y") return lines @_preprocess_data(replace_names=["x", "y"], label_namer="y") - @docstring.dedent_interpd + @_docstring.dedent_interpd def plot_date(self, x, y, fmt='o', tz=None, xdate=True, ydate=False, **kwargs): """ - Plot co-ercing the axis to treat floats as dates. + [*Discouraged*] Plot coercing the axis to treat floats as dates. + + .. admonition:: Discouraged + + This method exists for historic reasons and will 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`. Similar to `.plot`, this plots *y* vs. *x* as lines or markers. However, the axis labels are formatted as dates depending on *xdate* @@ -1642,15 +1714,17 @@ def plot_date(self, x, y, fmt='o', tz=None, xdate=True, ydate=False, Returns ------- - list of `~.Line2D` + list of `.Line2D` Objects representing the plotted data. Other Parameters ---------------- + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER **kwargs Keyword arguments control the `.Line2D` properties: - %(Line2D_kwdoc)s + %(Line2D:kwdoc)s See Also -------- @@ -1676,7 +1750,7 @@ def plot_date(self, x, y, fmt='o', tz=None, xdate=True, ydate=False, return self.plot(x, y, fmt, **kwargs) # @_preprocess_data() # let 'plot' do the unpacking.. - @docstring.dedent_interpd + @_docstring.dedent_interpd def loglog(self, *args, **kwargs): """ Make a plot with log scaling on both the x and y axis. @@ -1710,15 +1784,13 @@ def loglog(self, *args, **kwargs): Non-positive values can be masked as invalid, or clipped to a very small positive number. + **kwargs + All parameters supported by `.plot`. + Returns ------- - list of `~.Line2D` + list of `.Line2D` Objects representing the plotted data. - - Other Parameters - ---------------- - **kwargs - All parameters supported by `.plot`. """ dx = {k: v for k, v in kwargs.items() if k in ['base', 'subs', 'nonpositive', @@ -1732,7 +1804,7 @@ def loglog(self, *args, **kwargs): *args, **{k: v for k, v in kwargs.items() if k not in {*dx, *dy}}) # @_preprocess_data() # let 'plot' do the unpacking.. - @docstring.dedent_interpd + @_docstring.dedent_interpd def semilogx(self, *args, **kwargs): """ Make a plot with log scaling on the x axis. @@ -1763,15 +1835,13 @@ def semilogx(self, *args, **kwargs): Non-positive values in x can be masked as invalid, or clipped to a very small positive number. + **kwargs + All parameters supported by `.plot`. + Returns ------- - list of `~.Line2D` + list of `.Line2D` Objects representing the plotted data. - - Other Parameters - ---------------- - **kwargs - All parameters supported by `.plot`. """ d = {k: v for k, v in kwargs.items() if k in ['base', 'subs', 'nonpositive', @@ -1781,7 +1851,7 @@ def semilogx(self, *args, **kwargs): *args, **{k: v for k, v in kwargs.items() if k not in d}) # @_preprocess_data() # let 'plot' do the unpacking.. - @docstring.dedent_interpd + @_docstring.dedent_interpd def semilogy(self, *args, **kwargs): """ Make a plot with log scaling on the y axis. @@ -1812,15 +1882,13 @@ def semilogy(self, *args, **kwargs): Non-positive values in y can be masked as invalid, or clipped to a very small positive number. + **kwargs + All parameters supported by `.plot`. + Returns ------- - list of `~.Line2D` + list of `.Line2D` Objects representing the plotted data. - - Other Parameters - ---------------- - **kwargs - All parameters supported by `.plot`. """ d = {k: v for k, v in kwargs.items() if k in ['base', 'subs', 'nonpositive', @@ -1886,6 +1954,9 @@ def acorr(self, x, **kwargs): The marker for plotting the data points. Only used if *usevlines* is ``False``. + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER + **kwargs Additional parameters are passed to `.Axes.vlines` and `.Axes.axhline` if *usevlines* is ``True``; otherwise they are @@ -1960,6 +2031,9 @@ def xcorr(self, x, y, normed=True, detrend=mlab.detrend_none, The marker for plotting the data points. Only used if *usevlines* is ``False``. + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER + **kwargs Additional parameters are passed to `.Axes.vlines` and `.Axes.axhline` if *usevlines* is ``True``; otherwise they are @@ -2048,10 +2122,6 @@ def step(self, x, y, *args, where='pre', data=None, **kwargs): and plotted on the given positions, however, this is a rarely needed feature for step plots. - data : indexable object, optional - An object with labelled data. If given, provide the label names to - plot in *x* and *y*. - where : {'pre', 'post', 'mid'}, default: 'pre' Define where the steps should be placed: @@ -2063,19 +2133,17 @@ def step(self, x, y, *args, where='pre', data=None, **kwargs): value ``y[i]``. - 'mid': Steps occur half-way between the *x* positions. - Returns - ------- - list of `.Line2D` - Objects representing the plotted data. + data : indexable object, optional + An object with labelled data. If given, provide the label names to + plot in *x* and *y*. - Other Parameters - ---------------- **kwargs Additional parameters are the same as those for `.plot`. - Notes - ----- - .. [notes section required to get data note injection right] + Returns + ------- + list of `.Line2D` + Objects representing the plotted data. """ _api.check_in_list(('pre', 'post', 'mid'), where=where) kwargs['drawstyle'] = 'steps-' + where @@ -2111,14 +2179,22 @@ def _convert_dx(dx, x0, xconv, convert): # removes the units from unit packages like `pint` that # wrap numpy arrays. try: - x0 = cbook.safe_first_element(x0) + x0 = cbook._safe_first_finite(x0) except (TypeError, IndexError, KeyError): - x0 = x0 + pass + except StopIteration: + # this means we found no finite element, fall back to first + # element unconditionally + x0 = cbook.safe_first_element(x0) try: - x = cbook.safe_first_element(xconv) + x = cbook._safe_first_finite(xconv) except (TypeError, IndexError, KeyError): x = xconv + except StopIteration: + # this means we found no finite element, fall back to first + # element unconditionally + x = cbook.safe_first_element(xconv) delist = False if not np.iterable(dx): @@ -2134,7 +2210,7 @@ def _convert_dx(dx, x0, xconv, convert): return dx @_preprocess_data() - @docstring.dedent_interpd + @_docstring.dedent_interpd def bar(self, x, height, width=0.8, bottom=None, *, align="center", **kwargs): r""" @@ -2160,7 +2236,7 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center", The width(s) of the bars. bottom : float or array-like, default: 0 - The y coordinate(s) of the bars bases. + The y coordinate(s) of the bottom side(s) of the bars. align : {'center', 'edge'}, default: 'center' Alignment of the bars to the *x* coordinates: @@ -2191,6 +2267,14 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center", The tick labels of the bars. Default: None (Use default numeric labels.) + label : str or list of str, optional + A single label is attached to the resulting `.BarContainer` as a + label for the whole dataset. + If a list is provided, it must be the same length as *x* and + labels the individual bars. Repeated labels are not de-duplicated + and will cause repeated label entries, so this is best used when + bars also differ in style (e.g., by passing a list to *color*.) + xerr, yerr : float or array-like of shape(N,) or shape(2, N), optional If not *None*, add horizontal / vertical errorbars to the bar tips. The values are +/- sizes relative to the data: @@ -2202,8 +2286,8 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center", errors. - *None*: No errorbar. (Default) - See :doc:`/gallery/statistics/errorbar_features` - for an example on the usage of ``xerr`` and ``yerr``. + See :doc:`/gallery/statistics/errorbar_features` for an example on + the usage of *xerr* and *yerr*. ecolor : color or list of color, default: 'black' The line color of the errorbars. @@ -2212,16 +2296,19 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center", The length of the error bar caps in points. error_kw : dict, optional - Dictionary of kwargs to be passed to the `~.Axes.errorbar` - method. Values of *ecolor* or *capsize* defined here take - precedence over the independent kwargs. + Dictionary of keyword arguments to be passed to the + `~.Axes.errorbar` method. Values of *ecolor* or *capsize* defined + here take precedence over the independent keyword arguments. log : bool, default: False If *True*, set the y-axis to be log scale. + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER + **kwargs : `.Rectangle` properties - %(Rectangle_kwdoc)s + %(Rectangle:kwdoc)s See Also -------- @@ -2254,7 +2341,7 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center", ezorder += 0.01 error_kw.setdefault('zorder', ezorder) ecolor = kwargs.pop('ecolor', 'k') - capsize = kwargs.pop('capsize', rcParams["errorbar.capsize"]) + capsize = kwargs.pop('capsize', mpl.rcParams["errorbar.capsize"]) error_kw.setdefault('ecolor', ecolor) error_kw.setdefault('capsize', capsize) @@ -2271,7 +2358,7 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center", if orientation == 'vertical': if y is None: y = 0 - elif orientation == 'horizontal': + else: # horizontal if x is None: x = 0 @@ -2280,7 +2367,7 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center", [("x", x), ("y", height)], kwargs, convert=False) if log: self.set_yscale('log', nonpositive='clip') - elif orientation == 'horizontal': + else: # horizontal self._process_unit_info( [("x", width), ("y", y)], kwargs, convert=False) if log: @@ -2309,10 +2396,20 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center", if orientation == 'vertical': tick_label_axis = self.xaxis tick_label_position = x - elif orientation == 'horizontal': + else: # horizontal tick_label_axis = self.yaxis tick_label_position = y + if not isinstance(label, str) and np.iterable(label): + bar_container_label = '_nolegend_' + patch_labels = label + else: + bar_container_label = label + patch_labels = ['_nolegend_'] * len(x) + if len(patch_labels) != len(x): + raise ValueError(f'number of labels ({len(patch_labels)}) ' + f'does not match number of bars ({len(x)}).') + linewidth = itertools.cycle(np.atleast_1d(linewidth)) hatch = itertools.cycle(np.atleast_1d(hatch)) color = itertools.chain(itertools.cycle(mcolors.to_rgba_array(color)), @@ -2338,7 +2435,7 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center", f'and width ({width.dtype}) ' f'are incompatible') from e bottom = y - elif orientation == 'horizontal': + else: # horizontal try: bottom = y - height / 2 except TypeError as e: @@ -2346,27 +2443,27 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center", f'and height ({height.dtype}) ' f'are incompatible') from e left = x - elif align == 'edge': + else: # edge left = x bottom = y patches = [] args = zip(left, bottom, width, height, color, edgecolor, linewidth, - hatch) - for l, b, w, h, c, e, lw, htch in args: + hatch, patch_labels) + for l, b, w, h, c, e, lw, htch, lbl in args: r = mpatches.Rectangle( xy=(l, b), width=w, height=h, facecolor=c, edgecolor=e, linewidth=lw, - label='_nolegend_', + label=lbl, hatch=htch, ) - r.update(kwargs) + r._internal_update(kwargs) r.get_path()._interpolation_steps = 100 if orientation == 'vertical': r.sticky_edges.y.append(b) - elif orientation == 'horizontal': + else: # horizontal r.sticky_edges.x.append(l) self.add_patch(r) patches.append(r) @@ -2377,7 +2474,7 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center", ex = [l + 0.5 * w for l, w in zip(left, width)] ey = [b + h for b, h in zip(bottom, height)] - elif orientation == 'horizontal': + else: # horizontal # using list comps rather than arrays to preserve unit info ex = [l + w for l, w in zip(left, width)] ey = [b + 0.5 * h for b, h in zip(bottom, height)] @@ -2394,11 +2491,12 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center", if orientation == 'vertical': datavalues = height - elif orientation == 'horizontal': + else: # horizontal datavalues = width bar_container = BarContainer(patches, errorbar, datavalues=datavalues, - orientation=orientation, label=label) + orientation=orientation, + label=bar_container_label) self.add_container(bar_container) if tick_labels is not None: @@ -2408,9 +2506,10 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center", return bar_container - @docstring.dedent_interpd + # @_preprocess_data() # let 'bar' do the unpacking.. + @_docstring.dedent_interpd def barh(self, y, width, height=0.8, left=None, *, align="center", - **kwargs): + data=None, **kwargs): r""" Make a horizontal bar plot. @@ -2434,7 +2533,7 @@ def barh(self, y, width, height=0.8, left=None, *, align="center", The heights of the bars. left : float or array-like, default: 0 - The x coordinates of the left sides of the bars. + The x coordinates of the left side(s) of the bars. align : {'center', 'edge'}, default: 'center' Alignment of the base to the *y* coordinates*: @@ -2466,9 +2565,17 @@ def barh(self, y, width, height=0.8, left=None, *, align="center", The tick labels of the bars. Default: None (Use default numeric labels.) + label : str or list of str, optional + A single label is attached to the resulting `.BarContainer` as a + label for the whole dataset. + If a list is provided, it must be the same length as *y* and + labels the individual bars. Repeated labels are not de-duplicated + and will cause repeated label entries, so this is best used when + bars also differ in style (e.g., by passing a list to *color*.) + xerr, yerr : float or array-like of shape(N,) or shape(2, N), optional - If not ``None``, add horizontal / vertical errorbars to the - bar tips. The values are +/- sizes relative to the data: + If not *None*, add horizontal / vertical errorbars to the bar tips. + The values are +/- sizes relative to the data: - scalar: symmetric +/- values for all bars - shape(N,): symmetric +/- values for each bar @@ -2477,8 +2584,8 @@ def barh(self, y, width, height=0.8, left=None, *, align="center", errors. - *None*: No errorbar. (default) - See :doc:`/gallery/statistics/errorbar_features` - for an example on the usage of ``xerr`` and ``yerr``. + See :doc:`/gallery/statistics/errorbar_features` for an example on + the usage of *xerr* and *yerr*. ecolor : color or list of color, default: 'black' The line color of the errorbars. @@ -2487,16 +2594,20 @@ def barh(self, y, width, height=0.8, left=None, *, align="center", The length of the error bar caps in points. error_kw : dict, optional - Dictionary of kwargs to be passed to the `~.Axes.errorbar` - method. Values of *ecolor* or *capsize* defined here take - precedence over the independent kwargs. + Dictionary of keyword arguments to be passed to the + `~.Axes.errorbar` method. Values of *ecolor* or *capsize* defined + here take precedence over the independent keyword arguments. log : bool, default: False If ``True``, set the x-axis to be log scale. + data : indexable object, optional + If given, all parameters also accept a string ``s``, which is + interpreted as ``data[s]`` (unless this raises an exception). + **kwargs : `.Rectangle` properties - %(Rectangle_kwdoc)s + %(Rectangle:kwdoc)s See Also -------- @@ -2506,12 +2617,11 @@ def barh(self, y, width, height=0.8, left=None, *, align="center", ----- Stacked bars can be achieved by passing individual *left* values per bar. See - :doc:`/gallery/lines_bars_and_markers/horizontal_barchart_distribution` - . + :doc:`/gallery/lines_bars_and_markers/horizontal_barchart_distribution`. """ kwargs.setdefault('orientation', 'horizontal') patches = self.bar(x=left, height=height, width=width, bottom=y, - align=align, **kwargs) + align=align, data=data, **kwargs) return patches def bar_label(self, container, labels=None, *, fmt="%g", label_type="edge", @@ -2550,13 +2660,25 @@ def bar_label(self, container, labels=None, *, fmt="%g", label_type="edge", **kwargs Any remaining keyword arguments are passed through to - `.Axes.annotate`. + `.Axes.annotate`. The alignment parameters ( + *horizontalalignment* / *ha*, *verticalalignment* / *va*) are + not supported because the labels are automatically aligned to + the bars. Returns ------- list of `.Text` A list of `.Text` instances for the labels. """ + for key in ['horizontalalignment', 'ha', 'verticalalignment', 'va']: + if key in kwargs: + raise ValueError( + f"Passing {key!r} to bar_label() is not supported.") + + a, b = self.yaxis.get_view_interval() + y_inverted = a > b + c, d = self.xaxis.get_view_interval() + x_inverted = c > d # want to know whether to put label on positive or negative direction # cannot use np.sign here because it will return 0 if x == 0 @@ -2585,7 +2707,7 @@ def sign(x): annotations = [] for bar, err, dat, lbl in itertools.zip_longest( - bars, errs, datavalues, labels + bars, errs, datavalues, labels ): (x0, y0), (x1, y1) = bar.get_bbox().get_points() xc, yc = (x0 + x1) / 2, (y0 + y1) / 2 @@ -2593,45 +2715,55 @@ def sign(x): if orientation == "vertical": extrema = max(y0, y1) if dat >= 0 else min(y0, y1) length = abs(y0 - y1) - elif orientation == "horizontal": + else: # horizontal extrema = max(x0, x1) if dat >= 0 else min(x0, x1) length = abs(x0 - x1) - if err is None: + if err is None or np.size(err) == 0: endpt = extrema elif orientation == "vertical": endpt = err[:, 1].max() if dat >= 0 else err[:, 1].min() - elif orientation == "horizontal": + else: # horizontal endpt = err[:, 0].max() if dat >= 0 else err[:, 0].min() if label_type == "center": value = sign(dat) * length - elif label_type == "edge": + else: # edge value = extrema if label_type == "center": xy = xc, yc - elif label_type == "edge" and orientation == "vertical": - xy = xc, endpt - elif label_type == "edge" and orientation == "horizontal": - xy = endpt, yc + else: # edge + if orientation == "vertical": + xy = xc, endpt + else: # horizontal + xy = endpt, yc if orientation == "vertical": - xytext = 0, sign(dat) * padding - else: - xytext = sign(dat) * padding, 0 + y_direction = -1 if y_inverted else 1 + xytext = 0, y_direction * sign(dat) * padding + else: # horizontal + x_direction = -1 if x_inverted else 1 + xytext = x_direction * sign(dat) * padding, 0 if label_type == "center": ha, va = "center", "center" - elif label_type == "edge": - if orientation == "vertical" and dat >= 0: - ha, va = "center", "bottom" - elif orientation == "vertical" and dat < 0: - ha, va = "center", "top" - elif orientation == "horizontal" and dat >= 0: - ha, va = "left", "center" - elif orientation == "horizontal" and dat < 0: - ha, va = "right", "center" + else: # edge + if orientation == "vertical": + ha = 'center' + if y_inverted: + va = 'top' if dat > 0 else 'bottom' # also handles NaN + else: + va = 'top' if dat < 0 else 'bottom' # also handles NaN + else: # horizontal + if x_inverted: + ha = 'right' if dat > 0 else 'left' # also handles NaN + else: + ha = 'right' if dat < 0 else 'left' # also handles NaN + va = 'center' + + if np.isnan(dat): + lbl = '' annotation = self.annotate(fmt % value if lbl is None else lbl, xy, xytext, textcoords="offset points", @@ -2641,7 +2773,7 @@ def sign(x): return annotations @_preprocess_data() - @docstring.dedent_interpd + @_docstring.dedent_interpd def broken_barh(self, xranges, yrange, **kwargs): """ Plot a horizontal sequence of rectangles. @@ -2668,6 +2800,8 @@ def broken_barh(self, xranges, yrange, **kwargs): Other Parameters ---------------- + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER **kwargs : `.BrokenBarHCollection` properties Each *kwarg* can be either a single argument applying to all @@ -2683,15 +2817,15 @@ def broken_barh(self, xranges, yrange, **kwargs): Supported keywords: - %(BrokenBarHCollection_kwdoc)s + %(BrokenBarHCollection:kwdoc)s """ # process the unit information if len(xranges): - xdata = cbook.safe_first_element(xranges) + xdata = cbook._safe_first_finite(xranges) else: xdata = None if len(yrange): - ydata = cbook.safe_first_element(yrange) + ydata = cbook._safe_first_finite(yrange) else: ydata = None self._process_unit_info( @@ -2715,6 +2849,7 @@ def broken_barh(self, xranges, yrange, **kwargs): return col @_preprocess_data() + @_api.delete_parameter("3.6", "use_line_collection") def stem(self, *args, linefmt=None, markerfmt=None, basefmt=None, bottom=0, label=None, use_line_collection=True, orientation='vertical'): """ @@ -2732,6 +2867,8 @@ def stem(self, *args, linefmt=None, markerfmt=None, basefmt=None, bottom=0, The *locs*-positions are optional. The formats may be provided either as positional or as keyword-arguments. + Passing *markerfmt* and *basefmt* positionally is deprecated since + Matplotlib 3.5. Parameters ---------- @@ -2764,8 +2901,8 @@ def stem(self, *args, linefmt=None, markerfmt=None, basefmt=None, bottom=0, markerfmt : str, optional A string defining the color and/or shape of the markers at the stem - heads. Default: 'C0o', i.e. filled circles with the first color of - the color cycle. + heads. If the marker is not given, use the marker 'o', i.e. filled + circles. If the color is not given, use the color from *linefmt*. basefmt : str, default: 'C3-' ('C2-' in classic mode) A format string defining the properties of the baseline. @@ -2781,11 +2918,15 @@ def stem(self, *args, linefmt=None, markerfmt=None, basefmt=None, bottom=0, The label to use for the stems in legends. use_line_collection : bool, default: True + *Deprecated since 3.6* + If ``True``, store and plot the stem lines as a `~.collections.LineCollection` instead of individual lines, which significantly increases performance. If ``False``, defaults to the - old behavior of using a list of `.Line2D` objects. This parameter - may be deprecated in the future. + old behavior of using a list of `.Line2D` objects. + + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER Returns ------- @@ -2809,65 +2950,54 @@ def stem(self, *args, linefmt=None, markerfmt=None, basefmt=None, bottom=0, heads, = args locs = np.arange(len(heads)) args = () + elif isinstance(args[1], str): + heads, *args = args + locs = np.arange(len(heads)) else: locs, heads, *args = args + if len(args) > 1: + _api.warn_deprecated( + "3.5", + message="Passing the markerfmt parameter positionally is " + "deprecated since Matplotlib %(since)s; the " + "parameter will become keyword-only %(removal)s.") if orientation == 'vertical': locs, heads = self._process_unit_info([("x", locs), ("y", heads)]) - else: + else: # horizontal heads, locs = self._process_unit_info([("x", heads), ("y", locs)]) - # defaults for formats + # resolve line format if linefmt is None: - try: - # fallback to positional argument - linefmt = args[0] - except IndexError: - linecolor = 'C0' - linemarker = 'None' - linestyle = '-' - else: - linestyle, linemarker, linecolor = \ - _process_plot_format(linefmt) - else: - linestyle, linemarker, linecolor = _process_plot_format(linefmt) + linefmt = args[0] if len(args) > 0 else "C0-" + linestyle, linemarker, linecolor = _process_plot_format(linefmt) + # resolve marker format if markerfmt is None: - try: - # fallback to positional argument - markerfmt = args[1] - except IndexError: - markercolor = 'C0' - markermarker = 'o' - markerstyle = 'None' - else: - markerstyle, markermarker, markercolor = \ - _process_plot_format(markerfmt) - else: - markerstyle, markermarker, markercolor = \ - _process_plot_format(markerfmt) - + # if not given as kwarg, check for positional or fall back to 'o' + markerfmt = args[1] if len(args) > 1 else "o" + if markerfmt == '': + markerfmt = ' ' # = empty line style; '' would resolve rcParams + markerstyle, markermarker, markercolor = \ + _process_plot_format(markerfmt) + if markermarker is None: + markermarker = 'o' + if markerstyle is None: + markerstyle = 'None' + if markercolor is None: + markercolor = linecolor + + # resolve baseline format if basefmt is None: - try: - # fallback to positional argument - basefmt = args[2] - except IndexError: - if rcParams['_internal.classic_mode']: - basecolor = 'C2' - else: - basecolor = 'C3' - basemarker = 'None' - basestyle = '-' - else: - basestyle, basemarker, basecolor = \ - _process_plot_format(basefmt) - else: - basestyle, basemarker, basecolor = _process_plot_format(basefmt) + basefmt = (args[2] if len(args) > 2 else + "C2-" if mpl.rcParams["_internal.classic_mode"] else + "C3-") + basestyle, basemarker, basecolor = _process_plot_format(basefmt) # New behaviour in 3.1 is to use a LineCollection for the stemlines if use_line_collection: if linestyle is None: - linestyle = rcParams['lines.linestyle'] + linestyle = mpl.rcParams['lines.linestyle'] xlines = self.vlines if orientation == "vertical" else self.hlines stemlines = xlines( locs, bottom, heads, @@ -2916,14 +3046,12 @@ def pie(self, x, explode=None, labels=None, colors=None, autopct=None, pctdistance=0.6, shadow=False, labeldistance=1.1, startangle=0, radius=1, counterclock=True, wedgeprops=None, textprops=None, center=(0, 0), - frame=False, rotatelabels=False, *, normalize=None): + frame=False, rotatelabels=False, *, normalize=True): """ Plot a pie chart. Make a pie chart of array *x*. The fractional area of each wedge is - given by ``x/sum(x)``. If ``sum(x) < 1``, then the values of *x* give - the fractional area directly and the array will not be normalized. The - resulting pie will have an empty wedge of size ``1 - sum(x)``. + given by ``x/sum(x)``. The wedges are plotted counterclockwise, by default starting from the x-axis. @@ -2957,19 +3085,11 @@ def pie(self, x, explode=None, labels=None, colors=None, shadow : bool, default: False Draw a shadow beneath the pie. - normalize : None or bool, default: None + normalize : bool, default: True When *True*, always make a full pie by normalizing x so that ``sum(x) == 1``. *False* makes a partial pie if ``sum(x) <= 1`` and raises a `ValueError` for ``sum(x) > 1``. - When *None*, defaults to *True* if ``sum(x) >= 1`` and *False* if - ``sum(x) < 1``. - - Please note that the previous default value of *None* is now - deprecated, and the default will change to *True* in the next - release. Please pass ``normalize=False`` explicitly if you want to - draw a partial pie. - labeldistance : float or None, default: 1.1 The radial distance at which the pie labels are drawn. If set to ``None``, label are not drawn, but are stored for use in @@ -3004,6 +3124,9 @@ def pie(self, x, explode=None, labels=None, colors=None, rotatelabels : bool, default: False Rotate each label to the angle of the corresponding slice if true. + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER + Returns ------- patches : list @@ -3035,17 +3158,6 @@ def pie(self, x, explode=None, labels=None, colors=None, sx = x.sum() - if normalize is None: - if sx < 1: - _api.warn_deprecated( - "3.3", message="normalize=None does not normalize " - "if the sum is less than 1 but this behavior " - "is deprecated since %(since)s until %(removal)s. " - "After the deprecation " - "period the default value will be normalize=True. " - "To prevent normalization pass normalize=False ") - else: - normalize = True if normalize: x = x / sx elif sx > 1: @@ -3066,20 +3178,11 @@ def pie(self, x, explode=None, labels=None, colors=None, def get_next_color(): return next(color_cycle) - if radius is None: - _api.warn_deprecated( - "3.3", message="Support for passing a radius of None to mean " - "1 is deprecated since %(since)s and will be removed " - "%(removal)s.") - radius = 1 + _api.check_isinstance(Number, radius=radius, startangle=startangle) + if radius <= 0: + raise ValueError(f'radius must be a positive number, not {radius}') # Starting theta1 is the start fraction of the circle - if startangle is None: - _api.warn_deprecated( - "3.3", message="Support for passing a startangle of None to " - "mean 0 is deprecated since %(since)s and will be removed " - "%(removal)s.") - startangle = 0 theta1 = startangle / 360 if wedgeprops is None: @@ -3128,7 +3231,7 @@ def get_next_color(): horizontalalignment=label_alignment_h, verticalalignment=label_alignment_v, rotation=label_rotation, - size=rcParams['xtick.labelsize']) + size=mpl.rcParams['xtick.labelsize']) t.set(**textprops) texts.append(t) @@ -3163,9 +3266,41 @@ def get_next_color(): else: return slices, texts, autotexts + @staticmethod + def _errorevery_to_mask(x, errorevery): + """ + Normalize `errorbar`'s *errorevery* to be a boolean mask for data *x*. + + This function is split out to be usable both by 2D and 3D errorbars. + """ + if isinstance(errorevery, Integral): + errorevery = (0, errorevery) + if isinstance(errorevery, tuple): + if (len(errorevery) == 2 and + isinstance(errorevery[0], Integral) and + isinstance(errorevery[1], Integral)): + errorevery = slice(errorevery[0], None, errorevery[1]) + else: + raise ValueError( + f'{errorevery=!r} is a not a tuple of two integers') + elif isinstance(errorevery, slice): + pass + elif not isinstance(errorevery, str) and np.iterable(errorevery): + try: + x[errorevery] # fancy indexing + except (ValueError, IndexError) as err: + raise ValueError( + f"{errorevery=!r} is iterable but not a valid NumPy fancy " + "index to match 'xerr'/'yerr'") from err + else: + raise ValueError(f"{errorevery=!r} is not a recognized value") + everymask = np.zeros(len(x), bool) + everymask[errorevery] = True + return everymask + @_preprocess_data(replace_names=["x", "y", "xerr", "yerr"], label_namer="y") - @docstring.dedent_interpd + @_docstring.dedent_interpd def errorbar(self, x, y, yerr=None, xerr=None, fmt='', ecolor=None, elinewidth=None, capsize=None, barsabove=False, lolims=False, uplims=False, @@ -3193,7 +3328,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``. @@ -3259,6 +3394,9 @@ def errorbar(self, x, y, yerr=None, xerr=None, Other Parameters ---------------- + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER + **kwargs All other keyword arguments are passed on to the `~.Axes.plot` call drawing the markers. For example, this code makes big red squares @@ -3272,63 +3410,87 @@ def errorbar(self, x, y, yerr=None, xerr=None, property names, *markerfacecolor*, *markeredgecolor*, *markersize* and *markeredgewidth*. - Valid kwargs for the marker properties are `.Line2D` properties: - - %(Line2D_kwdoc)s + Valid kwargs for the marker properties are: + + - *dashes* + - *dash_capstyle* + - *dash_joinstyle* + - *drawstyle* + - *fillstyle* + - *linestyle* + - *marker* + - *markeredgecolor* + - *markeredgewidth* + - *markerfacecolor* + - *markerfacecoloralt* + - *markersize* + - *markevery* + - *solid_capstyle* + - *solid_joinstyle* + + Refer to the corresponding `.Line2D` property for more details: + + %(Line2D:kwdoc)s """ kwargs = cbook.normalize_kwargs(kwargs, mlines.Line2D) - # anything that comes in as 'None', drop so the default thing - # happens down stream + # Drop anything that comes in as None to use the default instead. kwargs = {k: v for k, v in kwargs.items() if v is not None} kwargs.setdefault('zorder', 2) - self._process_unit_info([("x", x), ("y", y)], kwargs, convert=False) + # Casting to object arrays preserves units. + if not isinstance(x, np.ndarray): + x = np.asarray(x, dtype=object) + if not isinstance(y, np.ndarray): + y = np.asarray(y, dtype=object) - # Make sure all the args are iterable; use lists not arrays to preserve - # units. - if not np.iterable(x): - x = [x] - - if not np.iterable(y): - y = [y] - - if len(x) != len(y): - raise ValueError("'x' and 'y' must have the same size") + def _upcast_err(err): + """ + Safely handle tuple of containers that carry units. - if xerr is not None: - if not np.iterable(xerr): - xerr = [xerr] * len(x) + This function covers the case where the input to the xerr/yerr is a + length 2 tuple of equal length ndarray-subclasses that carry the + unit information in the container. - if yerr is not None: - if not np.iterable(yerr): - yerr = [yerr] * len(y) + If we have a tuple of nested numpy array (subclasses), we defer + coercing the units to be consistent to the underlying unit + library (and implicitly the broadcasting). - if isinstance(errorevery, Integral): - errorevery = (0, errorevery) - if isinstance(errorevery, tuple): - if (len(errorevery) == 2 and - isinstance(errorevery[0], Integral) and - isinstance(errorevery[1], Integral)): - errorevery = slice(errorevery[0], None, errorevery[1]) - else: - raise ValueError( - f'errorevery={errorevery!r} is a not a tuple of two ' - f'integers') + Otherwise, fallback to casting to an object array. + """ - elif isinstance(errorevery, slice): - pass + if ( + # make sure it is not a scalar + np.iterable(err) and + # and it is not empty + len(err) > 0 and + # and the first element is an array sub-class use + # safe_first_element because getitem is index-first not + # location first on pandas objects so err[0] almost always + # fails. + isinstance(cbook._safe_first_finite(err), np.ndarray) + ): + # Get the type of the first element + atype = type(cbook._safe_first_finite(err)) + # Promote the outer container to match the inner container + if atype is np.ndarray: + # Converts using np.asarray, because data cannot + # be directly passed to init of np.ndarray + return np.asarray(err, dtype=object) + # If atype is not np.ndarray, directly pass data to init. + # This works for types such as unyts and astropy units + return atype(err) + # Otherwise wrap it in an object array + return np.asarray(err, dtype=object) + + if xerr is not None and not isinstance(xerr, np.ndarray): + xerr = _upcast_err(xerr) + if yerr is not None and not isinstance(yerr, np.ndarray): + yerr = _upcast_err(yerr) + x, y = np.atleast_1d(x, y) # Make sure all the args are iterable. + if len(x) != len(y): + raise ValueError("'x' and 'y' must have the same size") - elif not isinstance(errorevery, str) and np.iterable(errorevery): - # fancy indexing - try: - x[errorevery] - except (ValueError, IndexError) as err: - raise ValueError( - f"errorevery={errorevery!r} is iterable but not a valid " - f"NumPy fancy index to match 'xerr'/'yerr'") from err - else: - raise ValueError( - f"errorevery={errorevery!r} is not a recognized value") + everymask = self._errorevery_to_mask(x, errorevery) label = kwargs.pop("label", None) kwargs['label'] = '_nolegend_' @@ -3363,20 +3525,20 @@ def errorbar(self, x, y, yerr=None, xerr=None, if ecolor is None: ecolor = base_style['color'] - # Eject any marker information from line format string, as it's not + # Eject any line-specific information from format string, as it's not # needed for bars or caps. - base_style.pop('marker', None) - base_style.pop('markersize', None) - base_style.pop('markerfacecolor', None) - base_style.pop('markeredgewidth', None) - base_style.pop('markeredgecolor', None) - base_style.pop('markevery', None) - base_style.pop('linestyle', None) + for key in ['marker', 'markersize', 'markerfacecolor', + 'markerfacecoloralt', + 'markeredgewidth', 'markeredgecolor', 'markevery', + 'linestyle', 'fillstyle', 'drawstyle', 'dash_capstyle', + 'dash_joinstyle', 'solid_capstyle', 'solid_joinstyle', + 'dashes']: + base_style.pop(key, None) # Make the style dict for the line collections (the bars). eb_lines_style = {**base_style, 'color': ecolor} - if elinewidth: + if elinewidth is not None: eb_lines_style['linewidth'] = elinewidth elif 'linewidth' in kwargs: eb_lines_style['linewidth'] = kwargs['linewidth'] @@ -3388,7 +3550,7 @@ def errorbar(self, x, y, yerr=None, xerr=None, # Make the style dict for caps (the "hats"). eb_cap_style = {**base_style, 'linestyle': 'none'} if capsize is None: - capsize = rcParams["errorbar.capsize"] + capsize = mpl.rcParams["errorbar.capsize"] if capsize > 0: eb_cap_style['markersize'] = 2. * capsize if capthick is not None: @@ -3405,127 +3567,71 @@ def errorbar(self, x, y, yerr=None, xerr=None, barcols = [] caplines = [] - # arrays fine here, they are booleans and hence not units - lolims = np.broadcast_to(lolims, len(x)).astype(bool) - uplims = np.broadcast_to(uplims, len(x)).astype(bool) - xlolims = np.broadcast_to(xlolims, len(x)).astype(bool) - xuplims = np.broadcast_to(xuplims, len(x)).astype(bool) - - everymask = np.zeros(len(x), bool) - everymask[errorevery] = True - - def apply_mask(arrays, mask): - # Return, for each array in *arrays*, the elements for which *mask* - # is True, without using fancy indexing. - return [[*itertools.compress(array, mask)] for array in arrays] - - def extract_err(name, err, data, lolims, uplims): - """ - Private function to compute error bars. - - Parameters - ---------- - name : {'x', 'y'} - Name used in the error message. - err : array-like - xerr or yerr from errorbar(). - data : array-like - x or y from errorbar(). - lolims : array-like - Error is only applied on **upper** side when this is True. See - the note in the main docstring about this parameter's name. - uplims : array-like - Error is only applied on **lower** side when this is True. See - the note in the main docstring about this parameter's name. - """ - try: # Asymmetric error: pair of 1D iterables. - a, b = err - iter(a) - iter(b) - except (TypeError, ValueError): - a = b = err # Symmetric error: 1D iterable. - if np.ndim(a) > 1 or np.ndim(b) > 1: + # Vectorized fancy-indexer. + def apply_mask(arrays, mask): return [array[mask] for array in arrays] + + # dep: dependent dataset, indep: independent dataset + for (dep_axis, dep, err, lolims, uplims, indep, lines_func, + marker, lomarker, himarker) in [ + ("x", x, xerr, xlolims, xuplims, y, self.hlines, + "|", mlines.CARETRIGHTBASE, mlines.CARETLEFTBASE), + ("y", y, yerr, lolims, uplims, x, self.vlines, + "_", mlines.CARETUPBASE, mlines.CARETDOWNBASE), + ]: + if err is None: + continue + lolims = np.broadcast_to(lolims, len(dep)).astype(bool) + uplims = np.broadcast_to(uplims, len(dep)).astype(bool) + try: + np.broadcast_to(err, (2, len(dep))) + except ValueError: raise ValueError( - f"{name}err must be a scalar or a 1D or (2, n) array-like") - # Using list comprehensions rather than arrays to preserve units. - for e in [a, b]: - if len(data) != len(e): - raise ValueError( - f"The lengths of the data ({len(data)}) and the " - f"error {len(e)} do not match") - low = [v if lo else v - e for v, e, lo in zip(data, a, lolims)] - high = [v if up else v + e for v, e, up in zip(data, b, uplims)] - return low, high - - if xerr is not None: - left, right = extract_err('x', xerr, x, xlolims, xuplims) - barcols.append(self.hlines( - *apply_mask([y, left, right], everymask), **eb_lines_style)) - # select points without upper/lower limits in x and - # draw normal errorbars for these points - noxlims = ~(xlolims | xuplims) - if noxlims.any() and capsize > 0: - yo, lo, ro = apply_mask([y, left, right], noxlims & everymask) - caplines.extend([ - mlines.Line2D(lo, yo, marker='|', **eb_cap_style), - mlines.Line2D(ro, yo, marker='|', **eb_cap_style)]) - if xlolims.any(): - xo, yo, ro = apply_mask([x, y, right], xlolims & everymask) - if self.xaxis_inverted(): - marker = mlines.CARETLEFTBASE - else: - marker = mlines.CARETRIGHTBASE - caplines.append(mlines.Line2D( - ro, yo, ls='None', marker=marker, **eb_cap_style)) - if capsize > 0: - caplines.append(mlines.Line2D( - xo, yo, marker='|', **eb_cap_style)) - if xuplims.any(): - xo, yo, lo = apply_mask([x, y, left], xuplims & everymask) - if self.xaxis_inverted(): - marker = mlines.CARETRIGHTBASE - else: - marker = mlines.CARETLEFTBASE - caplines.append(mlines.Line2D( - lo, yo, ls='None', marker=marker, **eb_cap_style)) - if capsize > 0: - caplines.append(mlines.Line2D( - xo, yo, marker='|', **eb_cap_style)) - - if yerr is not None: - lower, upper = extract_err('y', yerr, y, lolims, uplims) - barcols.append(self.vlines( - *apply_mask([x, lower, upper], everymask), **eb_lines_style)) - # select points without upper/lower limits in y and - # draw normal errorbars for these points - noylims = ~(lolims | uplims) - if noylims.any() and capsize > 0: - xo, lo, uo = apply_mask([x, lower, upper], noylims & everymask) - caplines.extend([ - mlines.Line2D(xo, lo, marker='_', **eb_cap_style), - mlines.Line2D(xo, uo, marker='_', **eb_cap_style)]) - if lolims.any(): - xo, yo, uo = apply_mask([x, y, upper], lolims & everymask) - if self.yaxis_inverted(): - marker = mlines.CARETDOWNBASE - else: - marker = mlines.CARETUPBASE - caplines.append(mlines.Line2D( - xo, uo, ls='None', marker=marker, **eb_cap_style)) - if capsize > 0: - caplines.append(mlines.Line2D( - xo, yo, marker='_', **eb_cap_style)) - if uplims.any(): - xo, yo, lo = apply_mask([x, y, lower], uplims & everymask) - if self.yaxis_inverted(): - marker = mlines.CARETUPBASE - else: - marker = mlines.CARETDOWNBASE - caplines.append(mlines.Line2D( - xo, lo, ls='None', marker=marker, **eb_cap_style)) + f"'{dep_axis}err' (shape: {np.shape(err)}) must be a " + f"scalar or a 1D or (2, n) array-like whose shape matches " + f"'{dep_axis}' (shape: {np.shape(dep)})") from None + res = np.zeros(err.shape, dtype=bool) # Default in case of nan + if np.any(np.less(err, -err, out=res, where=(err == err))): + # like err<0, but also works for timedelta and nan. + raise ValueError( + f"'{dep_axis}err' must not contain negative values") + # This is like + # elow, ehigh = np.broadcast_to(...) + # return dep - elow * ~lolims, dep + ehigh * ~uplims + # except that broadcast_to would strip units. + low, high = dep + np.row_stack([-(1 - lolims), 1 - uplims]) * err + + barcols.append(lines_func( + *apply_mask([indep, low, high], everymask), **eb_lines_style)) + # Normal errorbars for points without upper/lower limits. + nolims = ~(lolims | uplims) + if nolims.any() and capsize > 0: + indep_masked, lo_masked, hi_masked = apply_mask( + [indep, low, high], nolims & everymask) + for lh_masked in [lo_masked, hi_masked]: + # Since this has to work for x and y as dependent data, we + # first set both x and y to the independent variable and + # overwrite the respective dependent data in a second step. + line = mlines.Line2D(indep_masked, indep_masked, + marker=marker, **eb_cap_style) + line.set(**{f"{dep_axis}data": lh_masked}) + caplines.append(line) + for idx, (lims, hl) in enumerate([(lolims, high), (uplims, low)]): + if not lims.any(): + continue + hlmarker = ( + himarker + if getattr(self, f"{dep_axis}axis").get_inverted() ^ idx + else lomarker) + x_masked, y_masked, hl_masked = apply_mask( + [x, y, hl], lims & everymask) + # As above, we set the dependent data in a second step. + line = mlines.Line2D(x_masked, y_masked, + marker=hlmarker, **eb_cap_style) + line.set(**{f"{dep_axis}data": hl_masked}) + caplines.append(line) if capsize > 0: caplines.append(mlines.Line2D( - xo, yo, marker='_', **eb_cap_style)) + x_masked, y_masked, marker=marker, **eb_cap_style)) for l in caplines: self.add_line(l) @@ -3547,28 +3653,41 @@ def boxplot(self, x, notch=None, sym=None, vert=None, whis=None, showbox=None, showfliers=None, boxprops=None, labels=None, flierprops=None, medianprops=None, meanprops=None, capprops=None, whiskerprops=None, - manage_ticks=True, autorange=False, zorder=None): + manage_ticks=True, autorange=False, zorder=None, + capwidths=None): """ - Make a box and whisker plot. + Draw a box and whisker plot. + + The box extends from the first quartile (Q1) to the third + quartile (Q3) of the data, with a line at the median. The + whiskers extend from the box by 1.5x the inter-quartile range + (IQR). Flier points are those past the end of the whiskers. + See https://en.wikipedia.org/wiki/Box_plot for reference. + + .. code-block:: none + + Q1-1.5IQR Q1 median Q3 Q3+1.5IQR + |-----:-----| + o |--------| : |--------| o o + |-----:-----| + flier <-----------> fliers + IQR - Make a box and whisker plot for each column of *x* or each - vector in sequence *x*. The box extends from the lower to - upper quartile values of the data, with a line at the median. - The whiskers extend from the box to show the range of the - data. Flier points are those past the end of the whiskers. Parameters ---------- x : Array or a sequence of vectors. - The input data. + The input data. If a 2D array, a boxplot is drawn for each column + in *x*. If a sequence of 1D arrays, a boxplot is drawn for each + array in *x*. notch : bool, default: False - Whether to draw a notched box plot (`True`), or a rectangular box - plot (`False`). The notches represent the confidence interval (CI) - around the median. The documentation for *bootstrap* describes how - the locations of the notches are computed by default, but their - locations may also be overridden by setting the *conf_intervals* - parameter. + Whether to draw a notched boxplot (`True`), or a rectangular + boxplot (`False`). The notches represent the confidence interval + (CI) around the median. The documentation for *bootstrap* + describes how the locations of the notches are computed by + default, but their locations may also be overridden by setting the + *conf_intervals* parameter. .. note:: @@ -3643,7 +3762,7 @@ def boxplot(self, x, notch=None, sym=None, vert=None, whis=None, patch_artist : bool, default: False If `False` produces boxes with the Line2D artist. Otherwise, - boxes and drawn with Patch artists. + boxes are drawn with Patch artists. labels : sequence, optional Labels for each dataset (one per dataset). @@ -3702,6 +3821,8 @@ def boxplot(self, x, notch=None, sym=None, vert=None, whis=None, Show the arithmetic means. capprops : dict, default: None The style of the caps. + capwidths : float or array, default: None + The widths of the caps. boxprops : dict, default: None The style of the box. whiskerprops : dict, default: None @@ -3712,33 +3833,38 @@ def boxplot(self, x, notch=None, sym=None, vert=None, whis=None, The style of the median. meanprops : dict, default: None The style of the mean. + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER + See Also + -------- + violinplot : Draw an estimate of the probability density function. """ # Missing arguments default to rcParams. if whis is None: - whis = rcParams['boxplot.whiskers'] + whis = mpl.rcParams['boxplot.whiskers'] if bootstrap is None: - bootstrap = rcParams['boxplot.bootstrap'] + bootstrap = mpl.rcParams['boxplot.bootstrap'] bxpstats = cbook.boxplot_stats(x, whis=whis, bootstrap=bootstrap, labels=labels, autorange=autorange) if notch is None: - notch = rcParams['boxplot.notch'] + notch = mpl.rcParams['boxplot.notch'] if vert is None: - vert = rcParams['boxplot.vertical'] + vert = mpl.rcParams['boxplot.vertical'] if patch_artist is None: - patch_artist = rcParams['boxplot.patchartist'] + patch_artist = mpl.rcParams['boxplot.patchartist'] if meanline is None: - meanline = rcParams['boxplot.meanline'] + meanline = mpl.rcParams['boxplot.meanline'] if showmeans is None: - showmeans = rcParams['boxplot.showmeans'] + showmeans = mpl.rcParams['boxplot.showmeans'] if showcaps is None: - showcaps = rcParams['boxplot.showcaps'] + showcaps = mpl.rcParams['boxplot.showcaps'] if showbox is None: - showbox = rcParams['boxplot.showbox'] + showbox = mpl.rcParams['boxplot.showbox'] if showfliers is None: - showfliers = rcParams['boxplot.showfliers'] + showfliers = mpl.rcParams['boxplot.showfliers'] if boxprops is None: boxprops = {} @@ -3760,7 +3886,7 @@ def boxplot(self, x, notch=None, sym=None, vert=None, whis=None, # if non-default sym value, put it into the flier dictionary # the logic for providing the default symbol ('b+') now lives - # in bxp in the initial value of final_flierprops + # in bxp in the initial value of flierkw # handle all of the *sym* related logic here so we only have to pass # on the flierprops dict. if sym is not None: @@ -3825,7 +3951,8 @@ def boxplot(self, x, notch=None, sym=None, vert=None, whis=None, medianprops=medianprops, meanprops=meanprops, meanline=meanline, showfliers=showfliers, capprops=capprops, whiskerprops=whiskerprops, - manage_ticks=manage_ticks, zorder=zorder) + manage_ticks=manage_ticks, zorder=zorder, + capwidths=capwidths) return artists def bxp(self, bxpstats, positions=None, widths=None, vert=True, @@ -3833,7 +3960,8 @@ def bxp(self, bxpstats, positions=None, widths=None, vert=True, showcaps=True, showbox=True, showfliers=True, boxprops=None, whiskerprops=None, flierprops=None, medianprops=None, capprops=None, meanprops=None, - meanline=False, manage_ticks=True, zorder=None): + meanline=False, manage_ticks=True, zorder=None, + capwidths=None): """ Drawing function for box and whisker plots. @@ -3849,85 +3977,49 @@ def bxp(self, bxpstats, positions=None, widths=None, vert=True, A list of dictionaries containing stats for each boxplot. Required keys are: - - ``med``: The median (scalar float). - - - ``q1``: The first quartile (25th percentile) (scalar - float). - - - ``q3``: The third quartile (75th percentile) (scalar - float). - - - ``whislo``: Lower bound of the lower whisker (scalar - float). - - - ``whishi``: Upper bound of the upper whisker (scalar - float). + - ``med``: Median (scalar). + - ``q1``, ``q3``: First & third quartiles (scalars). + - ``whislo``, ``whishi``: Lower & upper whisker positions (scalars). Optional keys are: - - ``mean``: The mean (scalar float). Needed if - ``showmeans=True``. - - - ``fliers``: Data beyond the whiskers (sequence of floats). + - ``mean``: Mean (scalar). Needed if ``showmeans=True``. + - ``fliers``: Data beyond the whiskers (array-like). Needed if ``showfliers=True``. - - - ``cilo`` & ``cihi``: Lower and upper confidence intervals + - ``cilo``, ``cihi``: Lower & upper confidence intervals about the median. Needed if ``shownotches=True``. - - - ``label``: Name of the dataset (string). If available, + - ``label``: Name of the dataset (str). If available, this will be used a tick label for the boxplot positions : array-like, default: [1, 2, ..., n] The positions of the boxes. The ticks and limits are automatically set to match the positions. - widths : array-like, default: None - Either a scalar or a vector and sets the width of each - box. The default is ``0.15*(distance between extreme - positions)``, clipped to no less than 0.15 and no more than - 0.5. + widths : float or array-like, default: None + The widths of the boxes. The default is + ``clip(0.15*(distance between extreme positions), 0.15, 0.5)``. + + capwidths : float or array-like, default: None + Either a scalar or a vector and sets the width of each cap. + The default is ``0.5*(with of the box)``, see *widths*. vert : bool, default: True - If `True` (default), makes the boxes vertical. If `False`, - makes horizontal boxes. + If `True` (default), makes the boxes vertical. + If `False`, makes horizontal boxes. patch_artist : bool, default: False If `False` produces boxes with the `.Line2D` artist. If `True` produces boxes with the `~matplotlib.patches.Patch` artist. - shownotches : bool, default: False - If `False` (default), produces a rectangular box plot. - If `True`, will produce a notched box plot - - showmeans : bool, default: False - If `True`, will toggle on the rendering of the means - - showcaps : bool, default: True - If `True`, will toggle on the rendering of the caps - - showbox : bool, default: True - If `True`, will toggle on the rendering of the box - - showfliers : bool, default: True - If `True`, will toggle on the rendering of the fliers + shownotches, showmeans, showcaps, showbox, showfliers : bool + Whether to draw the CI notches, the mean value (both default to + False), the caps, the box, and the fliers (all three default to + True). - boxprops : dict or None (default) - If provided, will set the plotting style of the boxes - - whiskerprops : dict or None (default) - If provided, will set the plotting style of the whiskers - - capprops : dict or None (default) - If provided, will set the plotting style of the caps - - flierprops : dict or None (default) - If provided will set the plotting style of the fliers - - medianprops : dict or None (default) - If provided, will set the plotting style of the medians - - meanprops : dict or None (default) - If provided, will set the plotting style of the means + boxprops, whiskerprops, capprops, flierprops, medianprops, meanprops :\ + dict, optional + Artist properties for the boxes, whiskers, caps, fliers, medians, and + means. meanline : bool, default: False If `True` (and *showmeans* is `True`), will try to render the mean @@ -3949,28 +4041,19 @@ def bxp(self, bxpstats, positions=None, widths=None, vert=True, of the `.Line2D` instances created. That dictionary has the following keys (assuming vertical boxplots): - - ``boxes``: the main body of the boxplot showing the - quartiles and the median's confidence intervals if - enabled. - + - ``boxes``: main bodies of the boxplot showing the quartiles, and + the median's confidence intervals if enabled. - ``medians``: horizontal lines at the median of each box. - - - ``whiskers``: the vertical lines extending to the most - extreme, non-outlier data points. - - - ``caps``: the horizontal lines at the ends of the - whiskers. - - - ``fliers``: points representing data that extend beyond - the whiskers (fliers). - + - ``whiskers``: vertical lines up to the last non-outlier data. + - ``caps``: horizontal lines at the ends of the whiskers. + - ``fliers``: points representing data beyond the whiskers (fliers). - ``means``: points or lines representing the means. Examples -------- .. plot:: gallery/statistics/bxp.py - """ + # lists of artists to be output whiskers = [] caps = [] @@ -3988,72 +4071,46 @@ def bxp(self, bxpstats, positions=None, widths=None, vert=True, zdelta = 0.1 - def line_props_with_rcdefaults(subkey, explicit, zdelta=0, - use_marker=True): - d = {k.split('.')[-1]: v for k, v in rcParams.items() - if k.startswith(f'boxplot.{subkey}')} + def merge_kw_rc(subkey, explicit, zdelta=0, usemarker=True): + d = {k.split('.')[-1]: v for k, v in mpl.rcParams.items() + if k.startswith(f'boxplot.{subkey}props')} d['zorder'] = zorder + zdelta - if not use_marker: + if not usemarker: d['marker'] = '' d.update(cbook.normalize_kwargs(explicit, mlines.Line2D)) return d - # box properties - if patch_artist: - final_boxprops = { - 'linestyle': rcParams['boxplot.boxprops.linestyle'], - 'linewidth': rcParams['boxplot.boxprops.linewidth'], - 'edgecolor': rcParams['boxplot.boxprops.color'], - 'facecolor': ('white' if rcParams['_internal.classic_mode'] - else rcParams['patch.facecolor']), - 'zorder': zorder, - **cbook.normalize_kwargs(boxprops, mpatches.PathPatch) - } - else: - final_boxprops = line_props_with_rcdefaults('boxprops', boxprops, - use_marker=False) - final_whiskerprops = line_props_with_rcdefaults( - 'whiskerprops', whiskerprops, use_marker=False) - final_capprops = line_props_with_rcdefaults( - 'capprops', capprops, use_marker=False) - final_flierprops = line_props_with_rcdefaults( - 'flierprops', flierprops) - final_medianprops = line_props_with_rcdefaults( - 'medianprops', medianprops, zdelta, use_marker=False) - final_meanprops = line_props_with_rcdefaults( - 'meanprops', meanprops, zdelta) + box_kw = { + 'linestyle': mpl.rcParams['boxplot.boxprops.linestyle'], + 'linewidth': mpl.rcParams['boxplot.boxprops.linewidth'], + 'edgecolor': mpl.rcParams['boxplot.boxprops.color'], + 'facecolor': ('white' if mpl.rcParams['_internal.classic_mode'] + else mpl.rcParams['patch.facecolor']), + 'zorder': zorder, + **cbook.normalize_kwargs(boxprops, mpatches.PathPatch) + } if patch_artist else merge_kw_rc('box', boxprops, usemarker=False) + whisker_kw = merge_kw_rc('whisker', whiskerprops, usemarker=False) + cap_kw = merge_kw_rc('cap', capprops, usemarker=False) + flier_kw = merge_kw_rc('flier', flierprops) + median_kw = merge_kw_rc('median', medianprops, zdelta, usemarker=False) + mean_kw = merge_kw_rc('mean', meanprops, zdelta) removed_prop = 'marker' if meanline else 'linestyle' # Only remove the property if it's not set explicitly as a parameter. if meanprops is None or removed_prop not in meanprops: - final_meanprops[removed_prop] = '' - - def patch_list(xs, ys, **kwargs): - path = mpath.Path( - # Last vertex will have a CLOSEPOLY code and thus be ignored. - np.append(np.column_stack([xs, ys]), [(0, 0)], 0), - closed=True) - patch = mpatches.PathPatch(path, **kwargs) - self.add_artist(patch) - return [patch] + mean_kw[removed_prop] = '' # vertical or horizontal plot? - if vert: - def doplot(*args, **kwargs): - return self.plot(*args, **kwargs) - - def dopatch(xs, ys, **kwargs): - return patch_list(xs, ys, **kwargs) + maybe_swap = slice(None) if vert else slice(None, None, -1) - else: - def doplot(*args, **kwargs): - shuffled = [] - for i in range(0, len(args), 2): - shuffled.extend([args[i + 1], args[i]]) - return self.plot(*shuffled, **kwargs) + def do_plot(xs, ys, **kwargs): + return self.plot(*[xs, ys][maybe_swap], **kwargs)[0] - def dopatch(xs, ys, **kwargs): - xs, ys = ys, xs # flip X, Y - return patch_list(xs, ys, **kwargs) + def do_patch(xs, ys, **kwargs): + path = mpath.Path._create_closed( + np.column_stack([xs, ys][maybe_swap])) + patch = mpatches.PathPatch(path, **kwargs) + self.add_artist(patch) + return patch # input validation N = len(bxpstats) @@ -4077,38 +4134,45 @@ def dopatch(xs, ys, **kwargs): elif len(widths) != N: raise ValueError(datashape_message.format("widths")) - for pos, width, stats in zip(positions, widths, bxpstats): + # capwidth + if capwidths is None: + capwidths = 0.5 * np.array(widths) + elif np.isscalar(capwidths): + capwidths = [capwidths] * N + elif len(capwidths) != N: + raise ValueError(datashape_message.format("capwidths")) + + for pos, width, stats, capwidth in zip(positions, widths, bxpstats, + capwidths): # try to find a new label datalabels.append(stats.get('label', pos)) # whisker coords - whisker_x = np.ones(2) * pos - whiskerlo_y = np.array([stats['q1'], stats['whislo']]) - whiskerhi_y = np.array([stats['q3'], stats['whishi']]) - + whis_x = [pos, pos] + whislo_y = [stats['q1'], stats['whislo']] + whishi_y = [stats['q3'], stats['whishi']] # cap coords - cap_left = pos - width * 0.25 - cap_right = pos + width * 0.25 - cap_x = np.array([cap_left, cap_right]) - cap_lo = np.ones(2) * stats['whislo'] - cap_hi = np.ones(2) * stats['whishi'] - + cap_left = pos - capwidth * 0.5 + cap_right = pos + capwidth * 0.5 + cap_x = [cap_left, cap_right] + cap_lo = np.full(2, stats['whislo']) + cap_hi = np.full(2, stats['whishi']) # box and median coords box_left = pos - width * 0.5 box_right = pos + width * 0.5 med_y = [stats['med'], stats['med']] - # notched boxes if shownotches: - box_x = [box_left, box_right, box_right, cap_right, box_right, - box_right, box_left, box_left, cap_left, box_left, - box_left] + notch_left = pos - width * 0.25 + notch_right = pos + width * 0.25 + box_x = [box_left, box_right, box_right, notch_right, + box_right, box_right, box_left, box_left, notch_left, + box_left, box_left] box_y = [stats['q1'], stats['q1'], stats['cilo'], stats['med'], stats['cihi'], stats['q3'], stats['q3'], stats['cihi'], stats['med'], stats['cilo'], stats['q1']] - med_x = cap_x - + med_x = [notch_left, notch_right] # plain boxes else: box_x = [box_left, box_right, box_right, box_left, box_left] @@ -4116,50 +4180,33 @@ def dopatch(xs, ys, **kwargs): stats['q1']] med_x = [box_left, box_right] - # maybe draw the box: + # maybe draw the box if showbox: - if patch_artist: - boxes.extend(dopatch(box_x, box_y, **final_boxprops)) - else: - boxes.extend(doplot(box_x, box_y, **final_boxprops)) - + do_box = do_patch if patch_artist else do_plot + boxes.append(do_box(box_x, box_y, **box_kw)) # draw the whiskers - whiskers.extend(doplot( - whisker_x, whiskerlo_y, **final_whiskerprops - )) - whiskers.extend(doplot( - whisker_x, whiskerhi_y, **final_whiskerprops - )) - - # maybe draw the caps: + whiskers.append(do_plot(whis_x, whislo_y, **whisker_kw)) + whiskers.append(do_plot(whis_x, whishi_y, **whisker_kw)) + # maybe draw the caps if showcaps: - caps.extend(doplot(cap_x, cap_lo, **final_capprops)) - caps.extend(doplot(cap_x, cap_hi, **final_capprops)) - + caps.append(do_plot(cap_x, cap_lo, **cap_kw)) + caps.append(do_plot(cap_x, cap_hi, **cap_kw)) # draw the medians - medians.extend(doplot(med_x, med_y, **final_medianprops)) - + medians.append(do_plot(med_x, med_y, **median_kw)) # maybe draw the means if showmeans: if meanline: - means.extend(doplot( + means.append(do_plot( [box_left, box_right], [stats['mean'], stats['mean']], - **final_meanprops + **mean_kw )) else: - means.extend(doplot( - [pos], [stats['mean']], **final_meanprops - )) - + means.append(do_plot([pos], [stats['mean']], **mean_kw)) # maybe draw the fliers if showfliers: - # fliers coords flier_x = np.full(len(stats['fliers']), pos, dtype=np.float64) flier_y = stats['fliers'] - - fliers.extend(doplot( - flier_x, flier_y, **final_flierprops - )) + fliers.append(do_plot(flier_x, flier_y, **flier_kw)) if manage_ticks: axis_name = "x" if vert else "y" @@ -4191,8 +4238,7 @@ def dopatch(xs, ys, **kwargs): axis.set_major_formatter(formatter) formatter.seq = [*formatter.seq, *datalabels] - self._request_autoscale_view( - scalex=self._autoscaleXon, scaley=self._autoscaleYon) + self._request_autoscale_view() return dict(whiskers=whiskers, caps=caps, boxes=boxes, medians=medians, fliers=fliers, means=means) @@ -4268,7 +4314,7 @@ def _parse_scatter_color_args(c, edgecolors, kwargs, xsize, mcolors.to_rgba_array(kwcolor) except ValueError as err: raise ValueError( - "'color' kwarg must be an color or sequence of color " + "'color' kwarg must be a color or sequence of color " "specs. For a sequence of values to be color-mapped, use " "the 'c' argument instead.") from err if edgecolors is None: @@ -4276,18 +4322,18 @@ def _parse_scatter_color_args(c, edgecolors, kwargs, xsize, if facecolors is None: facecolors = kwcolor - if edgecolors is None and not rcParams['_internal.classic_mode']: - edgecolors = rcParams['scatter.edgecolors'] + if edgecolors is None and not mpl.rcParams['_internal.classic_mode']: + edgecolors = mpl.rcParams['scatter.edgecolors'] c_was_none = c is None if c is None: c = (facecolors if facecolors is not None - else "b" if rcParams['_internal.classic_mode'] + else "b" if mpl.rcParams['_internal.classic_mode'] else get_next_color_func()) c_is_string_or_strings = ( isinstance(c, str) or (np.iterable(c) and len(c) > 0 - and isinstance(cbook.safe_first_element(c), str))) + and isinstance(cbook._safe_first_finite(c), str))) def invalid_shape_exception(csize, xsize): return ValueError( @@ -4351,6 +4397,7 @@ def invalid_shape_exception(csize, xsize): "edgecolors", "c", "facecolor", "facecolors", "color"], label_namer="y") + @_docstring.interpd def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None, *, edgecolors=None, plotnonfinite=False, **kwargs): @@ -4363,7 +4410,7 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None, The data positions. s : float or array-like, shape (n, ), optional - The marker size in points**2. + The marker size in points**2 (typographic points are 1/72 in.). Default is ``rcParams['lines.markersize'] ** 2``. c : array-like or list of colors or color, optional @@ -4397,21 +4444,17 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None, See :mod:`matplotlib.markers` for more information about marker styles. - cmap : str or `~matplotlib.colors.Colormap`, default: :rc:`image.cmap` - A `.Colormap` instance or registered colormap name. *cmap* is only - used if *c* is an array of floats. + %(cmap_doc)s + + This parameter is ignored if *c* is RGB(A). - norm : `~matplotlib.colors.Normalize`, default: None - If *c* is an array of floats, *norm* is used to scale the color - data, *c*, in the range 0 to 1, in order to map into the colormap - *cmap*. - If *None*, use the default `.colors.Normalize`. + %(norm_doc)s - vmin, vmax : float, default: None - *vmin* and *vmax* are used in conjunction with the default norm to - map the color array *c* to the colormap *cmap*. If None, the - respective min and max of the color array is used. - It is deprecated to use *vmin*/*vmax* when *norm* is given. + This parameter is ignored if *c* is RGB(A). + + %(vmin_vmax_doc)s + + This parameter is ignored if *c* is RGB(A). alpha : float, default: None The alpha blending value, between 0 (transparent) and 1 (opaque). @@ -4443,6 +4486,8 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None, Other Parameters ---------------- + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER **kwargs : `~matplotlib.collections.Collection` properties See Also @@ -4466,9 +4511,7 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None, """ # Process **kwargs to handle aliases, conflicts with explicit kwargs: - x, y = self._process_unit_info([("x", x), ("y", y)], kwargs) - # np.ma.ravel yields an ndarray, not a masked array, # unless its argument is a masked array. x = np.ma.ravel(x) @@ -4477,8 +4520,8 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None, raise ValueError("x and y must be the same size") if s is None: - s = (20 if rcParams['_internal.classic_mode'] else - rcParams['lines.markersize'] ** 2.0) + s = (20 if mpl.rcParams['_internal.classic_mode'] else + mpl.rcParams['lines.markersize'] ** 2.0) s = np.ma.ravel(s) if (len(s) not in (1, x.size) or (not np.issubdtype(s.dtype, np.floating) and @@ -4514,7 +4557,7 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None, # load default marker from rcParams if marker is None: - marker = rcParams['scatter.marker'] + marker = mpl.rcParams['scatter.marker'] if isinstance(marker, mmarkers.MarkerStyle): marker_obj = marker @@ -4546,8 +4589,8 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None, # promote the facecolor to be the edgecolor edgecolors = colors # set the facecolor to 'none' (at the last chance) because - # we can not not fill a path if the facecolor is non-null. - # (which is defendable at the renderer level) + # we can not fill a path if the facecolor is non-null + # (which is defendable at the renderer level). colors = 'none' else: # if we are not nulling the face color we can do this @@ -4555,38 +4598,47 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None, edgecolors = 'face' if linewidths is None: - linewidths = rcParams['lines.linewidth'] + linewidths = mpl.rcParams['lines.linewidth'] elif np.iterable(linewidths): linewidths = [ - lw if lw is not None else rcParams['lines.linewidth'] + lw if lw is not None else mpl.rcParams['lines.linewidth'] for lw in linewidths] offsets = np.ma.column_stack([x, y]) collection = mcoll.PathCollection( - (path,), scales, - facecolors=colors, - edgecolors=edgecolors, - linewidths=linewidths, - offsets=offsets, - transOffset=kwargs.pop('transform', self.transData), - alpha=alpha - ) + (path,), scales, + facecolors=colors, + edgecolors=edgecolors, + linewidths=linewidths, + offsets=offsets, + offset_transform=kwargs.pop('transform', self.transData), + alpha=alpha, + ) collection.set_transform(mtransforms.IdentityTransform()) - collection.update(kwargs) - if colors is None: collection.set_array(c) collection.set_cmap(cmap) collection.set_norm(norm) collection._scale_norm(norm, vmin, vmax) + else: + extra_kwargs = { + 'cmap': cmap, 'norm': norm, 'vmin': vmin, 'vmax': vmax + } + extra_keys = [k for k, v in extra_kwargs.items() if v is not None] + if any(extra_keys): + keys_str = ", ".join(f"'{k}'" for k in extra_keys) + _api.warn_external( + "No data for colormapping provided via 'c'. " + f"Parameters {keys_str} will be ignored") + collection._internal_update(kwargs) # Classic mode only: # ensure there are margins to allow for the # finite size of the symbols. In v2.x, margins # are present by default, so we disable this # scatter-specific override. - if rcParams['_internal.classic_mode']: + if mpl.rcParams['_internal.classic_mode']: if self._xmargin < 0.05 and x.size > 0: self.set_xmargin(0.05) if self._ymargin < 0.05 and x.size > 0: @@ -4598,7 +4650,7 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None, return collection @_preprocess_data(replace_names=["x", "y", "C"], label_namer="y") - @docstring.dedent_interpd + @_docstring.dedent_interpd def hexbin(self, x, y, C=None, gridsize=100, bins=None, xscale='linear', yscale='linear', extent=None, cmap=None, norm=None, vmin=None, vmax=None, @@ -4629,7 +4681,31 @@ def hexbin(self, x, y, C=None, gridsize=100, bins=None, the hexagons are approximately regular. Alternatively, if a tuple (*nx*, *ny*), the number of hexagons - in the *x*-direction and the *y*-direction. + in the *x*-direction and the *y*-direction. In the + *y*-direction, counting is done along vertically aligned + hexagons, not along the zig-zag chains of hexagons; see the + following illustration. + + .. plot:: + + import numpy + import matplotlib.pyplot as plt + + np.random.seed(19680801) + n= 300 + x = np.random.standard_normal(n) + y = np.random.standard_normal(n) + + fig, ax = plt.subplots(figsize=(4, 4)) + h = ax.hexbin(x, y, gridsize=(5, 3)) + hx, hy = h.get_offsets().T + ax.plot(hx[24::3], hy[24::3], 'ro-') + ax.plot(hx[-3:], hy[-3:], 'ro-') + ax.set_title('gridsize=(5, 3)') + ax.axis('off') + + To get approximately regular hexagons, choose + :math:`n_x = \\sqrt{3}\\,n_y`. bins : 'log' or int or sequence, default: None Discretization of the hexagon values. @@ -4659,17 +4735,16 @@ def hexbin(self, x, y, C=None, gridsize=100, bins=None, colormapped rectangles along the bottom of the x-axis and left of the y-axis. - extent : float, default: *None* - The limits of the bins. The default assigns the limits - based on *gridsize*, *x*, *y*, *xscale* and *yscale*. + extent : 4-tuple of float, default: *None* + The limits of the bins (xmin, xmax, ymin, ymax). + The default assigns the limits based on + *gridsize*, *x*, *y*, *xscale* and *yscale*. If *xscale* or *yscale* is set to 'log', the limits are expected to be the exponent for a power of 10. E.g. for x-limits of 1 and 50 in 'linear' scale and y-limits of 10 and 1000 in 'log' scale, enter (1, 50, 1, 3). - Order of scalars is (left, right, bottom, top). - Returns ------- `~matplotlib.collections.PolyCollection` @@ -4686,21 +4761,11 @@ def hexbin(self, x, y, C=None, gridsize=100, bins=None, Other Parameters ---------------- - cmap : str or `~matplotlib.colors.Colormap`, default: :rc:`image.cmap` - The Colormap instance or registered colormap name used to map - the bin values to colors. - - norm : `~matplotlib.colors.Normalize`, optional - The Normalize instance scales the bin values to the canonical - colormap range [0, 1] for mapping to colors. By default, the data - range is mapped to the colorbar range using linear scaling. - - vmin, vmax : float, default: None - The colorbar range. If *None*, suitable min/max values are - automatically chosen by the `~.Normalize` instance (defaults to - the respective min/max values of the bins in case of the default - linear scaling). - It is deprecated to use *vmin*/*vmax* when *norm* is given. + %(cmap_doc)s + + %(norm_doc)s + + %(vmin_vmax_doc)s alpha : float between 0 and 1, optional The alpha blending value, between 0 (transparent) and 1 (opaque). @@ -4729,11 +4794,17 @@ def reduce_C_function(C: array) -> float - `numpy.sum`: integral of the point values - `numpy.amax`: value taken from the largest point + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER + **kwargs : `~matplotlib.collections.PolyCollection` properties All other keyword arguments are passed on to `.PolyCollection`: - %(PolyCollection_kwdoc)s + %(PolyCollection:kwdoc)s + See Also + -------- + hist2d : 2D histogram rectangular bins """ self._process_unit_info([("x", x), ("y", y)], kwargs, convert=False) @@ -4746,28 +4817,39 @@ def reduce_C_function(C: array) -> float nx = gridsize ny = int(nx / math.sqrt(3)) # Count the number of data in each hexagon - x = np.array(x, float) - y = np.array(y, float) + x = np.asarray(x, float) + y = np.asarray(y, float) + + # Will be log()'d if necessary, and then rescaled. + tx = x + ty = y + if xscale == 'log': if np.any(x <= 0.0): - raise ValueError("x contains non-positive values, so can not" - " be log-scaled") - x = np.log10(x) + raise ValueError("x contains non-positive values, so can not " + "be log-scaled") + tx = np.log10(tx) if yscale == 'log': if np.any(y <= 0.0): - raise ValueError("y contains non-positive values, so can not" - " be log-scaled") - y = np.log10(y) + raise ValueError("y contains non-positive values, so can not " + "be log-scaled") + ty = np.log10(ty) if extent is not None: xmin, xmax, ymin, ymax = extent else: - xmin, xmax = (np.min(x), np.max(x)) if len(x) else (0, 1) - ymin, ymax = (np.min(y), np.max(y)) if len(y) else (0, 1) + xmin, xmax = (tx.min(), tx.max()) if len(x) else (0, 1) + ymin, ymax = (ty.min(), ty.max()) if len(y) else (0, 1) # to avoid issues with singular data, expand the min/max pairs xmin, xmax = mtransforms.nonsingular(xmin, xmax, expander=0.1) ymin, ymax = mtransforms.nonsingular(ymin, ymax, expander=0.1) + nx1 = nx + 1 + ny1 = ny + 1 + nx2 = nx + ny2 = ny + n = nx1 * ny1 + nx2 * ny2 + # In the x-direction, the hexagons exactly cover the region from # xmin to xmax. Need some padding to avoid roundoff errors. padding = 1.e-9 * (xmax - xmin) @@ -4775,80 +4857,48 @@ def reduce_C_function(C: array) -> float xmax += padding sx = (xmax - xmin) / nx sy = (ymax - ymin) / ny - - if marginals: - xorig = x.copy() - yorig = y.copy() - - x = (x - xmin) / sx - y = (y - ymin) / sy - ix1 = np.round(x).astype(int) - iy1 = np.round(y).astype(int) - ix2 = np.floor(x).astype(int) - iy2 = np.floor(y).astype(int) - - nx1 = nx + 1 - ny1 = ny + 1 - nx2 = nx - ny2 = ny - n = nx1 * ny1 + nx2 * ny2 - - d1 = (x - ix1) ** 2 + 3.0 * (y - iy1) ** 2 - d2 = (x - ix2 - 0.5) ** 2 + 3.0 * (y - iy2 - 0.5) ** 2 + # Positions in hexagon index coordinates. + ix = (tx - xmin) / sx + iy = (ty - ymin) / sy + ix1 = np.round(ix).astype(int) + iy1 = np.round(iy).astype(int) + ix2 = np.floor(ix).astype(int) + iy2 = np.floor(iy).astype(int) + # flat indices, plus one so that out-of-range points go to position 0. + i1 = np.where((0 <= ix1) & (ix1 < nx1) & (0 <= iy1) & (iy1 < ny1), + ix1 * ny1 + iy1 + 1, 0) + i2 = np.where((0 <= ix2) & (ix2 < nx2) & (0 <= iy2) & (iy2 < ny2), + ix2 * ny2 + iy2 + 1, 0) + + d1 = (ix - ix1) ** 2 + 3.0 * (iy - iy1) ** 2 + d2 = (ix - ix2 - 0.5) ** 2 + 3.0 * (iy - iy2 - 0.5) ** 2 bdist = (d1 < d2) - if C is None: - lattice1 = np.zeros((nx1, ny1)) - lattice2 = np.zeros((nx2, ny2)) - c1 = (0 <= ix1) & (ix1 < nx1) & (0 <= iy1) & (iy1 < ny1) & bdist - c2 = (0 <= ix2) & (ix2 < nx2) & (0 <= iy2) & (iy2 < ny2) & ~bdist - np.add.at(lattice1, (ix1[c1], iy1[c1]), 1) - np.add.at(lattice2, (ix2[c2], iy2[c2]), 1) - if mincnt is not None: - lattice1[lattice1 < mincnt] = np.nan - lattice2[lattice2 < mincnt] = np.nan - accum = np.concatenate([lattice1.ravel(), lattice2.ravel()]) - good_idxs = ~np.isnan(accum) + if C is None: # [1:] drops out-of-range points. + counts1 = np.bincount(i1[bdist], minlength=1 + nx1 * ny1)[1:] + counts2 = np.bincount(i2[~bdist], minlength=1 + nx2 * ny2)[1:] + accum = np.concatenate([counts1, counts2]).astype(float) + if mincnt is not None: + accum[accum < mincnt] = np.nan + C = np.ones(len(x)) else: - if mincnt is None: - mincnt = 0 - - # create accumulation arrays - lattice1 = np.empty((nx1, ny1), dtype=object) - for i in range(nx1): - for j in range(ny1): - lattice1[i, j] = [] - lattice2 = np.empty((nx2, ny2), dtype=object) - for i in range(nx2): - for j in range(ny2): - lattice2[i, j] = [] - + # store the C values in a list per hexagon index + Cs_at_i1 = [[] for _ in range(1 + nx1 * ny1)] + Cs_at_i2 = [[] for _ in range(1 + nx2 * ny2)] for i in range(len(x)): if bdist[i]: - if 0 <= ix1[i] < nx1 and 0 <= iy1[i] < ny1: - lattice1[ix1[i], iy1[i]].append(C[i]) + Cs_at_i1[i1[i]].append(C[i]) else: - if 0 <= ix2[i] < nx2 and 0 <= iy2[i] < ny2: - lattice2[ix2[i], iy2[i]].append(C[i]) - - for i in range(nx1): - for j in range(ny1): - vals = lattice1[i, j] - if len(vals) > mincnt: - lattice1[i, j] = reduce_C_function(vals) - else: - lattice1[i, j] = np.nan - for i in range(nx2): - for j in range(ny2): - vals = lattice2[i, j] - if len(vals) > mincnt: - lattice2[i, j] = reduce_C_function(vals) - else: - lattice2[i, j] = np.nan + Cs_at_i2[i2[i]].append(C[i]) + if mincnt is None: + mincnt = 0 + accum = np.array( + [reduce_C_function(acc) if len(acc) > mincnt else np.nan + for Cs_at_i in [Cs_at_i1, Cs_at_i2] + for acc in Cs_at_i[1:]], # [1:] drops out-of-range points. + float) - accum = np.concatenate([lattice1.astype(float).ravel(), - lattice2.astype(float).ravel()]) - good_idxs = ~np.isnan(accum) + good_idxs = ~np.isnan(accum) offsets = np.zeros((n, 2), float) offsets[:nx1 * ny1, 0] = np.repeat(np.arange(nx1), ny1) @@ -4892,8 +4942,9 @@ def reduce_C_function(C: array) -> float edgecolors=edgecolors, linewidths=linewidths, offsets=offsets, - transOffset=mtransforms.AffineDeltaTransform(self.transData), - ) + offset_transform=mtransforms.AffineDeltaTransform( + self.transData), + ) # Set normalizer if bins is 'log' if bins == 'log': @@ -4901,16 +4952,11 @@ def reduce_C_function(C: array) -> float _api.warn_external("Only one of 'bins' and 'norm' arguments " f"can be supplied, ignoring bins={bins}") else: - norm = mcolors.LogNorm() + norm = mcolors.LogNorm(vmin=vmin, vmax=vmax) + vmin = vmax = None bins = None - if isinstance(norm, mcolors.LogNorm): - if (accum == 0).any(): - # make sure we have no zeros - accum += 1 - - # autoscale the norm with current accum values if it hasn't - # been set + # autoscale the norm with current accum values if it hasn't been set if norm is not None: if norm.vmin is None and norm.vmax is None: norm.autoscale(accum) @@ -4927,7 +4973,7 @@ def reduce_C_function(C: array) -> float collection.set_cmap(cmap) collection.set_norm(norm) collection.set_alpha(alpha) - collection.update(kwargs) + collection._internal_update(kwargs) collection._scale_norm(norm, vmin, vmax) corners = ((xmin, ymin), (xmax, ymax)) @@ -4939,96 +4985,62 @@ def reduce_C_function(C: array) -> float if not marginals: return collection - if C is None: - C = np.ones(len(x)) + # Process marginals + bars = [] + for zname, z, zmin, zmax, zscale, nbins in [ + ("x", x, xmin, xmax, xscale, nx), + ("y", y, ymin, ymax, yscale, 2 * ny), + ]: - def coarse_bin(x, y, coarse): - ind = coarse.searchsorted(x).clip(0, len(coarse) - 1) - mus = np.zeros(len(coarse)) - for i in range(len(coarse)): - yi = y[ind == i] - if len(yi) > 0: - mu = reduce_C_function(yi) - else: - mu = np.nan - mus[i] = mu - return mus - - coarse = np.linspace(xmin, xmax, gridsize) - - xcoarse = coarse_bin(xorig, C, coarse) - valid = ~np.isnan(xcoarse) - verts, values = [], [] - for i, val in enumerate(xcoarse): - thismin = coarse[i] - if i < len(coarse) - 1: - thismax = coarse[i + 1] + if zscale == "log": + bin_edges = np.geomspace(zmin, zmax, nbins + 1) else: - thismax = thismin + np.diff(coarse)[-1] - - if not valid[i]: - continue - - verts.append([(thismin, 0), - (thismin, 0.05), - (thismax, 0.05), - (thismax, 0)]) - values.append(val) - - values = np.array(values) - trans = self.get_xaxis_transform(which='grid') - - hbar = mcoll.PolyCollection(verts, transform=trans, edgecolors='face') - - hbar.set_array(values) - hbar.set_cmap(cmap) - hbar.set_norm(norm) - hbar.set_alpha(alpha) - hbar.update(kwargs) - self.add_collection(hbar, autolim=False) - - coarse = np.linspace(ymin, ymax, gridsize) - ycoarse = coarse_bin(yorig, C, coarse) - valid = ~np.isnan(ycoarse) - verts, values = [], [] - for i, val in enumerate(ycoarse): - thismin = coarse[i] - if i < len(coarse) - 1: - thismax = coarse[i + 1] - else: - thismax = thismin + np.diff(coarse)[-1] - if not valid[i]: - continue - verts.append([(0, thismin), (0.0, thismax), - (0.05, thismax), (0.05, thismin)]) - values.append(val) - - values = np.array(values) - - trans = self.get_yaxis_transform(which='grid') - - vbar = mcoll.PolyCollection(verts, transform=trans, edgecolors='face') - vbar.set_array(values) - vbar.set_cmap(cmap) - vbar.set_norm(norm) - vbar.set_alpha(alpha) - vbar.update(kwargs) - self.add_collection(vbar, autolim=False) - - collection.hbar = hbar - collection.vbar = vbar + bin_edges = np.linspace(zmin, zmax, nbins + 1) + + verts = np.empty((nbins, 4, 2)) + verts[:, 0, 0] = verts[:, 1, 0] = bin_edges[:-1] + verts[:, 2, 0] = verts[:, 3, 0] = bin_edges[1:] + verts[:, 0, 1] = verts[:, 3, 1] = .00 + verts[:, 1, 1] = verts[:, 2, 1] = .05 + if zname == "y": + verts = verts[:, :, ::-1] # Swap x and y. + + # Sort z-values into bins defined by bin_edges. + bin_idxs = np.searchsorted(bin_edges, z) - 1 + values = np.empty(nbins) + for i in range(nbins): + # Get C-values for each bin, and compute bin value with + # reduce_C_function. + ci = C[bin_idxs == i] + values[i] = reduce_C_function(ci) if len(ci) > 0 else np.nan + + mask = ~np.isnan(values) + verts = verts[mask] + values = values[mask] + + trans = getattr(self, f"get_{zname}axis_transform")(which="grid") + bar = mcoll.PolyCollection( + verts, transform=trans, edgecolors="face") + bar.set_array(values) + bar.set_cmap(cmap) + bar.set_norm(norm) + bar.set_alpha(alpha) + bar._internal_update(kwargs) + bars.append(self.add_collection(bar, autolim=False)) + + collection.hbar, collection.vbar = bars def on_changed(collection): - hbar.set_cmap(collection.get_cmap()) - hbar.set_clim(collection.get_clim()) - vbar.set_cmap(collection.get_cmap()) - vbar.set_clim(collection.get_clim()) + collection.hbar.set_cmap(collection.get_cmap()) + collection.hbar.set_cmap(collection.get_cmap()) + collection.vbar.set_clim(collection.get_clim()) + collection.vbar.set_clim(collection.get_clim()) - collection.callbacksSM.connect('changed', on_changed) + collection.callbacks.connect('changed', on_changed) return collection - @docstring.dedent_interpd + @_docstring.dedent_interpd def arrow(self, x, y, dx, dy, **kwargs): """ Add an arrow to the Axes. @@ -5037,12 +5049,6 @@ def arrow(self, x, y, dx, dy, **kwargs): Parameters ---------- - x, y : float - The x and y coordinates of the arrow base. - - dx, dy : float - The length of the arrow along x and y direction. - %(FancyArrow)s Returns @@ -5073,44 +5079,40 @@ def arrow(self, x, y, dx, dy, **kwargs): self._request_autoscale_view() return a - @docstring.copy(mquiver.QuiverKey.__init__) - def quiverkey(self, Q, X, Y, U, label, **kw): - qk = mquiver.QuiverKey(Q, X, Y, U, label, **kw) + @_docstring.copy(mquiver.QuiverKey.__init__) + def quiverkey(self, Q, X, Y, U, label, **kwargs): + qk = mquiver.QuiverKey(Q, X, Y, U, label, **kwargs) self.add_artist(qk) return qk # Handle units for x and y, if they've been passed - def _quiver_units(self, args, kw): + def _quiver_units(self, args, kwargs): if len(args) > 3: x, y = args[0:2] - x, y = self._process_unit_info([("x", x), ("y", y)], kw) + x, y = self._process_unit_info([("x", x), ("y", y)], kwargs) return (x, y) + args[2:] return args # args can by a combination if X, Y, U, V, C and all should be replaced @_preprocess_data() - def quiver(self, *args, **kw): + @_docstring.dedent_interpd + def quiver(self, *args, **kwargs): + """%(quiver_doc)s""" # Make sure units are handled for x and y values - args = self._quiver_units(args, kw) - - q = mquiver.Quiver(self, *args, **kw) - + args = self._quiver_units(args, kwargs) + q = mquiver.Quiver(self, *args, **kwargs) self.add_collection(q, autolim=True) self._request_autoscale_view() return q - quiver.__doc__ = mquiver.Quiver.quiver_doc # args can be some combination of X, Y, U, V, C and all should be replaced @_preprocess_data() - @docstring.dedent_interpd - def barbs(self, *args, **kw): - """ - %(barbs_doc)s - """ + @_docstring.dedent_interpd + def barbs(self, *args, **kwargs): + """%(barbs_doc)s""" # Make sure units are handled for x and y values - args = self._quiver_units(args, kw) - - b = mquiver.Barbs(self, *args, **kw) + args = self._quiver_units(args, kwargs) + b = mquiver.Barbs(self, *args, **kwargs) self.add_collection(b, autolim=True) self._request_autoscale_view() return b @@ -5243,25 +5245,24 @@ def _fill_between_x_or_y( Other Parameters ---------------- + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER + **kwargs All other keyword arguments are passed on to `.PolyCollection`. They control the `.Polygon` properties: - %(PolyCollection_kwdoc)s + %(PolyCollection:kwdoc)s See Also -------- fill_between : Fill between two sets of y-values. fill_betweenx : Fill between two sets of x-values. - - Notes - ----- - .. [notes section required to get data note injection right] """ dep_dir = {"x": "y", "y": "x"}[ind_dir] - if not rcParams["_internal.classic_mode"]: + if not mpl.rcParams["_internal.classic_mode"]: kwargs = cbook.normalize_kwargs(kwargs, mcoll.Collection) if not any(c in kwargs for c in ("color", "facecolor")): kwargs["facecolor"] = \ @@ -5285,9 +5286,10 @@ def _fill_between_x_or_y( raise ValueError(f"where size ({where.size}) does not match " f"{ind_dir} size ({ind.size})") where = where & ~functools.reduce( - np.logical_or, map(np.ma.getmask, [ind, dep1, dep2])) + np.logical_or, map(np.ma.getmaskarray, [ind, dep1, dep2])) - ind, dep1, dep2 = np.broadcast_arrays(np.atleast_1d(ind), dep1, dep2) + ind, dep1, dep2 = np.broadcast_arrays( + np.atleast_1d(ind), dep1, dep2, subok=True) polys = [] for idx0, idx1 in cbook.contiguous_regions(where): @@ -5371,7 +5373,7 @@ def fill_between(self, x, y1, y2=0, where=None, interpolate=False, dir="horizontal", ind="x", dep="y" ) fill_between = _preprocess_data( - docstring.dedent_interpd(fill_between), + _docstring.dedent_interpd(fill_between), replace_names=["x", "y1", "y2", "where"]) def fill_betweenx(self, y, x1, x2=0, where=None, @@ -5385,14 +5387,21 @@ def fill_betweenx(self, y, x1, x2=0, where=None, dir="vertical", ind="y", dep="x" ) fill_betweenx = _preprocess_data( - docstring.dedent_interpd(fill_betweenx), + _docstring.dedent_interpd(fill_betweenx), replace_names=["y", "x1", "x2", "where"]) #### plotting z(x, y): imshow, pcolor and relatives, contour + + # Once this deprecation elapses, also move vmin, vmax right after norm, to + # match the signature of other methods returning ScalarMappables and keep + # the documentation for *norm*, *vmax* and *vmin* together. + @_api.make_keyword_only("3.5", "aspect") @_preprocess_data() + @_docstring.interpd def imshow(self, X, cmap=None, norm=None, aspect=None, - interpolation=None, alpha=None, vmin=None, vmax=None, - origin=None, extent=None, *, filternorm=True, filterrad=4.0, + interpolation=None, alpha=None, + vmin=None, vmax=None, origin=None, extent=None, *, + interpolation_stage=None, filternorm=True, filterrad=4.0, resample=None, url=None, **kwargs): """ Display data as an image, i.e., on a 2D regular raster. @@ -5427,15 +5436,17 @@ def imshow(self, X, cmap=None, norm=None, aspect=None, Out-of-range RGB(A) values are clipped. - cmap : str or `~matplotlib.colors.Colormap`, default: :rc:`image.cmap` - The Colormap instance or registered colormap name used to map - scalar data to colors. This parameter is ignored for RGB(A) data. + %(cmap_doc)s + + This parameter is ignored if *X* is RGB(A). + + %(norm_doc)s - norm : `~matplotlib.colors.Normalize`, optional - The `.Normalize` instance used to scale scalar data to the [0, 1] - range before mapping to colors using *cmap*. By default, a linear - scaling mapping the lowest value to 0 and the highest to 1 is used. - This parameter is ignored for RGB(A) data. + This parameter is ignored if *X* is RGB(A). + + %(vmin_vmax_doc)s + + This parameter is ignored if *X* is RGB(A). aspect : {'equal', 'auto'} or float, default: :rc:`image.aspect` The aspect ratio of the Axes. This parameter is particularly @@ -5484,18 +5495,17 @@ def imshow(self, X, cmap=None, norm=None, aspect=None, which can be set by *filterrad*. Additionally, the antigrain image resize filter is controlled by the parameter *filternorm*. + interpolation_stage : {'data', 'rgba'}, default: 'data' + If 'data', interpolation + is carried out on the data provided by the user. If 'rgba', the + interpolation is carried out after the colormapping has been + applied (visual interpolation). + alpha : float or array-like, optional The alpha blending value, between 0 (transparent) and 1 (opaque). If *alpha* is an array, the alpha blending values are applied pixel by pixel, and *alpha* must have the same shape as *X*. - vmin, vmax : float, optional - When using scalar data and no explicit *norm*, *vmin* and *vmax* - define the data range that the colormap covers. By default, - the colormap covers the complete value range of the supplied - data. It is deprecated to use *vmin*/*vmax* when *norm* is given. - When using RGB(A) data, parameters *vmin*/*vmax* are ignored. - origin : {'upper', 'lower'}, default: :rc:`image.origin` Place the [0, 0] index of the array in the upper left or lower left corner of the Axes. The convention (the default) 'upper' is @@ -5553,6 +5563,9 @@ def imshow(self, X, cmap=None, norm=None, aspect=None, Other Parameters ---------------- + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER + **kwargs : `~matplotlib.artist.Artist` properties These parameters are passed on to the constructor of the `.AxesImage` artist. @@ -5579,11 +5592,14 @@ def imshow(self, X, cmap=None, norm=None, aspect=None, (unassociated) alpha representation. """ if aspect is None: - aspect = rcParams['image.aspect'] + aspect = mpl.rcParams['image.aspect'] self.set_aspect(aspect) - im = mimage.AxesImage(self, cmap, norm, interpolation, origin, extent, - filternorm=filternorm, filterrad=filterrad, - resample=resample, **kwargs) + im = mimage.AxesImage(self, cmap=cmap, norm=norm, + interpolation=interpolation, origin=origin, + extent=extent, filternorm=filternorm, + filterrad=filterrad, resample=resample, + interpolation_stage=interpolation_stage, + **kwargs) im.set_data(X) im.set_alpha(alpha) @@ -5600,7 +5616,7 @@ def imshow(self, X, cmap=None, norm=None, aspect=None, self.add_image(im) return im - def _pcolorargs(self, funcname, *args, shading='flat', **kwargs): + def _pcolorargs(self, funcname, *args, shading='auto', **kwargs): # - create X and Y if not present; # - reshape X and Y as needed if they are 1-D; # - check for proper sizes based on `shading` kwarg; @@ -5610,7 +5626,7 @@ def _pcolorargs(self, funcname, *args, shading='flat', **kwargs): _valid_shading = ['gouraud', 'nearest', 'flat', 'auto'] try: _api.check_in_list(_valid_shading, shading=shading) - except ValueError as err: + except ValueError: _api.warn_external(f"shading value '{shading}' not in list of " f"valid values {_valid_shading}. Setting " "shading='auto'.") @@ -5630,9 +5646,10 @@ def _pcolorargs(self, funcname, *args, shading='flat', **kwargs): if len(args) == 3: # Check x and y for bad data... C = np.asanyarray(args[2]) - X, Y = [cbook.safe_masked_invalid(a) for a in args[:2]] # unit conversion allows e.g. datetime objects as axis values + X, Y = args[:2] X, Y = self._process_unit_info([("x", X), ("y", Y)], kwargs) + X, Y = [cbook.safe_masked_invalid(a) for a in [X, Y]] if funcname == 'pcolormesh': if np.ma.is_masked(X) or np.ma.is_masked(Y): @@ -5660,9 +5677,8 @@ def _pcolorargs(self, funcname, *args, shading='flat', **kwargs): y = Y.reshape(Ny, 1) Y = y.repeat(Nx, axis=1) if X.shape != Y.shape: - raise TypeError( - 'Incompatible X, Y inputs to %s; see help(%s)' % ( - funcname, funcname)) + raise TypeError(f'Incompatible X, Y inputs to {funcname}; ' + f'see help({funcname})') if shading == 'auto': if ncols == Nx and nrows == Ny: @@ -5671,25 +5687,16 @@ def _pcolorargs(self, funcname, *args, shading='flat', **kwargs): shading = 'flat' if shading == 'flat': - if not (ncols in (Nx, Nx - 1) and nrows in (Ny, Ny - 1)): + if (Nx, Ny) != (ncols + 1, nrows + 1): raise TypeError('Dimensions of C %s are incompatible with' ' X (%d) and/or Y (%d); see help(%s)' % ( C.shape, Nx, Ny, funcname)) - if (ncols == Nx or nrows == Ny): - _api.warn_deprecated( - "3.3", message="shading='flat' when X and Y have the same " - "dimensions as C is deprecated since %(since)s. Either " - "specify the corners of the quadrilaterals with X and Y, " - "or pass shading='auto', 'nearest' or 'gouraud', or set " - "rcParams['pcolor.shading']. This will become an error " - "%(removal)s.") - C = C[:Ny - 1, :Nx - 1] else: # ['nearest', 'gouraud']: if (Nx, Ny) != (ncols, nrows): raise TypeError('Dimensions of C %s are incompatible with' ' X (%d) and/or Y (%d); see help(%s)' % ( C.shape, Nx, Ny, funcname)) - if shading in ['nearest', 'auto']: + if shading == 'nearest': # grid is specified at the center, so define corners # at the midpoints between the grid centers and then use the # flat algorithm. @@ -5725,8 +5732,20 @@ def _interp_grid(X): C = cbook.safe_masked_invalid(C) return X, Y, C, shading + def _pcolor_grid_deprecation_helper(self): + grid_active = any(axis._major_tick_kw["gridOn"] + for axis in self._axis_map.values()) + # explicit is-True check because get_axisbelow() can also be 'line' + grid_hidden_by_pcolor = self.get_axisbelow() is True + if grid_active and not grid_hidden_by_pcolor: + _api.warn_deprecated( + "3.5", message="Auto-removal of grids by pcolor() and " + "pcolormesh() is deprecated since %(since)s and will be " + "removed %(removal)s; please call grid(False) first.") + self.grid(False) + @_preprocess_data() - @docstring.dedent_interpd + @_docstring.dedent_interpd def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None, vmin=None, vmax=None, **kwargs): r""" @@ -5750,7 +5769,8 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None, Parameters ---------- C : 2D array-like - The color-mapped values. + The color-mapped values. Color-mapping is controlled by *cmap*, + *norm*, *vmin*, and *vmax*. X, Y : array-like, optional The coordinates of the corners of quadrilaterals of a pcolormesh:: @@ -5779,9 +5799,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 @@ -5798,21 +5817,11 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None, See :doc:`/gallery/images_contours_and_fields/pcolormesh_grids` for more description. - cmap : str or `~matplotlib.colors.Colormap`, default: :rc:`image.cmap` - A Colormap instance or registered colormap name. The colormap - maps the *C* values to colors. + %(cmap_doc)s - norm : `~matplotlib.colors.Normalize`, optional - The Normalize instance scales the data values to the canonical - colormap range [0, 1] for mapping to colors. By default, the data - range is mapped to the colorbar range using linear scaling. + %(norm_doc)s - vmin, vmax : float, default: None - The colorbar range. If *None*, suitable min/max values are - automatically chosen by the `~.Normalize` instance (defaults to - the respective min/max values of *C* in case of the default linear - scaling). - It is deprecated to use *vmin*/*vmax* when *norm* is given. + %(vmin_vmax_doc)s edgecolors : {'none', None, 'face', color, color sequence}, optional The color of the edges. Defaults to 'none'. Possible values: @@ -5848,11 +5857,14 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None, Stroking the edges may be preferred if *alpha* is 1, but will cause artifacts otherwise. + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER + **kwargs Additionally, the following arguments are allowed. They are passed along to the `~matplotlib.collections.PolyCollection` constructor: - %(PolyCollection_kwdoc)s + %(PolyCollection:kwdoc)s See Also -------- @@ -5880,7 +5892,7 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None, """ if shading is None: - shading = rcParams['pcolor.shading'] + shading = mpl.rcParams['pcolor.shading'] shading = shading.lower() X, Y, C, shading = self._pcolorargs('pcolor', *args, shading=shading, kwargs=kwargs) @@ -5933,14 +5945,10 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None, kwargs.setdefault('snap', False) - collection = mcoll.PolyCollection(verts, **kwargs) - - collection.set_alpha(alpha) - collection.set_array(C) - collection.set_cmap(cmap) - collection.set_norm(norm) + collection = mcoll.PolyCollection( + verts, array=C, cmap=cmap, norm=norm, alpha=alpha, **kwargs) collection._scale_norm(norm, vmin, vmax) - self.grid(False) + self._pcolor_grid_deprecation_helper() x = X.compressed() y = Y.compressed() @@ -5972,7 +5980,7 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None, return collection @_preprocess_data() - @docstring.dedent_interpd + @_docstring.dedent_interpd def pcolormesh(self, *args, alpha=None, norm=None, cmap=None, vmin=None, vmax=None, shading=None, antialiased=False, **kwargs): """ @@ -5994,7 +6002,8 @@ def pcolormesh(self, *args, alpha=None, norm=None, cmap=None, vmin=None, Parameters ---------- C : 2D array-like - The color-mapped values. + The color-mapped values. Color-mapping is controlled by *cmap*, + *norm*, *vmin*, and *vmax*. X, Y : array-like, optional The coordinates of the corners of quadrilaterals of a pcolormesh:: @@ -6025,21 +6034,11 @@ def pcolormesh(self, *args, alpha=None, norm=None, cmap=None, vmin=None, expanded as needed into the appropriate 2D arrays, making a rectangular grid. - cmap : str or `~matplotlib.colors.Colormap`, default: :rc:`image.cmap` - A Colormap instance or registered colormap name. The colormap - maps the *C* values to colors. + %(cmap_doc)s - norm : `~matplotlib.colors.Normalize`, optional - The Normalize instance scales the data values to the canonical - colormap range [0, 1] for mapping to colors. By default, the data - range is mapped to the colorbar range using linear scaling. + %(norm_doc)s - vmin, vmax : float, default: None - The colorbar range. If *None*, suitable min/max values are - automatically chosen by the `~.Normalize` instance (defaults to - the respective min/max values of *C* in case of the default linear - scaling). - It is deprecated to use *vmin*/*vmax* when *norm* is given. + %(vmin_vmax_doc)s edgecolors : {'none', None, 'face', color, color sequence}, optional The color of the edges. Defaults to 'none'. Possible values: @@ -6082,7 +6081,7 @@ def pcolormesh(self, *args, alpha=None, norm=None, cmap=None, vmin=None, snap : bool, default: False Whether to snap the mesh to pixel boundaries. - rasterized: bool, optional + rasterized : bool, optional Rasterize the pcolormesh when drawing vector graphics. This can speed up rendering and produce smaller files for large data sets. See also :doc:`/gallery/misc/rasterization_demo`. @@ -6093,11 +6092,14 @@ def pcolormesh(self, *args, alpha=None, norm=None, cmap=None, vmin=None, Other Parameters ---------------- + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER + **kwargs Additionally, the following arguments are allowed. They are passed along to the `~matplotlib.collections.QuadMesh` constructor: - %(QuadMesh_kwdoc)s + %(QuadMesh:kwdoc)s See Also -------- @@ -6154,31 +6156,25 @@ def pcolormesh(self, *args, alpha=None, norm=None, cmap=None, vmin=None, """ if shading is None: - shading = rcParams['pcolor.shading'] + shading = mpl.rcParams['pcolor.shading'] shading = shading.lower() kwargs.setdefault('edgecolors', 'none') X, Y, C, shading = self._pcolorargs('pcolormesh', *args, shading=shading, kwargs=kwargs) - Ny, Nx = X.shape - X = X.ravel() - Y = Y.ravel() - - # convert to one dimensional arrays + coords = np.stack([X, Y], axis=-1) + # convert to one dimensional array C = C.ravel() - coords = np.column_stack((X, Y)).astype(float, copy=False) - collection = mcoll.QuadMesh(Nx - 1, Ny - 1, coords, - antialiased=antialiased, shading=shading, - **kwargs) - snap = kwargs.get('snap', rcParams['pcolormesh.snap']) - collection.set_snap(snap) - collection.set_alpha(alpha) - collection.set_array(C) - collection.set_cmap(cmap) - collection.set_norm(norm) + + kwargs.setdefault('snap', mpl.rcParams['pcolormesh.snap']) + + collection = mcoll.QuadMesh( + 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() - self.grid(False) + coords = coords.reshape(-1, 2) # flatten the grid structure; keep x, y # Transform from native to data coordinates? t = collection._transform @@ -6202,7 +6198,7 @@ def pcolormesh(self, *args, alpha=None, norm=None, cmap=None, vmin=None, return collection @_preprocess_data() - @docstring.dedent_interpd + @_docstring.dedent_interpd def pcolorfast(self, *args, alpha=None, norm=None, cmap=None, vmin=None, vmax=None, **kwargs): """ @@ -6232,8 +6228,8 @@ def pcolorfast(self, *args, alpha=None, norm=None, cmap=None, vmin=None, C : array-like The image data. Supported array shapes are: - - (M, N): an image with scalar data. The data is visualized - using a colormap. + - (M, N): an image with scalar data. Color-mapping is controlled + by *cmap*, *norm*, *vmin*, and *vmax*. - (M, N, 3): an image with RGB values (0-1 float or 0-255 int). - (M, N, 4): an image with RGBA values (0-1 float or 0-255 int), i.e. including transparency. @@ -6276,21 +6272,17 @@ def pcolorfast(self, *args, alpha=None, norm=None, cmap=None, vmin=None, These arguments can only be passed positionally. - cmap : str or `~matplotlib.colors.Colormap`, default: :rc:`image.cmap` - A Colormap instance or registered colormap name. The colormap - maps the *C* values to colors. + %(cmap_doc)s + + This parameter is ignored if *C* is RGB(A). + + %(norm_doc)s - norm : `~matplotlib.colors.Normalize`, optional - The Normalize instance scales the data values to the canonical - colormap range [0, 1] for mapping to colors. By default, the data - range is mapped to the colorbar range using linear scaling. + This parameter is ignored if *C* is RGB(A). - vmin, vmax : float, default: None - The colorbar range. If *None*, suitable min/max values are - automatically chosen by the `~.Normalize` instance (defaults to - the respective min/max values of *C* in case of the default linear - scaling). - It is deprecated to use *vmin*/*vmax* when *norm* is given. + %(vmin_vmax_doc)s + + This parameter is ignored if *C* is RGB(A). alpha : float, default: None The alpha blending value, between 0 (transparent) and 1 (opaque). @@ -6309,13 +6301,12 @@ def pcolorfast(self, *args, alpha=None, norm=None, cmap=None, vmin=None, Other Parameters ---------------- + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER + **kwargs Supported additional parameters depend on the type of grid. See return types of *image* for further description. - - Notes - ----- - .. [notes section required to get data note injection right] """ C = args[-1] @@ -6356,7 +6347,7 @@ def pcolorfast(self, *args, alpha=None, norm=None, cmap=None, vmin=None, else: raise ValueError("C must be 2D or 3D") collection = mcoll.QuadMesh( - nc, nr, coords, **qm_kwargs, + coords, **qm_kwargs, alpha=alpha, cmap=cmap, norm=norm, antialiased=False, edgecolors="none") self.add_collection(collection, autolim=False) @@ -6367,7 +6358,7 @@ def pcolorfast(self, *args, alpha=None, norm=None, cmap=None, vmin=None, extent = xl, xr, yb, yt = x[0], x[-1], y[0], y[-1] if style == "image": im = mimage.AxesImage( - self, cmap, norm, + self, cmap=cmap, norm=norm, data=C, alpha=alpha, extent=extent, interpolation='nearest', origin='lower', **kwargs) @@ -6393,32 +6384,36 @@ def pcolorfast(self, *args, alpha=None, norm=None, cmap=None, vmin=None, return ret @_preprocess_data() + @_docstring.dedent_interpd def contour(self, *args, **kwargs): - kwargs['filled'] = False - contours = mcontour.QuadContourSet(self, *args, **kwargs) - self._request_autoscale_view() - return contours - contour.__doc__ = """ + """ Plot contour lines. Call signature:: contour([X, Y,] Z, [levels], **kwargs) - """ + mcontour.QuadContourSet._contour_doc - - @_preprocess_data() - def contourf(self, *args, **kwargs): - kwargs['filled'] = True + %(contour_doc)s + """ + kwargs['filled'] = False contours = mcontour.QuadContourSet(self, *args, **kwargs) self._request_autoscale_view() return contours - contourf.__doc__ = """ + + @_preprocess_data() + @_docstring.dedent_interpd + def contourf(self, *args, **kwargs): + """ Plot filled contours. Call signature:: contourf([X, Y,] Z, [levels], **kwargs) - """ + mcontour.QuadContourSet._contour_doc + %(contour_doc)s + """ + kwargs['filled'] = True + contours = mcontour.QuadContourSet(self, *args, **kwargs) + self._request_autoscale_view() + return contours def clabel(self, CS, levels=None, **kwargs): """ @@ -6428,7 +6423,7 @@ def clabel(self, CS, levels=None, **kwargs): Parameters ---------- - CS : `~.ContourSet` instance + CS : `.ContourSet` instance Line contours to label. levels : array-like, optional @@ -6448,23 +6443,33 @@ def hist(self, x, bins=None, range=None, density=False, weights=None, orientation='vertical', rwidth=None, log=False, color=None, label=None, stacked=False, **kwargs): """ - Plot a histogram. + Compute and plot a histogram. - Compute and draw the histogram of *x*. The return value is a tuple - (*n*, *bins*, *patches*) or ([*n0*, *n1*, ...], *bins*, [*patches0*, - *patches1*, ...]) if the input contains multiple data. See the - documentation of the *weights* parameter to draw a histogram of - already-binned data. + This method uses `numpy.histogram` to bin the data in *x* and count the + number of values in each bin, then draws the distribution either as a + `.BarContainer` or `.Polygon`. The *bins*, *range*, *density*, and + *weights* parameters are forwarded to `numpy.histogram`. - Multiple data can be provided via *x* as a list of datasets - of potentially different length ([*x0*, *x1*, ...]), or as - a 2D ndarray in which each column is a dataset. Note that - the ndarray form is transposed relative to the list form. + If the data has already been binned and counted, use `~.bar` or + `~.stairs` to plot the distribution:: - Masked arrays are not supported. + counts, bins = np.histogram(x) + plt.stairs(counts, bins) + + Alternatively, plot pre-computed bins and counts using ``hist()`` by + treating each bin as a single point with a weight equal to its count:: + + plt.hist(bins[:-1], bins, weights=counts) + + The data input *x* can be a singular array, a list of datasets of + potentially different lengths ([*x0*, *x1*, ...]), or a 2D ndarray in + which each column is a dataset. Note that the ndarray form is + transposed relative to the list form. If the input is an array, then + the return value is a tuple (*n*, *bins*, *patches*); if the input is a + sequence of arrays, then the return value is a tuple + ([*n0*, *n1*, ...], *bins*, [*patches0*, *patches1*, ...]). - The *bins*, *range*, *weights*, and *density* parameters behave as in - `numpy.histogram`. + Masked arrays are not supported. Parameters ---------- @@ -6518,15 +6523,6 @@ def hist(self, x, bins=None, range=None, density=False, weights=None, normalized, so that the integral of the density over the range remains 1. - This parameter can be used to draw a histogram of data that has - already been binned, e.g. using `numpy.histogram` (by treating each - bin as a single point with a weight equal to its count) :: - - counts, bins = np.histogram(data) - plt.hist(bins[:-1], bins, weights=counts) - - (or you may alternatively use `~.bar()`). - cumulative : bool or -1, default: False If ``True``, then a histogram is computed where each bin gives the counts in that bin plus all bins for smaller values. The last bin @@ -6614,18 +6610,22 @@ def hist(self, x, bins=None, range=None, density=False, weights=None, Other Parameters ---------------- + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER + **kwargs `~matplotlib.patches.Patch` properties See Also -------- - hist2d : 2D histograms + hist2d : 2D histogram with rectangular bins + hexbin : 2D histogram with hexagonal bins Notes ----- - For large numbers of bins (>1000), 'step' and 'stepfilled' can be - significantly faster than 'bar' and 'barstacked'. - + For large numbers of bins (>1000), plotting can be significantly faster + if *histtype* is set to 'step' or 'stepfilled' rather than 'bar' or + 'barstacked'. """ # Avoid shadowing the builtin. bin_range = range @@ -6635,7 +6635,7 @@ def hist(self, x, bins=None, range=None, density=False, weights=None, x = [x] if bins is None: - bins = rcParams['hist.bins'] + bins = mpl.rcParams['hist.bins'] # Validate string inputs here to avoid cluttering subsequent code. _api.check_in_list(['bar', 'barstacked', 'step', 'stepfilled'], @@ -6737,6 +6737,7 @@ def hist(self, x, bins=None, range=None, density=False, weights=None, m, bins = np.histogram(x[i], bins, weights=w[i], **hist_kwargs) tops.append(m) tops = np.array(tops, float) # causes problems later if it's an int + bins = np.array(bins, float) # causes problems if float16 if stacked: tops = tops.cumsum(axis=0) # If a stacked density plot, normalize so the area of all the @@ -6761,7 +6762,7 @@ def hist(self, x, bins=None, range=None, density=False, weights=None, if rwidth is not None: dr = np.clip(rwidth, 0, 1) elif (len(tops) > 1 and - ((not stacked) or rcParams['_internal.classic_mode'])): + ((not stacked) or mpl.rcParams['_internal.classic_mode'])): dr = 0.8 else: dr = 1.0 @@ -6886,11 +6887,11 @@ def hist(self, x, bins=None, range=None, density=False, weights=None, for patch, lbl in itertools.zip_longest(patches, labels): if patch: p = patch[0] - p.update(kwargs) + p._internal_update(kwargs) if lbl is not None: p.set_label(lbl) for p in patch[1:]: - p.update(kwargs) + p._internal_update(kwargs) p.set_label('_nolegend_') if nx == 1: @@ -6935,6 +6936,9 @@ def stairs(self, values, edges=None, *, Other Parameters ---------------- + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER + **kwargs `~matplotlib.patches.StepPatch` properties @@ -6945,7 +6949,7 @@ def stairs(self, values, edges=None, *, else: _color = self._get_lines.get_next_color() if fill: - kwargs.setdefault('edgecolor', 'none') + kwargs.setdefault('linewidth', 0) kwargs.setdefault('facecolor', _color) else: kwargs.setdefault('edgecolor', _color) @@ -6975,7 +6979,7 @@ def stairs(self, values, edges=None, *, return patch @_preprocess_data(replace_names=["x", "y", "weights"]) - @docstring.dedent_interpd + @_docstring.dedent_interpd def hist2d(self, x, y, bins=10, range=None, density=False, weights=None, cmin=None, cmax=None, **kwargs): """ @@ -7034,20 +7038,18 @@ def hist2d(self, x, y, bins=10, range=None, density=False, weights=None, Other Parameters ---------------- - cmap : Colormap or str, optional - A `.colors.Colormap` instance. If not set, use rc settings. + %(cmap_doc)s - norm : Normalize, optional - A `.colors.Normalize` instance is used to - scale luminance data to ``[0, 1]``. If not set, defaults to - `.colors.Normalize()`. + %(norm_doc)s - vmin/vmax : None or scalar, optional - Arguments passed to the `~.colors.Normalize` instance. + %(vmin_vmax_doc)s alpha : ``0 <= scalar <= 1`` or ``None``, optional The alpha blending value. + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER + **kwargs Additional parameters are passed along to the `~.Axes.pcolormesh` method and `~matplotlib.collections.QuadMesh` @@ -7056,6 +7058,7 @@ def hist2d(self, x, y, bins=10, range=None, density=False, weights=None, See Also -------- hist : 1D histogram plotting + hexbin : 2D histogram with hexagonal bins Notes ----- @@ -7083,7 +7086,7 @@ def hist2d(self, x, y, bins=10, range=None, density=False, weights=None, return h, xedges, yedges, pc @_preprocess_data(replace_names=["x"]) - @docstring.dedent_interpd + @_docstring.dedent_interpd def psd(self, x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, noverlap=None, pad_to=None, sides=None, scale_by_freq=None, return_line=None, **kwargs): @@ -7135,10 +7138,13 @@ def psd(self, x, NFFT=None, Fs=None, Fc=None, detrend=None, Other Parameters ---------------- + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER + **kwargs Keyword arguments control the `.Line2D` properties: - %(Line2D_kwdoc)s + %(Line2D:kwdoc)s See Also -------- @@ -7179,12 +7185,9 @@ def psd(self, x, NFFT=None, Fs=None, Fc=None, detrend=None, self.set_xlabel('Frequency') self.set_ylabel('Power Spectral Density (%s)' % psd_units) self.grid(True) - vmin, vmax = self.viewLim.intervaly - intv = vmax - vmin - logi = int(np.log10(intv)) - if logi == 0: - logi = .1 - step = 10 * logi + + vmin, vmax = self.get_ybound() + step = max(10 * int(np.log10(vmax - vmin)), 1) ticks = np.arange(math.floor(vmin), math.ceil(vmax) + 1, step) self.set_yticks(ticks) @@ -7194,7 +7197,7 @@ def psd(self, x, NFFT=None, Fs=None, Fc=None, detrend=None, return pxx, freqs, line @_preprocess_data(replace_names=["x", "y"], label_namer="y") - @docstring.dedent_interpd + @_docstring.dedent_interpd def csd(self, x, y, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, noverlap=None, pad_to=None, sides=None, scale_by_freq=None, return_line=None, **kwargs): @@ -7248,10 +7251,13 @@ def csd(self, x, y, NFFT=None, Fs=None, Fc=None, detrend=None, Other Parameters ---------------- + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER + **kwargs Keyword arguments control the `.Line2D` properties: - %(Line2D_kwdoc)s + %(Line2D:kwdoc)s See Also -------- @@ -7281,11 +7287,9 @@ def csd(self, x, y, NFFT=None, Fs=None, Fc=None, detrend=None, self.set_xlabel('Frequency') self.set_ylabel('Cross Spectrum Magnitude (dB)') self.grid(True) - vmin, vmax = self.viewLim.intervaly - - intv = vmax - vmin - step = 10 * int(np.log10(intv)) + vmin, vmax = self.get_ybound() + step = max(10 * int(np.log10(vmax - vmin)), 1) ticks = np.arange(math.floor(vmin), math.ceil(vmax) + 1, step) self.set_yticks(ticks) @@ -7295,7 +7299,7 @@ def csd(self, x, y, NFFT=None, Fs=None, Fc=None, detrend=None, return pxy, freqs, line @_preprocess_data(replace_names=["x"]) - @docstring.dedent_interpd + @_docstring.dedent_interpd def magnitude_spectrum(self, x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, scale=None, **kwargs): @@ -7338,10 +7342,13 @@ def magnitude_spectrum(self, x, Fs=None, Fc=None, window=None, Other Parameters ---------------- + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER + **kwargs Keyword arguments control the `.Line2D` properties: - %(Line2D_kwdoc)s + %(Line2D:kwdoc)s See Also -------- @@ -7378,7 +7385,7 @@ def magnitude_spectrum(self, x, Fs=None, Fc=None, window=None, return spec, freqs, line @_preprocess_data(replace_names=["x"]) - @docstring.dedent_interpd + @_docstring.dedent_interpd def angle_spectrum(self, x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, **kwargs): """ @@ -7415,10 +7422,13 @@ def angle_spectrum(self, x, Fs=None, Fc=None, window=None, Other Parameters ---------------- + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER + **kwargs Keyword arguments control the `.Line2D` properties: - %(Line2D_kwdoc)s + %(Line2D:kwdoc)s See Also -------- @@ -7444,7 +7454,7 @@ def angle_spectrum(self, x, Fs=None, Fc=None, window=None, return spec, freqs, lines[0] @_preprocess_data(replace_names=["x"]) - @docstring.dedent_interpd + @_docstring.dedent_interpd def phase_spectrum(self, x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, **kwargs): """ @@ -7481,10 +7491,13 @@ def phase_spectrum(self, x, Fs=None, Fc=None, window=None, Other Parameters ---------------- + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER + **kwargs Keyword arguments control the `.Line2D` properties: - %(Line2D_kwdoc)s + %(Line2D:kwdoc)s See Also -------- @@ -7510,15 +7523,14 @@ def phase_spectrum(self, x, Fs=None, Fc=None, window=None, return spec, freqs, lines[0] @_preprocess_data(replace_names=["x", "y"]) - @docstring.dedent_interpd + @_docstring.dedent_interpd def cohere(self, x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, window=mlab.window_hanning, noverlap=0, pad_to=None, sides='default', scale_by_freq=None, **kwargs): r""" Plot the coherence between *x* and *y*. - Plot the coherence between *x* and *y*. Coherence is the - normalized cross spectral density: + Coherence is the normalized cross spectral density: .. math:: @@ -7548,10 +7560,13 @@ def cohere(self, x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, Other Parameters ---------------- + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER + **kwargs Keyword arguments control the `.Line2D` properties: - %(Line2D_kwdoc)s + %(Line2D:kwdoc)s References ---------- @@ -7560,7 +7575,8 @@ def cohere(self, x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, """ cxy, freqs = mlab.cohere(x=x, y=y, NFFT=NFFT, Fs=Fs, detrend=detrend, window=window, noverlap=noverlap, - scale_by_freq=scale_by_freq) + scale_by_freq=scale_by_freq, sides=sides, + pad_to=pad_to) freqs += Fc self.plot(freqs, cxy, **kwargs) @@ -7571,7 +7587,7 @@ def cohere(self, x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, return cxy, freqs @_preprocess_data(replace_names=["x"]) - @docstring.dedent_interpd + @_docstring.dedent_interpd def specgram(self, x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, noverlap=None, cmap=None, xextent=None, pad_to=None, sides=None, @@ -7626,6 +7642,9 @@ def specgram(self, x, NFFT=None, Fs=None, Fc=None, detrend=None, right border of the last bin. Note that for *noverlap>0* the width of the bins is smaller than those of the segments. + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER + **kwargs Additional keyword arguments are passed on to `~.axes.Axes.imshow` which makes the specgram image. The origin keyword argument @@ -7702,7 +7721,7 @@ def specgram(self, x, NFFT=None, Fs=None, Fc=None, detrend=None, else: Z = 20. * np.log10(spec) else: - raise ValueError('Unknown scale %s', scale) + raise ValueError(f'Unknown scale {scale!r}') Z = np.flipud(Z) @@ -7724,7 +7743,7 @@ def specgram(self, x, NFFT=None, Fs=None, Fc=None, detrend=None, return spec, freqs, t, im - @docstring.dedent_interpd + @_docstring.dedent_interpd def spy(self, Z, precision=0, marker=None, markersize=None, aspect='equal', origin="upper", **kwargs): """ @@ -7801,7 +7820,7 @@ def spy(self, Z, precision=0, marker=None, markersize=None, For the marker style, you can pass any `.Line2D` property except for *linestyle*: - %(Line2D_kwdoc)s + %(Line2D:kwdoc)s """ if marker is None and markersize is None and hasattr(Z, 'tocoo'): marker = 's' @@ -7856,7 +7875,7 @@ def spy(self, Z, precision=0, marker=None, markersize=None, self.title.set_y(1.05) if origin == "upper": self.xaxis.tick_top() - else: + else: # lower self.xaxis.tick_bottom() self.xaxis.set_ticks_position('both') self.xaxis.set_major_locator( @@ -7968,8 +7987,12 @@ def violinplot(self, dataset, positions=None, vert=True, widths=0.5, The method used to calculate the estimator bandwidth. This can be 'scott', 'silverman', a scalar constant or a callable. If a scalar, this will be used directly as `kde.factor`. If a - callable, it should take a `GaussianKDE` instance as its only - parameter and return a scalar. If None (default), 'scott' is used. + callable, it should take a `matplotlib.mlab.GaussianKDE` instance as + its only parameter and return a scalar. If None (default), 'scott' + is used. + + data : indexable object, optional + DATA_PARAMETER_PLACEHOLDER Returns ------- @@ -8003,8 +8026,8 @@ def violinplot(self, dataset, positions=None, vert=True, widths=0.5, """ def _kde_method(X, coords): - if hasattr(X, 'values'): # support pandas.Series - X = X.values + # Unpack in case of e.g. Pandas or xarray object + X = cbook._unpack_to_numpy(X) # fallback gracefully if the vector contains only one value if np.all(X[0] == X): return (X[0] == coords).astype(float) @@ -8102,7 +8125,6 @@ def violin(self, vpstats, positions=None, vert=True, widths=0.5, - ``cquantiles``: A `~.collections.LineCollection` instance created to identify the quantiles values of each of the violin's distribution. - """ # Statistical quantities to be plotted on the violins @@ -8110,10 +8132,11 @@ def violin(self, vpstats, positions=None, vert=True, widths=0.5, mins = [] maxes = [] medians = [] - quantiles = np.asarray([]) + quantiles = [] + + qlens = [] # Number of quantiles in each dataset. - # Collections to be returned - artists = {} + artists = {} # Collections to be returned N = len(vpstats) datashape_message = ("List of violinplot statistics and `{0}` " @@ -8131,84 +8154,56 @@ def violin(self, vpstats, positions=None, vert=True, widths=0.5, elif len(widths) != N: raise ValueError(datashape_message.format("widths")) - # Calculate ranges for statistics lines - pmins = -0.25 * np.array(widths) + positions - pmaxes = 0.25 * np.array(widths) + positions + # Calculate ranges for statistics lines (shape (2, N)). + line_ends = [[-0.25], [0.25]] * np.array(widths) + positions + + # Colors. + if mpl.rcParams['_internal.classic_mode']: + fillcolor = 'y' + linecolor = 'r' + else: + fillcolor = linecolor = self._get_lines.get_next_color() # Check whether we are rendering vertically or horizontally if vert: fill = self.fill_betweenx - perp_lines = self.hlines - par_lines = self.vlines + perp_lines = functools.partial(self.hlines, colors=linecolor) + par_lines = functools.partial(self.vlines, colors=linecolor) else: fill = self.fill_between - perp_lines = self.vlines - par_lines = self.hlines - - if rcParams['_internal.classic_mode']: - fillcolor = 'y' - edgecolor = 'r' - else: - fillcolor = edgecolor = self._get_lines.get_next_color() + perp_lines = functools.partial(self.vlines, colors=linecolor) + par_lines = functools.partial(self.hlines, colors=linecolor) # Render violins bodies = [] for stats, pos, width in zip(vpstats, positions, widths): - # The 0.5 factor reflects the fact that we plot from v-p to - # v+p + # The 0.5 factor reflects the fact that we plot from v-p to v+p. vals = np.array(stats['vals']) vals = 0.5 * width * vals / vals.max() - bodies += [fill(stats['coords'], - -vals + pos, - vals + pos, - facecolor=fillcolor, - alpha=0.3)] + bodies += [fill(stats['coords'], -vals + pos, vals + pos, + facecolor=fillcolor, alpha=0.3)] means.append(stats['mean']) mins.append(stats['min']) maxes.append(stats['max']) medians.append(stats['median']) - q = stats.get('quantiles') - if q is not None: - # If exist key quantiles, assume it's a list of floats - quantiles = np.concatenate((quantiles, q)) + q = stats.get('quantiles') # a list of floats, or None + if q is None: + q = [] + quantiles.extend(q) + qlens.append(len(q)) artists['bodies'] = bodies - # Render means - if showmeans: - artists['cmeans'] = perp_lines(means, pmins, pmaxes, - colors=edgecolor) - - # Render extrema - if showextrema: - artists['cmaxes'] = perp_lines(maxes, pmins, pmaxes, - colors=edgecolor) - artists['cmins'] = perp_lines(mins, pmins, pmaxes, - colors=edgecolor) - artists['cbars'] = par_lines(positions, mins, maxes, - colors=edgecolor) - - # Render medians - if showmedians: - artists['cmedians'] = perp_lines(medians, - pmins, - pmaxes, - colors=edgecolor) - - # Render quantile values - if quantiles.size > 0: - # Recalculate ranges for statistics lines for quantiles. - # ppmins are the left end of quantiles lines - ppmins = np.asarray([]) - # pmaxes are the right end of quantiles lines - ppmaxs = np.asarray([]) - for stats, cmin, cmax in zip(vpstats, pmins, pmaxes): - q = stats.get('quantiles') - if q is not None: - ppmins = np.concatenate((ppmins, [cmin] * np.size(q))) - ppmaxs = np.concatenate((ppmaxs, [cmax] * np.size(q))) - # Start rendering - artists['cquantiles'] = perp_lines(quantiles, ppmins, ppmaxs, - colors=edgecolor) + if showmeans: # Render means + artists['cmeans'] = perp_lines(means, *line_ends) + if showextrema: # Render extrema + artists['cmaxes'] = perp_lines(maxes, *line_ends) + artists['cmins'] = perp_lines(mins, *line_ends) + artists['cbars'] = par_lines(positions, mins, maxes) + if showmedians: # Render medians + artists['cmedians'] = perp_lines(medians, *line_ends) + if quantiles: # Render quantiles: each width is repeated qlen times. + artists['cquantiles'] = perp_lines( + quantiles, *np.repeat(line_ends, qlens, axis=1)) return artists @@ -8226,3 +8221,13 @@ def violin(self, vpstats, positions=None, vert=True, widths=0.5, tricontourf = mtri.tricontourf tripcolor = mtri.tripcolor triplot = mtri.triplot + + def _get_aspect_ratio(self): + """ + Convenience method to calculate the aspect ratio of the axes in + the display coordinate system. + """ + figure_size = self.get_figure().get_size_inches() + ll, ur = self.get_position() * figure_size + width, height = ur - ll + return height / (width * self.get_data_ratio()) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 728d22cd7d08..056ff7ea2ced 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -1,4 +1,4 @@ -from collections import OrderedDict +from collections.abc import MutableSequence from contextlib import ExitStack import functools import inspect @@ -11,22 +11,22 @@ import numpy as np import matplotlib as mpl -from matplotlib import _api, cbook +from matplotlib import _api, cbook, _docstring, offsetbox +import matplotlib.artist as martist +import matplotlib.axis as maxis from matplotlib.cbook import _OrderedSet, _check_1d, index_of -from matplotlib import docstring +import matplotlib.collections as mcoll import matplotlib.colors as mcolors +import matplotlib.font_manager as font_manager +import matplotlib.image as mimage import matplotlib.lines as mlines import matplotlib.patches as mpatches -import matplotlib.artist as martist -import matplotlib.transforms as mtransforms -import matplotlib.ticker as mticker -import matplotlib.axis as maxis +from matplotlib.rcsetup import cycler, validate_axisbelow import matplotlib.spines as mspines -import matplotlib.font_manager as font_manager +import matplotlib.table as mtable import matplotlib.text as mtext -import matplotlib.image as mimage -import matplotlib.path as mpath -from matplotlib.rcsetup import cycler, validate_axisbelow +import matplotlib.ticker as mticker +import matplotlib.transforms as mtransforms _log = logging.getLogger(__name__) @@ -94,16 +94,16 @@ def wrapper(self, *args, **kwargs): class _TransformedBoundsLocator: """ - Axes locator for `.Axes.inset_axes` and similarly positioned axes. + Axes locator for `.Axes.inset_axes` and similarly positioned Axes. The locator is a callable object used in `.Axes.set_aspect` to compute the - axes location depending on the renderer. + Axes location depending on the renderer. """ def __init__(self, bounds, transform): """ *bounds* (a ``[l, b, w, h]`` rectangle) and *transform* together - specify the position of the inset axes. + specify the position of the inset Axes. """ self._bounds = bounds self._transform = transform @@ -117,7 +117,7 @@ def __call__(self, ax, renderer): self._transform - ax.figure.transSubfigure) -def _process_plot_format(fmt): +def _process_plot_format(fmt, *, ambiguous_fmt_datakey=False): """ Convert a MATLAB style color/line style format string to a (*linestyle*, *marker*, *color*) tuple. @@ -162,31 +162,31 @@ def _process_plot_format(fmt): except ValueError: pass # No, not just a color. + errfmt = ("{!r} is neither a data key nor a valid format string ({})" + if ambiguous_fmt_datakey else + "{!r} is not a valid format string ({})") + i = 0 while i < len(fmt): c = fmt[i] if fmt[i:i+2] in mlines.lineStyles: # First, the two-char styles. if linestyle is not None: - raise ValueError( - 'Illegal format string "%s"; two linestyle symbols' % fmt) + raise ValueError(errfmt.format(fmt, "two linestyle symbols")) linestyle = fmt[i:i+2] i += 2 elif c in mlines.lineStyles: if linestyle is not None: - raise ValueError( - 'Illegal format string "%s"; two linestyle symbols' % fmt) + raise ValueError(errfmt.format(fmt, "two linestyle symbols")) linestyle = c i += 1 elif c in mlines.lineMarkers: if marker is not None: - raise ValueError( - 'Illegal format string "%s"; two marker symbols' % fmt) + raise ValueError(errfmt.format(fmt, "two marker symbols")) marker = c i += 1 elif c in mcolors.get_named_colors_mapping(): if color is not None: - raise ValueError( - 'Illegal format string "%s"; two color symbols' % fmt) + raise ValueError(errfmt.format(fmt, "two color symbols")) color = c i += 1 elif c == 'C' and i < len(fmt) - 1: @@ -195,7 +195,7 @@ def _process_plot_format(fmt): i += 2 else: raise ValueError( - 'Unrecognized character %c in format string' % c) + errfmt.format(fmt, f"unrecognized character {c!r}")) if linestyle is None and marker is None: linestyle = mpl.rcParams['lines.linestyle'] @@ -221,7 +221,7 @@ class _process_plot_var_args: def __init__(self, axes, command='plot'): self.axes = axes self.command = command - self.set_prop_cycle() + self.set_prop_cycle(None) def __getstate__(self): # note: it is not possible to pickle a generator (and thus a cycler). @@ -229,18 +229,13 @@ def __getstate__(self): def __setstate__(self, state): self.__dict__ = state.copy() - self.set_prop_cycle() + self.set_prop_cycle(None) - def set_prop_cycle(self, *args, **kwargs): - # Can't do `args == (None,)` as that crashes cycler. - if not (args or kwargs) or (len(args) == 1 and args[0] is None): - prop_cycler = mpl.rcParams['axes.prop_cycle'] - else: - prop_cycler = cycler(*args, **kwargs) - - self.prop_cycler = itertools.cycle(prop_cycler) - # This should make a copy - self._prop_keys = prop_cycler.keys + def set_prop_cycle(self, cycler): + if cycler is None: + cycler = mpl.rcParams['axes.prop_cycle'] + self.prop_cycler = itertools.cycle(cycler) + self._prop_keys = cycler.keys # This should make a copy def __call__(self, *args, data=None, **kwargs): self.axes._process_unit_info(kwargs=kwargs) @@ -297,6 +292,7 @@ def __call__(self, *args, data=None, **kwargs): kwargs["label"] = mpl._label_from_arg( replaced[label_namer_idx], args[label_namer_idx]) args = replaced + ambiguous_fmt_datakey = data is not None and len(args) == 2 if len(args) >= 4 and not cbook.is_scalar_or_string( kwargs.get("label")): @@ -312,7 +308,8 @@ def __call__(self, *args, data=None, **kwargs): if args and isinstance(args[0], str): this += args[0], args = args[1:] - yield from self._plot_args(this, kwargs) + yield from self._plot_args( + this, kwargs, ambiguous_fmt_datakey=ambiguous_fmt_datakey) def get_next_color(self): """Return the next color in the cycle.""" @@ -406,7 +403,8 @@ def _makefill(self, x, y, kw, kwargs): seg.set(**kwargs) return seg, kwargs - def _plot_args(self, tup, kwargs, return_kwargs=False): + def _plot_args(self, tup, kwargs, *, + return_kwargs=False, ambiguous_fmt_datakey=False): """ Process the arguments of ``plot([x], y, [fmt], **kwargs)`` calls. @@ -433,9 +431,13 @@ def _plot_args(self, tup, kwargs, return_kwargs=False): The keyword arguments passed to ``plot()``. return_kwargs : bool - If true, return the effective keyword arguments after label + Whether to also return the effective keyword arguments after label unpacking as well. + ambiguous_fmt_datakey : bool + Whether the format string in *tup* could also have been a + misspelled data key. + Returns ------- result @@ -449,7 +451,8 @@ def _plot_args(self, tup, kwargs, return_kwargs=False): if len(tup) > 1 and isinstance(tup[-1], str): # xy is tup with fmt stripped (could still be (y,) only) *xy, fmt = tup - linestyle, marker, color = _process_plot_format(fmt) + linestyle, marker, color = _process_plot_format( + fmt, ambiguous_fmt_datakey=ambiguous_fmt_datakey) elif len(tup) == 3: raise ValueError('third arg must be a format string') else: @@ -517,6 +520,8 @@ def _plot_args(self, tup, kwargs, return_kwargs=False): ncx, ncy = x.shape[1], y.shape[1] if ncx > 1 and ncy > 1 and ncx != ncy: raise ValueError(f"x has {ncx} columns but y has {ncy} columns") + if ncx == 0 or ncy == 0: + return [] label = kwargs.get('label') n_datasets = max(ncx, ncy) @@ -539,20 +544,35 @@ def _plot_args(self, tup, kwargs, return_kwargs=False): return [l[0] for l in result] -@cbook._define_aliases({"facecolor": ["fc"]}) +@_api.define_aliases({"facecolor": ["fc"]}) class _AxesBase(martist.Artist): name = "rectilinear" - _shared_x_axes = cbook.Grouper() - _shared_y_axes = cbook.Grouper() + # axis names are the prefixes for the attributes that contain the + # respective axis; e.g. 'x' <-> self.xaxis, containing an XAxis. + # Note that PolarAxes uses these attributes as well, so that we have + # 'x' <-> self.xaxis, containing a ThetaAxis. In particular we do not + # have 'theta' in _axis_names. + # In practice, this is ('x', 'y') for all 2D Axes and ('x', 'y', 'z') + # for Axes3D. + _axis_names = ("x", "y") + _shared_axes = {name: cbook.Grouper() for name in _axis_names} _twinned_axes = cbook.Grouper() + _subclass_uses_cla = False + + @property + def _axis_map(self): + """A mapping of axis names, e.g. 'x', to `Axis` instances.""" + return {name: getattr(self, f"{name}axis") + for name in self._axis_names} + def __str__(self): return "{0}({1[0]:g},{1[1]:g};{1[2]:g}x{1[3]:g})".format( type(self).__name__, self._position.bounds) - @_api.make_keyword_only("3.4", "facecolor") def __init__(self, fig, rect, + *, facecolor=None, # defaults to rc axes.facecolor frameon=True, sharex=None, # use Axes instance's xaxis info @@ -564,15 +584,15 @@ def __init__(self, fig, rect, **kwargs ): """ - Build an axes in a figure. + Build an Axes in a figure. Parameters ---------- fig : `~matplotlib.figure.Figure` - The axes is build in the `.Figure` *fig*. + The Axes is built in the `.Figure` *fig*. - rect : [left, bottom, width, height] - The axes is build in the rectangle *rect*. *rect* is in + rect : tuple (left, bottom, width, height). + The Axes is built in the rectangle *rect*. *rect* is in `.Figure` coordinates. sharex, sharey : `~.axes.Axes`, optional @@ -580,16 +600,16 @@ def __init__(self, fig, rect, y axis in the input `~.axes.Axes`. frameon : bool, default: True - Whether the axes frame is visible. + Whether the Axes frame is visible. box_aspect : float, optional - Set a fixed aspect for the axes box, i.e. the ratio of height to + Set a fixed aspect for the Axes box, i.e. the ratio of height to width. See `~.axes.Axes.set_box_aspect` for details. **kwargs Other optional keyword arguments: - %(Axes_kwdoc)s + %(Axes:kwdoc)s Returns ------- @@ -609,15 +629,14 @@ def __init__(self, fig, rect, self._aspect = 'auto' self._adjustable = 'box' self._anchor = 'C' - self._stale_viewlim_x = False - self._stale_viewlim_y = False + self._stale_viewlims = {name: False for name in self._axis_names} self._sharex = sharex self._sharey = sharey self.set_label(label) self.set_figure(fig) self.set_box_aspect(box_aspect) self._axes_locator = None # Optionally set via update(kwargs). - # placeholder for any colorbars added that use this axes. + # placeholder for any colorbars added that use this Axes. # (see colorbar.py): self._colorbars = [] self.spines = mspines.Spines.from_dict(self._gen_axes_spines()) @@ -631,7 +650,7 @@ def __init__(self, fig, rect, self.set_axisbelow(mpl.rcParams['axes.axisbelow']) self._rasterization_zorder = None - self.cla() + self.clear() # funcs used to format x and y - fall back on major formatters self.fmt_xdata = None @@ -645,12 +664,11 @@ def __init__(self, fig, rect, if yscale: self.set_yscale(yscale) - self.update(kwargs) + self._internal_update(kwargs) - for name, axis in self._get_axis_map().items(): - axis.callbacks._pickled_cids.add( - axis.callbacks.connect( - 'units finalize', self._unit_change_handler(name))) + for name, axis in self._axis_map.items(): + axis.callbacks._connect_picklable( + 'units', self._unit_change_handler(name)) rcParams = mpl.rcParams self.tick_params( @@ -683,25 +701,38 @@ def __init__(self, fig, rect, rcParams['ytick.major.right']), which='major') + def __init_subclass__(cls, **kwargs): + parent_uses_cla = super(cls, cls)._subclass_uses_cla + if 'cla' in cls.__dict__: + _api.warn_deprecated( + '3.6', + pending=True, + message=f'Overriding `Axes.cla` in {cls.__qualname__} is ' + 'pending deprecation in %(since)s and will be fully ' + 'deprecated in favor of `Axes.clear` in the future. ' + 'Please report ' + f'this to the {cls.__module__!r} author.') + cls._subclass_uses_cla = 'cla' in cls.__dict__ or parent_uses_cla + super().__init_subclass__(**kwargs) + def __getstate__(self): - # The renderer should be re-created by the figure, and then cached at - # that point. state = super().__getstate__() # Prune the sharing & twinning info to only contain the current group. - for grouper_name in [ - '_shared_x_axes', '_shared_y_axes', '_twinned_axes']: - grouper = getattr(self, grouper_name) - state[grouper_name] = (grouper.get_siblings(self) - if self in grouper else None) + state["_shared_axes"] = { + name: self._shared_axes[name].get_siblings(self) + for name in self._axis_names if self in self._shared_axes[name]} + state["_twinned_axes"] = (self._twinned_axes.get_siblings(self) + if self in self._twinned_axes else None) return state def __setstate__(self, state): # Merge the grouping info back into the global groupers. - for grouper_name in [ - '_shared_x_axes', '_shared_y_axes', '_twinned_axes']: - siblings = state.pop(grouper_name) - if siblings: - getattr(self, grouper_name).join(*siblings) + shared_axes = state.pop("_shared_axes") + for name, shared_siblings in shared_axes.items(): + self._shared_axes[name].join(*shared_siblings) + twinned_siblings = state.pop("_twinned_axes") + if twinned_siblings: + self._twinned_axes.join(*twinned_siblings) self.__dict__ = state self._stale = True @@ -709,25 +740,27 @@ def __repr__(self): fields = [] if self.get_label(): fields += [f"label={self.get_label()!r}"] - titles = [] - for k in ["left", "center", "right"]: - title = self.get_title(loc=k) - if title: - titles.append(f"{k!r}:{title!r}") - if titles: - fields += ["title={" + ",".join(titles) + "}"] - if self.get_xlabel(): - fields += [f"xlabel={self.get_xlabel()!r}"] - if self.get_ylabel(): - fields += [f"ylabel={self.get_ylabel()!r}"] - return f"<{self.__class__.__name__}:" + ", ".join(fields) + ">" - - def get_window_extent(self, *args, **kwargs): - """ - Return the axes bounding box in display space; *args* and *kwargs* + if hasattr(self, "get_title"): + titles = {} + for k in ["left", "center", "right"]: + title = self.get_title(loc=k) + if title: + titles[k] = title + if titles: + fields += [f"title={titles}"] + for name, axis in self._axis_map.items(): + if axis.get_label() and axis.get_label().get_text(): + fields += [f"{name}label={axis.get_label().get_text()!r}"] + return f"<{self.__class__.__name__}: " + ", ".join(fields) + ">" + + @_api.delete_parameter("3.6", "args") + @_api.delete_parameter("3.6", "kwargs") + def get_window_extent(self, renderer=None, *args, **kwargs): + """ + Return the Axes bounding box in display space; *args* and *kwargs* are empty. - This bounding box does not include the spines, ticks, ticklables, + This bounding box does not include the spines, ticks, ticklabels, or other labels. For a bounding box including these elements use `~matplotlib.axes.Axes.get_tightbbox`. @@ -766,31 +799,42 @@ def set_figure(self, fig): def _unstale_viewLim(self): # We should arrange to store this information once per share-group # instead of on every axis. - scalex = any(ax._stale_viewlim_x - for ax in self._shared_x_axes.get_siblings(self)) - scaley = any(ax._stale_viewlim_y - for ax in self._shared_y_axes.get_siblings(self)) - if scalex or scaley: - for ax in self._shared_x_axes.get_siblings(self): - ax._stale_viewlim_x = False - for ax in self._shared_y_axes.get_siblings(self): - ax._stale_viewlim_y = False - self.autoscale_view(scalex=scalex, scaley=scaley) + need_scale = { + name: any(ax._stale_viewlims[name] + for ax in self._shared_axes[name].get_siblings(self)) + for name in self._axis_names} + if any(need_scale.values()): + for name in need_scale: + for ax in self._shared_axes[name].get_siblings(self): + ax._stale_viewlims[name] = False + self.autoscale_view(**{f"scale{name}": scale + for name, scale in need_scale.items()}) @property def viewLim(self): self._unstale_viewLim() return self._viewLim - # API could be better, right now this is just to match the old calls to - # autoscale_view() after each plotting method. - def _request_autoscale_view(self, tight=None, scalex=True, scaley=True): + def _request_autoscale_view(self, axis="all", tight=None): + """ + Mark a single axis, or all of them, as stale wrt. autoscaling. + + No computation is performed until the next autoscaling; thus, separate + calls to control individual axises incur negligible performance cost. + + Parameters + ---------- + axis : str, default: "all" + Either an element of ``self._axis_names``, or "all". + tight : bool or None, default: None + """ + axis_names = _api.check_getitem( + {**{k: [k] for k in self._axis_names}, "all": self._axis_names}, + axis=axis) + for name in axis_names: + self._stale_viewlims[name] = True if tight is not None: self._tight = tight - if scalex: - self._stale_viewlim_x = True # Else keep old state. - if scaley: - self._stale_viewlim_y = True def _set_lim_and_transforms(self): """ @@ -801,7 +845,7 @@ def _set_lim_and_transforms(self): This method is primarily used by rectilinear projections of the `~matplotlib.axes.Axes` class, and is meant to be overridden by - new kinds of projection axes that need different transformations + new kinds of projection Axes that need different transformations and limits. (See `~matplotlib.projections.polar.PolarAxes` for an example.) """ @@ -850,7 +894,7 @@ def get_xaxis_transform(self, which='grid'): # for cartesian projection, this is top spine return self.spines.top.get_spine_transform() else: - raise ValueError('unknown value for which') + raise ValueError(f'unknown value for which: {which!r}') def get_xaxis_text1_transform(self, pad_points): """ @@ -858,7 +902,7 @@ def get_xaxis_text1_transform(self, pad_points): ------- transform : Transform The transform used for drawing x-axis labels, which will add - *pad_points* of padding (in points) between the axes and the label. + *pad_points* of padding (in points) between the axis and the label. The x-direction is in data coordinates and the y-direction is in axis coordinates valign : {'center', 'top', 'bottom', 'baseline', 'center_baseline'} @@ -884,7 +928,7 @@ def get_xaxis_text2_transform(self, pad_points): ------- transform : Transform The transform used for drawing secondary x-axis labels, which will - add *pad_points* of padding (in points) between the axes and the + add *pad_points* of padding (in points) between the axis and the label. The x-direction is in data coordinates and the y-direction is in axis coordinates valign : {'center', 'top', 'bottom', 'baseline', 'center_baseline'} @@ -926,7 +970,7 @@ def get_yaxis_transform(self, which='grid'): # for cartesian projection, this is top spine return self.spines.right.get_spine_transform() else: - raise ValueError('unknown value for which') + raise ValueError(f'unknown value for which: {which!r}') def get_yaxis_text1_transform(self, pad_points): """ @@ -934,7 +978,7 @@ def get_yaxis_text1_transform(self, pad_points): ------- transform : Transform The transform used for drawing y-axis labels, which will add - *pad_points* of padding (in points) between the axes and the label. + *pad_points* of padding (in points) between the axis and the label. The x-direction is in axis coordinates and the y-direction is in data coordinates valign : {'center', 'top', 'bottom', 'baseline', 'center_baseline'} @@ -960,7 +1004,7 @@ def get_yaxis_text2_transform(self, pad_points): ------- transform : Transform The transform used for drawing secondart y-axis labels, which will - add *pad_points* of padding (in points) between the axes and the + add *pad_points* of padding (in points) between the axis and the label. The x-direction is in axis coordinates and the y-direction is in data coordinates valign : {'center', 'top', 'bottom', 'baseline', 'center_baseline'} @@ -984,7 +1028,9 @@ def _update_transScale(self): self.transScale.set( mtransforms.blended_transform_factory( self.xaxis.get_transform(), self.yaxis.get_transform())) - for line in getattr(self, "lines", []): # Not set during init. + for line in getattr(self, "_children", []): # Not set during init. + if not isinstance(line, mlines.Line2D): + continue try: line._transformed_path.invalidate() except AttributeError: @@ -992,7 +1038,7 @@ def _update_transScale(self): def get_position(self, original=False): """ - Get a copy of the axes rectangle as a `.Bbox`. + Return the position of the Axes within the figure as a `.Bbox`. Parameters ---------- @@ -1016,7 +1062,7 @@ def get_position(self, original=False): def set_position(self, pos, which='both'): """ - Set the axes position. + Set the Axes position. Axes have two position attributes. The 'original' position is the position allocated for the Axes. The 'active' position is the @@ -1027,7 +1073,7 @@ def set_position(self, pos, which='both'): Parameters ---------- pos : [left, bottom, width, height] or `~matplotlib.transforms.Bbox` - The new position of the in `.Figure` coordinates. + The new position of the Axes in `.Figure` coordinates. which : {'both', 'active', 'original'}, default: 'both' Determines which position variables to change. @@ -1046,7 +1092,7 @@ def _set_position(self, pos, which='both'): """ Private version of set_position. - Call this internally to get the same functionality of `get_position`, + Call this internally to get the same functionality of `set_position`, but not to take the axis out of the constrained_layout hierarchy. """ if not isinstance(pos, mtransforms.BboxBase): @@ -1062,8 +1108,9 @@ def reset_position(self): """ Reset the active position to the original position. - This resets the a possible position change due to aspect constraints. - For an explanation of the positions see `.set_position`. + This undoes changes to the active position (as defined in + `.set_position`) which may have been performed to satisfy fixed-aspect + constraints. """ for ax in self._twinned_axes.get_siblings(self): pos = ax.get_position(original=True) @@ -1071,7 +1118,7 @@ def reset_position(self): def set_axes_locator(self, locator): """ - Set the axes locator. + Set the Axes locator. Parameters ---------- @@ -1087,13 +1134,13 @@ def get_axes_locator(self): return self._axes_locator def _set_artist_props(self, a): - """Set the boilerplate props for artists added to axes.""" + """Set the boilerplate props for artists added to Axes.""" a.set_figure(self.figure) if not a.is_transform_set(): a.set_transform(self.transData) a.axes = self - if a.mouseover: + if a.get_mouseover(): self._mouseover_set.add(a) def _gen_axes_patch(self): @@ -1101,10 +1148,10 @@ def _gen_axes_patch(self): Returns ------- Patch - The patch used to draw the background of the axes. It is also used - as the clipping path for any data elements on the axes. + The patch used to draw the background of the Axes. It is also used + as the clipping path for any data elements on the Axes. - In the standard axes, this is a rectangle, but in other projections + In the standard Axes, this is a rectangle, but in other projections it may not be. Notes @@ -1119,30 +1166,30 @@ def _gen_axes_spines(self, locations=None, offset=0.0, units='inches'): ------- dict Mapping of spine names to `.Line2D` or `.Patch` instances that are - used to draw axes spines. + used to draw Axes spines. - In the standard axes, spines are single line segments, but in other + In the standard Axes, spines are single line segments, but in other projections they may not be. Notes ----- Intended to be overridden by new projection types. """ - return OrderedDict((side, mspines.Spine.linear_spine(self, side)) - for side in ['left', 'right', 'bottom', 'top']) + return {side: mspines.Spine.linear_spine(self, side) + for side in ['left', 'right', 'bottom', 'top']} def sharex(self, other): """ Share the x-axis with *other*. This is equivalent to passing ``sharex=other`` when constructing the - axes, and cannot be used if the x-axis is already being shared with - another axes. + Axes, and cannot be used if the x-axis is already being shared with + another Axes. """ _api.check_isinstance(_AxesBase, other=other) if self._sharex is not None and other is not self._sharex: raise ValueError("x-axis is already shared") - self._shared_x_axes.join(self, other) + self._shared_axes["x"].join(self, other) self._sharex = other self.xaxis.major = other.xaxis.major # Ticker instances holding self.xaxis.minor = other.xaxis.minor # locator and formatter. @@ -1155,13 +1202,13 @@ def sharey(self, other): Share the y-axis with *other*. This is equivalent to passing ``sharey=other`` when constructing the - axes, and cannot be used if the y-axis is already being shared with - another axes. + Axes, and cannot be used if the y-axis is already being shared with + another Axes. """ _api.check_isinstance(_AxesBase, other=other) if self._sharey is not None and other is not self._sharey: raise ValueError("y-axis is already shared") - self._shared_y_axes.join(self, other) + self._shared_axes["y"].join(self, other) self._sharey = other self.yaxis.major = other.yaxis.major # Ticker instances holding self.yaxis.minor = other.yaxis.minor # locator and formatter. @@ -1169,9 +1216,12 @@ def sharey(self, other): self.set_ylim(y0, y1, emit=False, auto=other.get_autoscaley_on()) self.yaxis._scale = other.yaxis._scale - def cla(self): - """Clear the axes.""" - # Note: this is called by Axes.__init__() + def __clear(self): + """Clear the Axes.""" + # The actual implementation of clear() as long as clear() has to be + # an adapter delegating to the correct implementation. + # The implementation can move back into clear() when the + # deprecation on cla() subclassing expires. # stash the current visibility state if hasattr(self, 'patch'): @@ -1182,31 +1232,22 @@ def cla(self): xaxis_visible = self.xaxis.get_visible() yaxis_visible = self.yaxis.get_visible() - self.xaxis.clear() - self.yaxis.clear() - - for name, spine in self.spines.items(): + for axis in self._axis_map.values(): + axis.clear() # Also resets the scale to linear. + for spine in self.spines.values(): spine.clear() self.ignore_existing_data_limits = True - self.callbacks = cbook.CallbackRegistry() + self.callbacks = cbook.CallbackRegistry( + signals=["xlim_changed", "ylim_changed", "zlim_changed"]) - if self._sharex is not None: - self.sharex(self._sharex) - else: - self.xaxis._set_scale('linear') - try: - self.set_xlim(0, 1) - except TypeError: - pass - if self._sharey is not None: - self.sharey(self._sharey) - else: - self.yaxis._set_scale('linear') - try: - self.set_ylim(0, 1) - except TypeError: - pass + for name, axis in self._axis_map.items(): + share = getattr(self, f"_share{name}") + if share is not None: + getattr(self, f"share{name}")(share) + else: + axis._set_scale("linear") + axis._set_lim(0, 1, auto=True) # update the minor locator for x and y axis based on rcParams if mpl.rcParams['xtick.minor.visible']: @@ -1214,10 +1255,6 @@ def cla(self): if mpl.rcParams['ytick.minor.visible']: self.yaxis.set_minor_locator(mticker.AutoMinorLocator()) - if self._sharex is None: - self._autoscaleXon = True - if self._sharey is None: - self._autoscaleYon = True self._xmargin = mpl.rcParams['axes.xmargin'] self._ymargin = mpl.rcParams['axes.ymargin'] self._tight = None @@ -1228,18 +1265,12 @@ def cla(self): self._get_patches_for_fill = _process_plot_var_args(self, 'fill') self._gridOn = mpl.rcParams['axes.grid'] - self.lines = [] - self.patches = [] - self.texts = [] - self.tables = [] - self.artists = [] - self.images = [] + self._children = [] self._mouseover_set = _OrderedSet() self.child_axes = [] self._current_image = None # strictly for pyplot via _sci, _gci self._projection_init = None # strictly for pyplot.subplot self.legend_ = None - self.collections = [] # collection.Collection instances self.containers = [] self.grid(False) # Disable grid on init to use rcParameter @@ -1281,13 +1312,13 @@ def cla(self): for _title in (self.title, self._left_title, self._right_title): self._set_artist_props(_title) - # The patch draws the background of the axes. We want this to be below + # The patch draws the background of the Axes. We want this to be below # the other artists. We use the frame to draw the edges so we are # setting the edgecolor to None. self.patch = self._gen_axes_patch() self.patch.set_figure(self.figure) self.patch.set_facecolor(self._facecolor) - self.patch.set_edgecolor('None') + self.patch.set_edgecolor('none') self.patch.set_linewidth(0) self.patch.set_transform(self.transAxes) @@ -1296,8 +1327,8 @@ def cla(self): self.xaxis.set_clip_path(self.patch) self.yaxis.set_clip_path(self.patch) - self._shared_x_axes.clean() - self._shared_y_axes.clean() + self._shared_axes["x"].clean() + self._shared_axes["y"].clean() if self._sharex is not None: self.xaxis.set_visible(xaxis_visible) self.patch.set_visible(patch_visible) @@ -1308,8 +1339,185 @@ def cla(self): self.stale = True def clear(self): - """Clear the axes.""" - self.cla() + """Clear the Axes.""" + # Act as an alias, or as the superclass implementation depending on the + # subclass implementation. + if self._subclass_uses_cla: + self.cla() + else: + self.__clear() + + def cla(self): + """Clear the Axes.""" + # Act as an alias, or as the superclass implementation depending on the + # subclass implementation. + if self._subclass_uses_cla: + self.__clear() + else: + self.clear() + + class ArtistList(MutableSequence): + """ + A sublist of Axes children based on their type. + + The type-specific children sublists will become immutable in + Matplotlib 3.7. Then, these artist lists will likely be replaced by + tuples. Use as if this is a tuple already. + + This class exists only for the transition period to warn on the + deprecated modification of artist lists. + """ + def __init__(self, axes, prop_name, add_name, + valid_types=None, invalid_types=None): + """ + Parameters + ---------- + axes : .axes.Axes + The Axes from which this sublist will pull the children + Artists. + prop_name : str + The property name used to access this sublist from the Axes; + used to generate deprecation warnings. + add_name : str + The method name used to add Artists of this sublist's type to + the Axes; used to generate deprecation warnings. + valid_types : list of type, optional + A list of types that determine which children will be returned + by this sublist. If specified, then the Artists in the sublist + must be instances of any of these types. If unspecified, then + any type of Artist is valid (unless limited by + *invalid_types*.) + invalid_types : tuple, optional + A list of types that determine which children will *not* be + returned by this sublist. If specified, then Artists in the + sublist will never be an instance of these types. Otherwise, no + types will be excluded. + """ + self._axes = axes + self._prop_name = prop_name + self._add_name = add_name + self._type_check = lambda artist: ( + (not valid_types or isinstance(artist, valid_types)) and + (not invalid_types or not isinstance(artist, invalid_types)) + ) + + def __repr__(self): + return f'' + + def __len__(self): + return sum(self._type_check(artist) + for artist in self._axes._children) + + def __iter__(self): + for artist in list(self._axes._children): + if self._type_check(artist): + yield artist + + def __getitem__(self, key): + return [artist + for artist in self._axes._children + if self._type_check(artist)][key] + + def __add__(self, other): + if isinstance(other, (list, _AxesBase.ArtistList)): + return [*self, *other] + return NotImplemented + + def __radd__(self, other): + if isinstance(other, list): + return other + list(self) + return NotImplemented + + def insert(self, index, item): + _api.warn_deprecated( + '3.5', + name=f'modification of the Axes.{self._prop_name}', + obj_type='property', + alternative=f'Axes.{self._add_name}') + try: + index = self._axes._children.index(self[index]) + except IndexError: + index = None + getattr(self._axes, self._add_name)(item) + if index is not None: + # Move new item to the specified index, if there's something to + # put it before. + self._axes._children[index:index] = self._axes._children[-1:] + del self._axes._children[-1] + + def __setitem__(self, key, item): + _api.warn_deprecated( + '3.5', + name=f'modification of the Axes.{self._prop_name}', + obj_type='property', + alternative=f'Artist.remove() and Axes.f{self._add_name}') + del self[key] + if isinstance(key, slice): + key = key.start + if not np.iterable(item): + self.insert(key, item) + return + + try: + index = self._axes._children.index(self[key]) + except IndexError: + index = None + for i, artist in enumerate(item): + getattr(self._axes, self._add_name)(artist) + if index is not None: + # Move new items to the specified index, if there's something + # to put it before. + i = -(i + 1) + self._axes._children[index:index] = self._axes._children[i:] + del self._axes._children[i:] + + def __delitem__(self, key): + _api.warn_deprecated( + '3.5', + name=f'modification of the Axes.{self._prop_name}', + obj_type='property', + alternative='Artist.remove()') + if isinstance(key, slice): + for artist in self[key]: + artist.remove() + else: + self[key].remove() + + @property + def artists(self): + return self.ArtistList(self, 'artists', 'add_artist', invalid_types=( + mcoll.Collection, mimage.AxesImage, mlines.Line2D, mpatches.Patch, + mtable.Table, mtext.Text)) + + @property + def collections(self): + return self.ArtistList(self, 'collections', 'add_collection', + valid_types=mcoll.Collection) + + @property + def images(self): + return self.ArtistList(self, 'images', 'add_image', + valid_types=mimage.AxesImage) + + @property + def lines(self): + return self.ArtistList(self, 'lines', 'add_line', + valid_types=mlines.Line2D) + + @property + def patches(self): + return self.ArtistList(self, 'patches', 'add_patch', + valid_types=mpatches.Patch) + + @property + def tables(self): + return self.ArtistList(self, 'tables', 'add_table', + valid_types=mtable.Table) + + @property + def texts(self): + return self.ArtistList(self, 'texts', 'add_artist', + valid_types=mtext.Text) def get_facecolor(self): """Get the facecolor of the Axes.""" @@ -1357,13 +1565,13 @@ def set_prop_cycle(self, *args, **kwargs): Form 2 creates a `~cycler.Cycler` which cycles over one or more properties simultaneously and set it as the property cycle of the - axes. If multiple properties are given, their value lists must have + Axes. If multiple properties are given, their value lists must have the same length. This is just a shortcut for explicitly creating a cycler and passing it to the function, i.e. it's short for ``set_prop_cycle(cycler(label=values label2=values2, ...))``. Form 3 creates a `~cycler.Cycler` for a single property and set it - as the property cycle of the axes. This form exists for compatibility + as the property cycle of the Axes. This form exists for compatibility with the original `cycler.cycler` interface. Its use is discouraged in favor of the kwarg form, i.e. ``set_prop_cycle(label=values)``. @@ -1414,32 +1622,35 @@ def set_prop_cycle(self, *args, **kwargs): self._get_patches_for_fill.set_prop_cycle(prop_cycle) def get_aspect(self): + """ + Return the aspect ratio of the axes scaling. + + This is either "auto" or a float giving the ratio of y/x-scale. + """ return self._aspect def set_aspect(self, aspect, adjustable=None, anchor=None, share=False): """ - Set the aspect of the axis scaling, i.e. the ratio of y-unit to x-unit. + Set the aspect ratio of the axes scaling, i.e. y/x-scale. Parameters ---------- - aspect : {'auto'} or num + aspect : {'auto', 'equal'} or float Possible values: - ======== ================================================= - value description - ======== ================================================= - 'auto' automatic; fill the position rectangle with data. - num a circle will be stretched such that the height - is *num* times the width. 'equal' is a synonym - for ``aspect=1``, i.e. same scaling for x and y. - ======== ================================================= + - 'auto': fill the position rectangle with data. + - 'equal': same as ``aspect=1``, i.e. same scaling for x and y. + - *float*: The displayed size of 1 unit in y-data coordinates will + be *aspect* times the displayed size of 1 unit in x-data + coordinates; e.g. for ``aspect=2`` a square in data coordinates + will be rendered with a height of twice its width. adjustable : None or {'box', 'datalim'}, optional If not ``None``, this defines which parameter will be adjusted to meet the required aspect. See `.set_adjustable` for further details. - anchor : None or str or 2-tuple of float, optional + anchor : None or str or (float, float), optional If not ``None``, this defines where the Axes will be drawn if there is extra space due to aspect constraints. The most common way to to specify the anchor are abbreviations of cardinal directions: @@ -1472,8 +1683,8 @@ def set_aspect(self, aspect, adjustable=None, anchor=None, share=False): aspect = float(aspect) # raise ValueError if necessary if share: - axes = {*self._shared_x_axes.get_siblings(self), - *self._shared_y_axes.get_siblings(self)} + axes = {sibling for name in self._axis_names + for sibling in self._shared_axes[name].get_siblings(self)} else: axes = [self] @@ -1534,8 +1745,8 @@ def set_adjustable(self, adjustable, share=False): """ _api.check_in_list(["box", "datalim"], adjustable=adjustable) if share: - axs = {*self._shared_x_axes.get_siblings(self), - *self._shared_y_axes.get_siblings(self)} + axs = {sibling for name in self._axis_names + for sibling in self._shared_axes[name].get_siblings(self)} else: axs = [self] if (adjustable == "datalim" @@ -1544,7 +1755,7 @@ def set_adjustable(self, adjustable, share=False): for ax in axs)): # Limits adjustment by apply_aspect assumes that the axes' aspect # ratio can be computed from the data limits and scales. - raise ValueError("Cannot set axes adjustable to 'datalim' for " + raise ValueError("Cannot set Axes adjustable to 'datalim' for " "Axes which override 'get_data_ratio'") for ax in axs: ax._adjustable = adjustable @@ -1552,7 +1763,7 @@ def set_adjustable(self, adjustable, share=False): def get_box_aspect(self): """ - Return the axes box aspect, i.e. the ratio of height to width. + Return the Axes box aspect, i.e. the ratio of height to width. The box aspect is ``None`` (i.e. chosen depending on the available figure space) unless explicitly specified. @@ -1568,21 +1779,21 @@ def get_box_aspect(self): def set_box_aspect(self, aspect=None): """ - Set the axes box aspect, i.e. the ratio of height to width. + Set the Axes box aspect, i.e. the ratio of height to width. - This defines the aspect of the axes in figure space and is not to be + This defines the aspect of the Axes in figure space and is not to be confused with the data aspect (see `~.Axes.set_aspect`). Parameters ---------- aspect : float or None Changes the physical dimensions of the Axes, such that the ratio - of the axes height to the axes width in physical units is equal to + of the Axes height to the Axes width in physical units is equal to *aspect*. Defining a box aspect will change the *adjustable* property to 'datalim' (see `~.Axes.set_adjustable`). *None* will disable a fixed box aspect so that height and width - of the axes are chosen independently. + of the Axes are chosen independently. See Also -------- @@ -1627,28 +1838,21 @@ def set_anchor(self, anchor, share=False): Parameters ---------- - anchor : 2-tuple of floats or {'C', 'SW', 'S', 'SE', ...} - The anchor position may be either: - - - a sequence (*cx*, *cy*). *cx* and *cy* may range from 0 - to 1, where 0 is left or bottom and 1 is right or top. - - - a string using cardinal directions as abbreviation: - - - 'C' for centered - - 'S' (south) for bottom-center - - 'SW' (south west) for bottom-left - - etc. - - Here is an overview of the possible positions: - - +------+------+------+ - | 'NW' | 'N' | 'NE' | - +------+------+------+ - | 'W' | 'C' | 'E' | - +------+------+------+ - | 'SW' | 'S' | 'SE' | - +------+------+------+ + anchor : (float, float) or {'C', 'SW', 'S', 'SE', 'E', 'NE', ...} + Either an (*x*, *y*) pair of relative coordinates (0 is left or + bottom, 1 is right or top), 'C' (center), or a cardinal direction + ('SW', southwest, is bottom left, etc.). str inputs are shorthands + for (*x*, *y*) coordinates, as shown in the following table:: + + .. code-block:: none + + +-----------------+-----------------+-----------------+ + | 'NW' (0.0, 1.0) | 'N' (0.5, 1.0) | 'NE' (1.0, 1.0) | + +-----------------+-----------------+-----------------+ + | 'W' (0.0, 0.5) | 'C' (0.5, 0.5) | 'E' (1.0, 0.5) | + +-----------------+-----------------+-----------------+ + | 'SW' (0.0, 0.0) | 'S' (0.5, 0.0) | 'SE' (1.0, 0.0) | + +-----------------+-----------------+-----------------+ share : bool, default: False If ``True``, apply the settings to all shared Axes. @@ -1662,8 +1866,8 @@ def set_anchor(self, anchor, share=False): raise ValueError('argument must be among %s' % ', '.join(mtransforms.Bbox.coefs)) if share: - axes = {*self._shared_x_axes.get_siblings(self), - *self._shared_y_axes.get_siblings(self)} + axes = {sibling for name in self._axis_names + for sibling in self._shared_axes[name].get_siblings(self)} else: axes = [self] for ax in axes: @@ -1693,6 +1897,13 @@ def apply_aspect(self, position=None): Axes box (position) or the view limits. In the former case, `~matplotlib.axes.Axes.get_anchor` will affect the position. + Parameters + ---------- + position : None or .Bbox + If not ``None``, this defines the position of the + Axes within the figure as a Bbox. See `~.Axes.get_position` + for further details. + Notes ----- This is called automatically when each Axes is drawn. You may need @@ -1718,7 +1929,7 @@ def apply_aspect(self, position=None): return trans = self.get_figure().transSubfigure - bb = mtransforms.Bbox.from_bounds(0, 0, 1, 1).transformed(trans) + bb = mtransforms.Bbox.unit().transformed(trans) # this is the physical aspect of the panel (or figure): fig_aspect = bb.height / bb.width @@ -1778,12 +1989,14 @@ def apply_aspect(self, position=None): xm = 0 ym = 0 - shared_x = self in self._shared_x_axes - shared_y = self in self._shared_y_axes - # Not sure whether we need this check: + shared_x = self in self._shared_axes["x"] + shared_y = self in self._shared_axes["y"] + if shared_x and shared_y: - raise RuntimeError("adjustable='datalim' is not allowed when both " - "axes are shared") + raise RuntimeError("set_aspect(..., adjustable='datalim') or " + "axis('equal') are not allowed when both axes " + "are shared. Try set_aspect(..., " + "adjustable='box').") # If y is shared, then we are only allowed to change x, etc. if shared_y: @@ -1883,7 +2096,6 @@ def axis(self, *args, emit=True, **kwargs): self.set_autoscale_on(True) self.set_aspect('auto') self.autoscale_view(tight=False) - # self.apply_aspect() if s == 'equal': self.set_aspect('equal', adjustable='datalim') elif s == 'scaled': @@ -1907,8 +2119,8 @@ def axis(self, *args, emit=True, **kwargs): self.set_ylim([ylim[0], ylim[0] + edge_size], emit=emit, auto=False) else: - raise ValueError('Unrecognized string %s to axis; ' - 'try on or off' % s) + raise ValueError(f"Unrecognized string {s!r} to axis; " + "try 'on' or 'off'") else: if len(args) == 1: limits = args[0] @@ -1950,19 +2162,23 @@ def get_lines(self): def get_xaxis(self): """ - Return the XAxis instance. + [*Discouraged*] Return the XAxis instance. - The use of this function is discouraged. You should instead directly - access the attribute ``ax.xaxis``. + .. admonition:: Discouraged + + The use of this function is discouraged. You should instead + directly access the attribute ``ax.xaxis``. """ return self.xaxis def get_yaxis(self): """ - Return the YAxis instance. + [*Discouraged*] Return the YAxis instance. + + .. admonition:: Discouraged - The use of this function is discouraged. You should instead directly - access the attribute ``ax.yaxis``. + The use of this function is discouraged. You should instead + directly access the attribute ``ax.yaxis``. """ return self.yaxis @@ -1978,13 +2194,16 @@ def _sci(self, im): Set the current image. This image will be the target of colormap functions like - `~.pyplot.viridis`, and other functions such as `~.pyplot.clim`. The - current image is an attribute of the current axes. + ``pyplot.viridis``, and other functions such as `~.pyplot.clim`. The + current image is an attribute of the current Axes. """ + _api.check_isinstance( + (mpl.contour.ContourSet, mcoll.Collection, mimage.AxesImage), + im=im) if isinstance(im, mpl.contour.ContourSet): - if im.collections[0] not in self.collections: + if im.collections[0] not in self._children: raise ValueError("ContourSet must be in current Axes") - elif im not in self.images and im not in self.collections: + elif im not in self._children: raise ValueError("Argument must be an image, collection, or " "ContourSet in this Axes") self._current_image = im @@ -1995,21 +2214,33 @@ def _gci(self): def has_data(self): """ - Return whether any artists have been added to the axes. + Return whether any artists have been added to the Axes. This should not be used to determine whether the *dataLim* need to be updated, and may not actually be useful for anything. """ - return ( - len(self.collections) + - len(self.images) + - len(self.lines) + - len(self.patches)) > 0 + return any(isinstance(a, (mcoll.Collection, mimage.AxesImage, + mlines.Line2D, mpatches.Patch)) + for a in self._children) + + def _deprecate_noninstance(self, _name, _types, **kwargs): + """ + For each *key, value* pair in *kwargs*, check that *value* is an + instance of one of *_types*; if not, raise an appropriate deprecation. + """ + for key, value in kwargs.items(): + if not isinstance(value, _types): + _api.warn_deprecated( + '3.5', name=_name, + message=f'Passing argument *{key}* of unexpected type ' + f'{type(value).__qualname__} to %(name)s which only ' + f'accepts {_types} is deprecated since %(since)s and will ' + 'become an error %(removal)s.') def add_artist(self, a): """ - Add an `~.Artist` to the axes, and return the artist. + Add an `.Artist` to the Axes; return the artist. Use `add_artist` only for artists for which there is no dedicated "add" method; and if necessary, use a method such as `update_datalim` @@ -2021,8 +2252,8 @@ def add_artist(self, a): ``ax.transData``. """ a.axes = self - self.artists.append(a) - a._remove_method = self.artists.remove + self._children.append(a) + a._remove_method = self._children.remove self._set_artist_props(a) a.set_clip_path(self.patch) self.stale = True @@ -2030,12 +2261,12 @@ def add_artist(self, a): def add_child_axes(self, ax): """ - Add an `~.AxesBase` to the axes' children; return the child axes. + Add an `.AxesBase` to the Axes' children; return the child Axes. This is the lowlevel version. See `.axes.Axes.inset_axes`. """ - # normally axes have themselves as the axes, but these need to have + # normally Axes have themselves as the Axes, but these need to have # their parent... # Need to bypass the getter... ax._axes = self @@ -2048,13 +2279,15 @@ def add_child_axes(self, ax): def add_collection(self, collection, autolim=True): """ - Add a `~.Collection` to the axes' collections; return the collection. + Add a `.Collection` to the Axes; return the collection. """ + self._deprecate_noninstance('add_collection', mcoll.Collection, + collection=collection) label = collection.get_label() if not label: - collection.set_label('_collection%d' % len(self.collections)) - self.collections.append(collection) - collection._remove_method = self.collections.remove + collection.set_label(f'_child{len(self._children)}') + self._children.append(collection) + collection._remove_method = self._children.remove self._set_artist_props(collection) if collection.get_clip_path() is None: @@ -2080,13 +2313,14 @@ def add_collection(self, collection, autolim=True): def add_image(self, image): """ - Add an `~.AxesImage` to the axes' images; return the image. + Add an `.AxesImage` to the Axes; return the image. """ + self._deprecate_noninstance('add_image', mimage.AxesImage, image=image) self._set_artist_props(image) if not image.get_label(): - image.set_label('_image%d' % len(self.images)) - self.images.append(image) - image._remove_method = self.images.remove + image.set_label(f'_child{len(self._children)}') + self._children.append(image) + image._remove_method = self._children.remove self.stale = True return image @@ -2096,27 +2330,29 @@ def _update_image_limits(self, image): def add_line(self, line): """ - Add a `.Line2D` to the axes' lines; return the line. + Add a `.Line2D` to the Axes; return the line. """ + self._deprecate_noninstance('add_line', mlines.Line2D, line=line) self._set_artist_props(line) if line.get_clip_path() is None: line.set_clip_path(self.patch) self._update_line_limits(line) if not line.get_label(): - line.set_label('_line%d' % len(self.lines)) - self.lines.append(line) - line._remove_method = self.lines.remove + line.set_label(f'_child{len(self._children)}') + self._children.append(line) + line._remove_method = self._children.remove self.stale = True return line def _add_text(self, txt): """ - Add a `~.Text` to the axes' texts; return the text. + Add a `.Text` to the Axes; return the text. """ + self._deprecate_noninstance('_add_text', mtext.Text, txt=txt) self._set_artist_props(txt) - self.texts.append(txt) - txt._remove_method = self.texts.remove + self._children.append(txt) + txt._remove_method = self._children.remove self.stale = True return txt @@ -2128,52 +2364,57 @@ def _update_line_limits(self, line): if path.vertices.size == 0: return - line_trans = line.get_transform() + line_trf = line.get_transform() - if line_trans == self.transData: + if line_trf == self.transData: data_path = path - - elif any(line_trans.contains_branch_seperately(self.transData)): - # identify the transform to go from line's coordinates - # to data coordinates - trans_to_data = line_trans - self.transData - - # if transData is affine we can use the cached non-affine component - # of line's path. (since the non-affine part of line_trans is - # entirely encapsulated in trans_to_data). + elif any(line_trf.contains_branch_seperately(self.transData)): + # Compute the transform from line coordinates to data coordinates. + trf_to_data = line_trf - self.transData + # If transData is affine we can use the cached non-affine component + # of line's path (since the non-affine part of line_trf is + # entirely encapsulated in trf_to_data). if self.transData.is_affine: line_trans_path = line._get_transformed_path() na_path, _ = line_trans_path.get_transformed_path_and_affine() - data_path = trans_to_data.transform_path_affine(na_path) + data_path = trf_to_data.transform_path_affine(na_path) else: - data_path = trans_to_data.transform_path(path) + data_path = trf_to_data.transform_path(path) else: - # for backwards compatibility we update the dataLim with the + # For backwards compatibility we update the dataLim with the # coordinate range of the given path, even though the coordinate # systems are completely different. This may occur in situations # such as when ax.transAxes is passed through for absolute # positioning. data_path = path - if data_path.vertices.size > 0: - updatex, updatey = line_trans.contains_branch_seperately( - self.transData) - self.dataLim.update_from_path(data_path, - self.ignore_existing_data_limits, - updatex=updatex, - updatey=updatey) - self.ignore_existing_data_limits = False + if not data_path.vertices.size: + return + + updatex, updatey = line_trf.contains_branch_seperately(self.transData) + if self.name != "rectilinear": + # This block is mostly intended to handle axvline in polar plots, + # for which updatey would otherwise be True. + if updatex and line_trf == self.get_yaxis_transform(): + updatex = False + if updatey and line_trf == self.get_xaxis_transform(): + updatey = False + self.dataLim.update_from_path(data_path, + self.ignore_existing_data_limits, + updatex=updatex, updatey=updatey) + self.ignore_existing_data_limits = False def add_patch(self, p): """ - Add a `~.Patch` to the axes' patches; return the patch. + Add a `.Patch` to the Axes; return the patch. """ + self._deprecate_noninstance('add_patch', mpatches.Patch, p=p) self._set_artist_props(p) if p.get_clip_path() is None: p.set_clip_path(self.patch) self._update_patch_limits(p) - self.patches.append(p) - p._remove_method = self.patches.remove + self._children.append(p) + p._remove_method = self._children.remove return p def _update_patch_limits(self, patch): @@ -2190,33 +2431,46 @@ def _update_patch_limits(self, patch): ((not patch.get_width()) and (not patch.get_height()))): return p = patch.get_path() - vertices = p.vertices if p.codes is None else p.vertices[np.isin( - p.codes, (mpath.Path.CLOSEPOLY, mpath.Path.STOP), invert=True)] - if vertices.size > 0: - xys = patch.get_patch_transform().transform(vertices) - if patch.get_data_transform() != self.transData: - patch_to_data = (patch.get_data_transform() - - self.transData) - xys = patch_to_data.transform(xys) - - updatex, updatey = patch.get_transform().\ - contains_branch_seperately(self.transData) - self.update_datalim(xys, updatex=updatex, - updatey=updatey) + # Get all vertices on the path + # Loop through each segment to get extrema for Bezier curve sections + vertices = [] + for curve, code in p.iter_bezier(simplify=False): + # Get distance along the curve of any extrema + _, dzeros = curve.axis_aligned_extrema() + # Calculate vertices of start, end and any extrema in between + vertices.append(curve([0, *dzeros, 1])) + + if len(vertices): + vertices = np.row_stack(vertices) + + patch_trf = patch.get_transform() + updatex, updatey = patch_trf.contains_branch_seperately(self.transData) + if not (updatex or updatey): + return + if self.name != "rectilinear": + # As in _update_line_limits, but for axvspan. + if updatex and patch_trf == self.get_yaxis_transform(): + updatex = False + if updatey and patch_trf == self.get_xaxis_transform(): + updatey = False + trf_to_data = patch_trf - self.transData + xys = trf_to_data.transform(vertices) + self.update_datalim(xys, updatex=updatex, updatey=updatey) def add_table(self, tab): """ - Add a `~.Table` to the axes' tables; return the table. + Add a `.Table` to the Axes; return the table. """ + self._deprecate_noninstance('add_table', mtable.Table, tab=tab) self._set_artist_props(tab) - self.tables.append(tab) + self._children.append(tab) tab.set_clip_path(self.patch) - tab._remove_method = self.tables.remove + tab._remove_method = self._children.remove return tab def add_container(self, container): """ - Add a `~.Container` to the axes' containers; return the container. + Add a `.Container` to the Axes' containers; return the container. """ label = container.get_label() if not label: @@ -2232,16 +2486,17 @@ def _unit_change_handler(self, axis_name, event=None): if event is None: # Allow connecting `self._unit_change_handler(name)` return functools.partial( self._unit_change_handler, axis_name, event=object()) - _api.check_in_list(self._get_axis_map(), axis_name=axis_name) + _api.check_in_list(self._axis_map, axis_name=axis_name) + for line in self.lines: + line.recache_always() self.relim() - self._request_autoscale_view(scalex=(axis_name == "x"), - scaley=(axis_name == "y")) + self._request_autoscale_view(axis_name) def relim(self, visible_only=False): """ Recompute the data limits based on current artists. - At present, `~.Collection` instances are not supported. + At present, `.Collection` instances are not supported. Parameters ---------- @@ -2254,17 +2509,14 @@ def relim(self, visible_only=False): self.dataLim.set_points(mtransforms.Bbox.null().get_points()) self.ignore_existing_data_limits = True - for line in self.lines: - if not visible_only or line.get_visible(): - self._update_line_limits(line) - - for p in self.patches: - if not visible_only or p.get_visible(): - self._update_patch_limits(p) - - for image in self.images: - if not visible_only or image.get_visible(): - self._update_image_limits(image) + for artist in self._children: + if not visible_only or artist.get_visible(): + if isinstance(artist, mlines.Line2D): + self._update_line_limits(artist) + elif isinstance(artist, mpatches.Patch): + self._update_patch_limits(artist) + elif isinstance(artist, mimage.AxesImage): + self._update_image_limits(artist) def update_datalim(self, xys, updatex=True, updatey=True): """ @@ -2291,19 +2543,6 @@ def update_datalim(self, xys, updatex=True, updatey=True): updatex=updatex, updatey=updatey) self.ignore_existing_data_limits = False - @_api.deprecated( - "3.3", alternative="ax.dataLim.set(Bbox.union([ax.dataLim, bounds]))") - def update_datalim_bounds(self, bounds): - """ - Extend the `~.Axes.datalim` Bbox to include the given - `~matplotlib.transforms.Bbox`. - - Parameters - ---------- - bounds : `~matplotlib.transforms.Bbox` - """ - self.dataLim.set(mtransforms.Bbox.union([self.dataLim, bounds])) - def _process_unit_info(self, datasets=None, kwargs=None, *, convert=True): """ Set axis units based on *datasets* and *kwargs*, and optionally apply @@ -2313,11 +2552,12 @@ def _process_unit_info(self, datasets=None, kwargs=None, *, convert=True): ---------- datasets : list List of (axis_name, dataset) pairs (where the axis name is defined - as in `._get_axis_map`. + as in `._axis_map`). Individual datasets can also be None + (which gets passed through). kwargs : dict Other parameters from which unit info (i.e., the *xunits*, - *yunits*, *zunits* (for 3D axes), *runits* and *thetaunits* (for - polar axes) entries) is popped, if present. Note that this dict is + *yunits*, *zunits* (for 3D Axes), *runits* and *thetaunits* (for + polar) entries) is popped, if present. Note that this dict is mutated in-place! convert : bool, default: True Whether to return the original datasets or the converted ones. @@ -2334,7 +2574,7 @@ def _process_unit_info(self, datasets=None, kwargs=None, *, convert=True): # (e.g. if some are scalars, etc.). datasets = datasets or [] kwargs = kwargs or {} - axis_map = self._get_axis_map() + axis_map = self._axis_map for axis_name, data in datasets: try: axis = axis_map[axis_name] @@ -2360,7 +2600,8 @@ def _process_unit_info(self, datasets=None, kwargs=None, *, convert=True): for dataset_axis_name, data in datasets: if dataset_axis_name == axis_name and data is not None: axis.update_units(data) - return [axis_map[axis_name].convert_units(data) if convert else data + return [axis_map[axis_name].convert_units(data) + if convert and data is not None else data for axis_name, data in datasets] def in_axes(self, mouseevent): @@ -2369,57 +2610,27 @@ def in_axes(self, mouseevent): """ return self.patch.contains(mouseevent)[0] - def get_autoscale_on(self): - """ - Get whether autoscaling is applied for both axes on plot commands - """ - return self._autoscaleXon and self._autoscaleYon - - def get_autoscalex_on(self): - """ - Get whether autoscaling for the x-axis is applied on plot commands - """ - return self._autoscaleXon + get_autoscalex_on = _axis_method_wrapper("xaxis", "_get_autoscale_on") + get_autoscaley_on = _axis_method_wrapper("yaxis", "_get_autoscale_on") + set_autoscalex_on = _axis_method_wrapper("xaxis", "_set_autoscale_on") + set_autoscaley_on = _axis_method_wrapper("yaxis", "_set_autoscale_on") - def get_autoscaley_on(self): - """ - Get whether autoscaling for the y-axis is applied on plot commands - """ - return self._autoscaleYon + def get_autoscale_on(self): + """Return True if each axis is autoscaled, False otherwise.""" + return all(axis._get_autoscale_on() + for axis in self._axis_map.values()) def set_autoscale_on(self, b): """ - Set whether autoscaling is applied to axes on the next draw or call to - `.Axes.autoscale_view`. - - Parameters - ---------- - b : bool - """ - self._autoscaleXon = b - self._autoscaleYon = b - - def set_autoscalex_on(self, b): - """ - Set whether autoscaling for the x-axis is applied to axes on the next - draw or call to `.Axes.autoscale_view`. + Set whether autoscaling is applied to each axis on the next draw or + call to `.Axes.autoscale_view`. Parameters ---------- b : bool """ - self._autoscaleXon = b - - def set_autoscaley_on(self, b): - """ - Set whether autoscaling for the y-axis is applied to axes on the next - draw or call to `.Axes.autoscale_view`. - - Parameters - ---------- - b : bool - """ - self._autoscaleYon = b + for axis in self._axis_map.values(): + axis._set_autoscale_on(b) @property def use_sticky_edges(self): @@ -2441,20 +2652,19 @@ def use_sticky_edges(self): @use_sticky_edges.setter def use_sticky_edges(self, b): self._use_sticky_edges = bool(b) - # No effect until next autoscaling, which will mark the axes as stale. + # No effect until next autoscaling, which will mark the Axes as stale. def set_xmargin(self, m): """ Set padding of X data limits prior to autoscaling. - *m* times the data interval will be added to each - end of that interval before it is used in autoscaling. - For example, if your data is in the range [0, 2], a factor of - ``m = 0.1`` will result in a range [-0.2, 2.2]. + *m* times the data interval will be added to each end of that interval + before it is used in autoscaling. If *m* is negative, this will clip + the data range instead of expanding it. - Negative values -0.5 < m < 0 will result in clipping of the data range. - I.e. for a data range [0, 2], a factor of ``m = -0.1`` will result in - a range [0.2, 1.8]. + For example, if your data is in the range [0, 2], a margin of 0.1 will + result in a range [-0.2, 2.2]; a margin of -0.1 will result in a range + of [0.2, 1.8]. Parameters ---------- @@ -2463,21 +2673,20 @@ def set_xmargin(self, m): if m <= -0.5: raise ValueError("margin must be greater than -0.5") self._xmargin = m - self._request_autoscale_view(scalex=True, scaley=False) + self._request_autoscale_view("x") self.stale = True def set_ymargin(self, m): """ Set padding of Y data limits prior to autoscaling. - *m* times the data interval will be added to each - end of that interval before it is used in autoscaling. - For example, if your data is in the range [0, 2], a factor of - ``m = 0.1`` will result in a range [-0.2, 2.2]. + *m* times the data interval will be added to each end of that interval + before it is used in autoscaling. If *m* is negative, this will clip + the data range instead of expanding it. - Negative values -0.5 < m < 0 will result in clipping of the data range. - I.e. for a data range [0, 2], a factor of ``m = -0.1`` will result in - a range [0.2, 1.8]. + For example, if your data is in the range [0, 2], a margin of 0.1 will + result in a range [-0.2, 2.2]; a margin of -0.1 will result in a range + of [0.2, 1.8]. Parameters ---------- @@ -2486,14 +2695,14 @@ def set_ymargin(self, m): if m <= -0.5: raise ValueError("margin must be greater than -0.5") self._ymargin = m - self._request_autoscale_view(scalex=False, scaley=True) + self._request_autoscale_view("y") self.stale = True def margins(self, *margins, x=None, y=None, tight=True): """ Set or retrieve autoscaling margins. - The padding added to each limit of the axes is the *margin* + The padding added to each limit of the Axes is the *margin* times the data interval. All input parameters must be floats within the range [0, 1]. Passing both positional and keyword arguments is invalid and will raise a TypeError. If no @@ -2521,11 +2730,11 @@ def margins(self, *margins, x=None, y=None, tight=True): only the y-axis. tight : bool or None, default: True - The *tight* parameter is passed to :meth:`autoscale_view`, + The *tight* parameter is passed to `~.axes.Axes.autoscale_view`, which is executed after a margin is changed; the default here is *True*, on the assumption that when margins are specified, no additional padding to match tick marks is - usually desired. Set *tight* to *None* will preserve + usually desired. Setting *tight* to *None* preserves the previous setting. Returns @@ -2541,7 +2750,7 @@ def margins(self, *margins, x=None, y=None, tight=True): before calling :meth:`margins`. """ - if margins and x is not None and y is not None: + if margins and (x is not None or y is not None): raise TypeError('Cannot pass both positional and keyword ' 'arguments for x and/or y.') elif len(margins) == 1: @@ -2595,7 +2804,7 @@ def autoscale(self, enable=True, axis='both', tight=None): Convenience method for simple axis view autoscaling. It turns autoscaling on or off, and then, if autoscaling for either axis is on, it performs - the autoscaling on the specified axis or axes. + the autoscaling on the specified axis or Axes. Parameters ---------- @@ -2603,29 +2812,35 @@ def autoscale(self, enable=True, axis='both', tight=None): True turns autoscaling on, False turns it off. None leaves the autoscaling state unchanged. axis : {'both', 'x', 'y'}, default: 'both' - Which axis to operate on. + The axis on which to operate. (For 3D Axes, *axis* can also be set + to 'z', and 'both' refers to all three axes.) tight : bool or None, default: None If True, first set the margins to zero. Then, this argument is - forwarded to `autoscale_view` (regardless of its value); see the - description of its behavior there. + forwarded to `~.axes.Axes.autoscale_view` (regardless of + its value); see the description of its behavior there. """ if enable is None: scalex = True scaley = True else: - scalex = False - scaley = False if axis in ['x', 'both']: - self._autoscaleXon = bool(enable) - scalex = self._autoscaleXon + self.set_autoscalex_on(bool(enable)) + scalex = self.get_autoscalex_on() + else: + scalex = False if axis in ['y', 'both']: - self._autoscaleYon = bool(enable) - scaley = self._autoscaleYon + self.set_autoscaley_on(bool(enable)) + scaley = self.get_autoscaley_on() + else: + scaley = False if tight and scalex: self._xmargin = 0 if tight and scaley: self._ymargin = 0 - self._request_autoscale_view(tight=tight, scalex=scalex, scaley=scaley) + if scalex: + self._request_autoscale_view("x", tight=tight) + if scaley: + self._request_autoscale_view("y", tight=tight) def autoscale_view(self, tight=None, scalex=True, scaley=True): """ @@ -2662,7 +2877,7 @@ def autoscale_view(self, tight=None, scalex=True, scaley=True): case, use :meth:`matplotlib.axes.Axes.relim` prior to calling autoscale_view. - If the views of the axes are fixed, e.g. via `set_xlim`, they will + If the views of the Axes are fixed, e.g. via `set_xlim`, they will not be changed by autoscale_view(). See :meth:`matplotlib.axes.Axes.autoscale` for an alternative. """ @@ -2671,53 +2886,54 @@ def autoscale_view(self, tight=None, scalex=True, scaley=True): x_stickies = y_stickies = np.array([]) if self.use_sticky_edges: - # Only iterate over axes and artists if needed. The check for - # ``hasattr(ax, "lines")`` is necessary because this can be called - # very early in the axes init process (e.g., for twin axes) when - # these attributes don't even exist yet, in which case + # Only iterate over Axes and artists if needed. The check for + # ``hasattr(ax, "_children")`` is necessary because this can be + # called very early in the Axes init process (e.g., for twin Axes) + # when these attributes don't even exist yet, in which case # `get_children` would raise an AttributeError. - if self._xmargin and scalex and self._autoscaleXon: + if self._xmargin and scalex and self.get_autoscalex_on(): x_stickies = np.sort(np.concatenate([ artist.sticky_edges.x - for ax in self._shared_x_axes.get_siblings(self) - if hasattr(ax, "lines") + for ax in self._shared_axes["x"].get_siblings(self) + if hasattr(ax, "_children") for artist in ax.get_children()])) - if self._ymargin and scaley and self._autoscaleYon: + if self._ymargin and scaley and self.get_autoscaley_on(): y_stickies = np.sort(np.concatenate([ artist.sticky_edges.y - for ax in self._shared_y_axes.get_siblings(self) - if hasattr(ax, "lines") + for ax in self._shared_axes["y"].get_siblings(self) + if hasattr(ax, "_children") for artist in ax.get_children()])) if self.get_xscale() == 'log': x_stickies = x_stickies[x_stickies > 0] if self.get_yscale() == 'log': y_stickies = y_stickies[y_stickies > 0] - def handle_single_axis(scale, autoscaleon, shared_axes, interval, - minpos, axis, margin, stickies, set_bound): + def handle_single_axis( + scale, shared_axes, name, axis, margin, stickies, set_bound): - if not (scale and autoscaleon): + if not (scale and axis._get_autoscale_on()): return # nothing to do... shared = shared_axes.get_siblings(self) # Base autoscaling on finite data limits when there is at least one # finite data limit among all the shared_axes and intervals. - # Also, find the minimum minpos for use in the margin calculation. - x_values = [] - minimum_minpos = np.inf - for ax in shared: - x_values.extend(getattr(ax.dataLim, interval)) - minimum_minpos = min(minimum_minpos, - getattr(ax.dataLim, minpos)) - x_values = np.extract(np.isfinite(x_values), x_values) - if x_values.size >= 1: - x0, x1 = (x_values.min(), x_values.max()) + values = [val for ax in shared + for val in getattr(ax.dataLim, f"interval{name}") + if np.isfinite(val)] + if values: + x0, x1 = (min(values), max(values)) + elif getattr(self._viewLim, f"mutated{name}")(): + # No data, but explicit viewLims already set: + # in mutatedx or mutatedy. + return else: x0, x1 = (-np.inf, np.inf) - # If x0 and x1 are non finite, use the locator to figure out - # default limits. + # If x0 and x1 are nonfinite, get default limits from the locator. locator = axis.get_major_locator() x0, x1 = locator.nonsingular(x0, x1) + # Find the minimum minpos for use in the margin calculation. + minimum_minpos = min( + getattr(ax.dataLim, f"minpos{name}") for ax in shared) # Prevent margin addition from crossing a sticky value. A small # tolerance must be added due to floating point issues with @@ -2755,32 +2971,11 @@ def handle_single_axis(scale, autoscaleon, shared_axes, interval, # End of definition of internal function 'handle_single_axis'. handle_single_axis( - scalex, self._autoscaleXon, self._shared_x_axes, 'intervalx', - 'minposx', self.xaxis, self._xmargin, x_stickies, self.set_xbound) + scalex, self._shared_axes["x"], 'x', self.xaxis, self._xmargin, + x_stickies, self.set_xbound) handle_single_axis( - scaley, self._autoscaleYon, self._shared_y_axes, 'intervaly', - 'minposy', self.yaxis, self._ymargin, y_stickies, self.set_ybound) - - def _get_axis_list(self): - return self.xaxis, self.yaxis - - def _get_axis_map(self): - """ - Return a mapping of `Axis` "names" to `Axis` instances. - - The `Axis` name is derived from the attribute under which the instance - is stored, so e.g. for polar axes, the theta-axis is still named "x" - and the r-axis is still named "y" (for back-compatibility). - - In practice, this means that the entries are typically "x" and "y", and - additionally "z" for 3D axes. - """ - d = {} - axis_list = self._get_axis_list() - for k, v in vars(self).items(): - if k.endswith("axis") and v in axis_list: - d[k[:-len("axis")]] = v - return d + scaley, self._shared_axes["y"], 'y', self.yaxis, self._ymargin, + y_stickies, self.set_ybound) def _update_title_position(self, renderer): """ @@ -2793,35 +2988,39 @@ def _update_title_position(self, renderer): titles = (self.title, self._left_title, self._right_title) + # Need to check all our twins too, and all the children as well. + axs = self._twinned_axes.get_siblings(self) + self.child_axes + for ax in self.child_axes: # Child positions must be updated first. + locator = ax.get_axes_locator() + ax.apply_aspect(locator(self, renderer) if locator else None) + for title in titles: x, _ = title.get_position() # need to start again in case of window resizing title.set_position((x, 1.0)) - # need to check all our twins too... - axs = self._twinned_axes.get_siblings(self) - # and all the children - for ax in self.child_axes: - if ax is not None: - locator = ax.get_axes_locator() - if locator: - pos = locator(self, renderer) - ax.apply_aspect(pos) - else: - ax.apply_aspect() - axs = axs + [ax] - top = -np.Inf + top = -np.inf for ax in axs: + bb = None if (ax.xaxis.get_ticks_position() in ['top', 'unknown'] or ax.xaxis.get_label_position() == 'top'): bb = ax.xaxis.get_tightbbox(renderer) - else: - bb = ax.get_window_extent(renderer) - if bb is not None: - top = max(top, bb.ymax) + if bb is None: + if 'outline' in ax.spines: + # Special case for colorbars: + bb = ax.spines['outline'].get_window_extent() + else: + bb = ax.get_window_extent(renderer) + top = max(top, bb.ymax) + if title.get_text(): + ax.yaxis.get_tightbbox(renderer) # update offsetText + if ax.yaxis.offsetText.get_text(): + bb = ax.yaxis.offsetText.get_tightbbox(renderer) + if bb.intersection(title.get_tightbbox(renderer), bb): + top = bb.ymax if top < 0: - # the top of axes is not even on the figure, so don't try and + # the top of Axes is not even on the figure, so don't try and # automatically place it. - _log.debug('top of axes not in the figure, so title not moved') + _log.debug('top of Axes not in the figure, so title not moved') return if title.get_window_extent(renderer).ymin < top: _, y = self.transAxes.inverted().transform((0, top)) @@ -2841,17 +3040,8 @@ def _update_title_position(self, renderer): # Drawing @martist.allow_rasterization - @_api.delete_parameter( - "3.3", "inframe", alternative="Axes.redraw_in_frame()") - def draw(self, renderer=None, inframe=False): + def draw(self, renderer): # docstring inherited - if renderer is None: - _api.warn_deprecated( - "3.3", message="Support for not passing the 'renderer' " - "parameter to Axes.draw() is deprecated since %(since)s and " - "will be removed %(removal)s. Use axes.draw_artist(axes) " - "instead.") - renderer = self.figure._cachedRenderer if renderer is None: raise RuntimeError('No renderer defined') if not self.get_visible(): @@ -2863,18 +3053,14 @@ def draw(self, renderer=None, inframe=False): # prevent triggering call backs during the draw process self._stale = True - # loop over self and child axes... + # loop over self and child Axes... locator = self.get_axes_locator() - if locator: - pos = locator(self, renderer) - self.apply_aspect(pos) - else: - self.apply_aspect() + self.apply_aspect(locator(self, renderer) if locator else None) artists = self.get_children() artists.remove(self.patch) - # the frame draws the edges around the axes patch -- we + # the frame draws the edges around the Axes patch -- we # decouple these so the patch can be in the background and the # frame in the foreground. Do this before drawing the axis # objects so that the spine has the opportunity to update them. @@ -2884,18 +3070,14 @@ def draw(self, renderer=None, inframe=False): self._update_title_position(renderer) - if not self.axison or inframe: - for _axis in self._get_axis_list(): + if not self.axison: + for _axis in self._axis_map.values(): artists.remove(_axis) - if inframe: - artists.remove(self.title) - artists.remove(self._left_title) - artists.remove(self._right_title) - if not self.figure.canvas.is_saving(): - artists = [a for a in artists - if not a.get_animated() or a in self.images] + artists = [ + a for a in artists + if not a.get_animated() or isinstance(a, mimage.AxesImage)] artists = sorted(artists, key=attrgetter('zorder')) # rasterize artists with negative zorder @@ -2922,7 +3104,8 @@ def draw(self, renderer=None, inframe=False): a.draw(renderer) renderer.stop_rasterizing() - mimage._draw_list_compositing_images(renderer, self, artists) + mimage._draw_list_compositing_images( + renderer, self, artists, self.figure.suppressComposite) renderer.close_group('axes') self.stale = False @@ -2930,44 +3113,32 @@ def draw(self, renderer=None, inframe=False): def draw_artist(self, a): """ Efficiently redraw a single artist. - - This method can only be used after an initial draw of the figure, - because that creates and caches the renderer needed here. """ - if self.figure._cachedRenderer is None: - raise AttributeError("draw_artist can only be used after an " - "initial draw which caches the renderer") - a.draw(self.figure._cachedRenderer) + a.draw(self.figure.canvas.get_renderer()) def redraw_in_frame(self): """ Efficiently redraw Axes data, but not axis ticks, labels, etc. - - This method can only be used after an initial draw which caches the - renderer. """ - if self.figure._cachedRenderer is None: - raise AttributeError("redraw_in_frame can only be used after an " - "initial draw which caches the renderer") with ExitStack() as stack: - for artist in [*self._get_axis_list(), + for artist in [*self._axis_map.values(), self.title, self._left_title, self._right_title]: - stack.callback(artist.set_visible, artist.get_visible()) - artist.set_visible(False) - self.draw(self.figure._cachedRenderer) + stack.enter_context(artist._cm_set(visible=False)) + self.draw(self.figure.canvas.get_renderer()) + @_api.deprecated("3.6", alternative="Axes.figure.canvas.get_renderer()") def get_renderer_cache(self): - return self.figure._cachedRenderer + return self.figure.canvas.get_renderer() # Axes rectangle characteristics def get_frame_on(self): - """Get whether the axes rectangle patch is drawn.""" + """Get whether the Axes rectangle patch is drawn.""" return self._frameon def set_frame_on(self, b): """ - Set whether the axes rectangle patch is drawn. + Set whether the Axes rectangle patch is drawn. Parameters ---------- @@ -3013,31 +3184,30 @@ def set_axisbelow(self, b): -------- get_axisbelow """ + # Check that b is True, False or 'line' self._axisbelow = axisbelow = validate_axisbelow(b) - if axisbelow is True: - zorder = 0.5 - elif axisbelow is False: - zorder = 2.5 - elif axisbelow == "line": - zorder = 1.5 - else: - raise ValueError("Unexpected axisbelow value") - for axis in self._get_axis_list(): + zorder = { + True: 0.5, + 'line': 1.5, + False: 2.5, + }[axisbelow] + for axis in self._axis_map.values(): axis.set_zorder(zorder) self.stale = True - @docstring.dedent_interpd - def grid(self, b=None, which='major', axis='both', **kwargs): + @_docstring.dedent_interpd + @_api.rename_parameter("3.5", "b", "visible") + def grid(self, visible=None, which='major', axis='both', **kwargs): """ Configure the grid lines. Parameters ---------- - b : bool or None, optional - Whether to show the grid lines. If any *kwargs* are supplied, - it is assumed you want the grid on and *b* will be set to True. + visible : bool or None, optional + Whether to show the grid lines. If any *kwargs* are supplied, it + is assumed you want the grid on and *visible* will be set to True. - If *b* is *None* and there are no *kwargs*, this toggles the + If *visible* is *None* and there are no *kwargs*, this toggles the visibility of the lines. which : {'major', 'minor', 'both'}, optional @@ -3053,7 +3223,7 @@ def grid(self, b=None, which='major', axis='both', **kwargs): Valid keyword arguments are: - %(Line2D_kwdoc)s + %(Line2D:kwdoc)s Notes ----- @@ -3065,14 +3235,14 @@ def grid(self, b=None, which='major', axis='both', **kwargs): """ _api.check_in_list(['x', 'y', 'both'], axis=axis) if axis in ['x', 'both']: - self.xaxis.grid(b, which=which, **kwargs) + self.xaxis.grid(visible, which=which, **kwargs) if axis in ['y', 'both']: - self.yaxis.grid(b, which=which, **kwargs) + self.yaxis.grid(visible, which=which, **kwargs) def ticklabel_format(self, *, axis='both', style='', scilimits=None, useOffset=None, useLocale=None, useMathText=None): r""" - Configure the `.ScalarFormatter` used by default for linear axes. + Configure the `.ScalarFormatter` used by default for linear Axes. If a parameter is not set, the corresponding property of the formatter is left unchanged. @@ -3080,7 +3250,7 @@ def ticklabel_format(self, *, axis='both', style='', scilimits=None, Parameters ---------- axis : {'x', 'y', 'both'}, default: 'both' - The axes to configure. Only major ticks are affected. + The axis to configure. Only major ticks are affected. style : {'sci', 'scientific', 'plain'} Whether to use scientific notation. @@ -3125,8 +3295,8 @@ def ticklabel_format(self, *, axis='both', style='', scilimits=None, ) from err STYLES = {'sci': True, 'scientific': True, 'plain': False, '': None} is_sci_style = _api.check_getitem(STYLES, style=style) - axis_map = {**{k: [v] for k, v in self._get_axis_map().items()}, - 'both': self._get_axis_list()} + axis_map = {**{k: [v] for k, v in self._axis_map.items()}, + 'both': list(self._axis_map.values())} axises = _api.check_getitem(axis_map, axis=axis) try: for axis in axises: @@ -3154,8 +3324,8 @@ def locator_params(self, axis='both', tight=None, **kwargs): Parameters ---------- axis : {'both', 'x', 'y'}, default: 'both' - The axis on which to operate. - + The axis on which to operate. (For 3D Axes, *axis* can also be + set to 'z', and 'both' refers to all three axes.) tight : bool or None, optional Parameter passed to `~.Axes.autoscale_view`. Default is None, for no change. @@ -3167,7 +3337,7 @@ def locator_params(self, axis='both', tight=None, **kwargs): ``set_params()`` method of the locator. Supported keywords depend on the type of the locator. See for example `~.ticker.MaxNLocator.set_params` for the `.ticker.MaxNLocator` - used by default for linear axes. + used by default for linear. Examples -------- @@ -3177,15 +3347,12 @@ def locator_params(self, axis='both', tight=None, **kwargs): ax.locator_params(tight=True, nbins=4) """ - _api.check_in_list(['x', 'y', 'both'], axis=axis) - update_x = axis in ['x', 'both'] - update_y = axis in ['y', 'both'] - if update_x: - self.xaxis.get_major_locator().set_params(**kwargs) - if update_y: - self.yaxis.get_major_locator().set_params(**kwargs) - self._request_autoscale_view(tight=tight, - scalex=update_x, scaley=update_y) + _api.check_in_list([*self._axis_names, "both"], axis=axis) + for name in self._axis_names: + if axis in [name, "both"]: + loc = self._axis_map[name].get_major_locator() + loc.set_params(**kwargs) + self._request_autoscale_view(name, tight=tight) self.stale = True def tick_params(self, axis='both', **kwargs): @@ -3207,7 +3374,7 @@ def tick_params(self, axis='both', **kwargs): Other Parameters ---------------- direction : {'in', 'out', 'inout'} - Puts ticks inside the axes, outside the axes, or both. + Puts ticks inside the Axes, outside the Axes, or both. length : float Tick length in points. width : float @@ -3305,7 +3472,7 @@ def set_xlabel(self, xlabel, fontdict=None, labelpad=None, *, The label text. labelpad : float, default: :rc:`axes.labelpad` - Spacing in points from the axes bounding box including ticks + Spacing in points from the Axes bounding box including ticks and tick labels. If None, the previous value is left as is. loc : {'left', 'center', 'right'}, default: :rc:`xaxis.labellocation` @@ -3330,15 +3497,19 @@ def set_xlabel(self, xlabel, fontdict=None, labelpad=None, *, f"its corresponding low level keyword " f"arguments ({protected_kw}) are also " f"supplied") - loc = 'center' + else: loc = (loc if loc is not None else mpl.rcParams['xaxis.labellocation']) - _api.check_in_list(('left', 'center', 'right'), loc=loc) - if loc == 'left': - kwargs.update(x=0, horizontalalignment='left') - elif loc == 'right': - kwargs.update(x=1, horizontalalignment='right') + _api.check_in_list(('left', 'center', 'right'), loc=loc) + + x = { + 'left': 0, + 'center': 0.5, + 'right': 1, + }[loc] + kwargs.update(x=x, horizontalalignment=loc) + return self.xaxis.set_label_text(xlabel, fontdict, **kwargs) def invert_xaxis(self): @@ -3375,7 +3546,7 @@ def set_xbound(self, lower=None, upper=None): """ Set the lower and upper numerical bounds of the x-axis. - This method will honor axes inversion regardless of parameter order. + This method will honor axis inversion regardless of parameter order. It will not change the autoscaling setting (`.get_autoscalex_on()`). Parameters @@ -3414,7 +3585,7 @@ def get_xlim(self): See Also -------- - set_xlim + .Axes.set_xlim set_xbound, get_xbound invert_xaxis, xaxis_inverted @@ -3422,7 +3593,6 @@ def get_xlim(self): ----- The x-axis may be inverted, in which case the *left* value will be greater than the *right* value. - """ return tuple(self.viewLim.intervalx) @@ -3443,6 +3613,7 @@ def _validate_converted_limits(self, limit, convert): raise ValueError("Axis limits cannot be NaN or Inf") return converted_limit + @_api.make_keyword_only("3.6", "emit") def set_xlim(self, left=None, right=None, emit=True, auto=False, *, xmin=None, xmax=None): """ @@ -3472,9 +3643,8 @@ def set_xlim(self, left=None, right=None, emit=True, auto=False, False turns off, None leaves unchanged. xmin, xmax : float, optional - They are equivalent to left and right respectively, - and it is an error to pass both *xmin* and *left* or - *xmax* and *right*. + They are equivalent to left and right respectively, and it is an + error to pass both *xmin* and *left* or *xmax* and *right*. Returns ------- @@ -3509,119 +3679,21 @@ def set_xlim(self, left=None, right=None, emit=True, auto=False, present is on the right. >>> set_xlim(5000, 0) - """ if right is None and np.iterable(left): left, right = left if xmin is not None: if left is not None: - raise TypeError('Cannot pass both `xmin` and `left`') + raise TypeError("Cannot pass both 'left' and 'xmin'") left = xmin if xmax is not None: if right is not None: - raise TypeError('Cannot pass both `xmax` and `right`') + raise TypeError("Cannot pass both 'right' and 'xmax'") right = xmax - - self._process_unit_info([("x", (left, right))], convert=False) - left = self._validate_converted_limits(left, self.convert_xunits) - right = self._validate_converted_limits(right, self.convert_xunits) - - if left is None or right is None: - # Axes init calls set_xlim(0, 1) before get_xlim() can be called, - # so only grab the limits if we really need them. - old_left, old_right = self.get_xlim() - if left is None: - left = old_left - if right is None: - right = old_right - - if self.get_xscale() == 'log' and (left <= 0 or right <= 0): - # Axes init calls set_xlim(0, 1) before get_xlim() can be called, - # so only grab the limits if we really need them. - old_left, old_right = self.get_xlim() - if left <= 0: - _api.warn_external( - 'Attempted to set non-positive left xlim on a ' - 'log-scaled axis.\n' - 'Invalid limit will be ignored.') - left = old_left - if right <= 0: - _api.warn_external( - 'Attempted to set non-positive right xlim on a ' - 'log-scaled axis.\n' - 'Invalid limit will be ignored.') - right = old_right - if left == right: - _api.warn_external( - f"Attempting to set identical left == right == {left} results " - f"in singular transformations; automatically expanding.") - reverse = left > right - left, right = self.xaxis.get_major_locator().nonsingular(left, right) - left, right = self.xaxis.limit_range_for_scale(left, right) - # cast to bool to avoid bad interaction between python 3.8 and np.bool_ - left, right = sorted([left, right], reverse=bool(reverse)) - - self._viewLim.intervalx = (left, right) - # Mark viewlims as no longer stale without triggering an autoscale. - for ax in self._shared_x_axes.get_siblings(self): - ax._stale_viewlim_x = False - if auto is not None: - self._autoscaleXon = bool(auto) - - if emit: - self.callbacks.process('xlim_changed', self) - # Call all of the other x-axes that are shared with this one - for other in self._shared_x_axes.get_siblings(self): - if other is not self: - other.set_xlim(self.viewLim.intervalx, - emit=False, auto=auto) - if other.figure != self.figure: - other.figure.canvas.draw_idle() - self.stale = True - return left, right + return self.xaxis._set_lim(left, right, emit=emit, auto=auto) get_xscale = _axis_method_wrapper("xaxis", "get_scale") - - def set_xscale(self, value, **kwargs): - """ - Set the x-axis scale. - - Parameters - ---------- - value : {"linear", "log", "symlog", "logit", ...} or `.ScaleBase` - The axis scale type to apply. - - **kwargs - Different keyword arguments are accepted, depending on the scale. - See the respective class keyword arguments: - - - `matplotlib.scale.LinearScale` - - `matplotlib.scale.LogScale` - - `matplotlib.scale.SymmetricalLogScale` - - `matplotlib.scale.LogitScale` - - `matplotlib.scale.FuncScale` - - Notes - ----- - By default, Matplotlib supports the above mentioned scales. - Additionally, custom scales may be registered using - `matplotlib.scale.register_scale`. These scales can then also - be used here. - """ - old_default_lims = (self.xaxis.get_major_locator() - .nonsingular(-np.inf, np.inf)) - g = self.get_shared_x_axes() - for ax in g.get_siblings(self): - ax.xaxis._set_scale(value, **kwargs) - ax._update_transScale() - ax.stale = True - new_default_lims = (self.xaxis.get_major_locator() - .nonsingular(-np.inf, np.inf)) - if old_default_lims != new_default_lims: - # Force autoscaling now, to take advantage of the scale locator's - # nonsingular() before it possibly gets swapped out by the user. - self.autoscale_view(scaley=False) - + set_xscale = _axis_method_wrapper("xaxis", "_set_axes_scale") get_xticks = _axis_method_wrapper("xaxis", "get_ticklocs") set_xticks = _axis_method_wrapper("xaxis", "set_ticks") get_xmajorticklabels = _axis_method_wrapper("xaxis", "get_majorticklabels") @@ -3649,7 +3721,7 @@ def set_ylabel(self, ylabel, fontdict=None, labelpad=None, *, The label text. labelpad : float, default: :rc:`axes.labelpad` - Spacing in points from the axes bounding box including ticks + Spacing in points from the Axes bounding box including ticks and tick labels. If None, the previous value is left as is. loc : {'bottom', 'center', 'top'}, default: :rc:`yaxis.labellocation` @@ -3674,15 +3746,19 @@ def set_ylabel(self, ylabel, fontdict=None, labelpad=None, *, f"its corresponding low level keyword " f"arguments ({protected_kw}) are also " f"supplied") - loc = 'center' + else: loc = (loc if loc is not None else mpl.rcParams['yaxis.labellocation']) - _api.check_in_list(('bottom', 'center', 'top'), loc=loc) - if loc == 'bottom': - kwargs.update(y=0, horizontalalignment='left') - elif loc == 'top': - kwargs.update(y=1, horizontalalignment='right') + _api.check_in_list(('bottom', 'center', 'top'), loc=loc) + + y, ha = { + 'bottom': (0, 'left'), + 'center': (0.5, 'center'), + 'top': (1, 'right') + }[loc] + kwargs.update(y=y, horizontalalignment=ha) + return self.yaxis.set_label_text(ylabel, fontdict, **kwargs) def invert_yaxis(self): @@ -3719,7 +3795,7 @@ def set_ybound(self, lower=None, upper=None): """ Set the lower and upper numerical bounds of the y-axis. - This method will honor axes inversion regardless of parameter order. + This method will honor axis inversion regardless of parameter order. It will not change the autoscaling setting (`.get_autoscaley_on()`). Parameters @@ -3758,7 +3834,7 @@ def get_ylim(self): See Also -------- - set_ylim + .Axes.set_ylim set_ybound, get_ybound invert_yaxis, yaxis_inverted @@ -3766,10 +3842,10 @@ def get_ylim(self): ----- The y-axis may be inverted, in which case the *bottom* value will be greater than the *top* value. - """ return tuple(self.viewLim.intervaly) + @_api.make_keyword_only("3.6", "emit") def set_ylim(self, bottom=None, top=None, emit=True, auto=False, *, ymin=None, ymax=None): """ @@ -3799,9 +3875,8 @@ def set_ylim(self, bottom=None, top=None, emit=True, auto=False, *False* turns off, *None* leaves unchanged. ymin, ymax : float, optional - They are equivalent to bottom and top respectively, - and it is an error to pass both *ymin* and *bottom* or - *ymax* and *top*. + They are equivalent to bottom and top respectively, and it is an + error to pass both *ymin* and *bottom* or *ymax* and *top*. Returns ------- @@ -3841,114 +3916,16 @@ def set_ylim(self, bottom=None, top=None, emit=True, auto=False, bottom, top = bottom if ymin is not None: if bottom is not None: - raise TypeError('Cannot pass both `ymin` and `bottom`') + raise TypeError("Cannot pass both 'bottom' and 'ymin'") bottom = ymin if ymax is not None: if top is not None: - raise TypeError('Cannot pass both `ymax` and `top`') + raise TypeError("Cannot pass both 'top' and 'ymax'") top = ymax - - self._process_unit_info([("y", (bottom, top))], convert=False) - bottom = self._validate_converted_limits(bottom, self.convert_yunits) - top = self._validate_converted_limits(top, self.convert_yunits) - - if bottom is None or top is None: - # Axes init calls set_ylim(0, 1) before get_ylim() can be called, - # so only grab the limits if we really need them. - old_bottom, old_top = self.get_ylim() - if bottom is None: - bottom = old_bottom - if top is None: - top = old_top - - if self.get_yscale() == 'log' and (bottom <= 0 or top <= 0): - # Axes init calls set_xlim(0, 1) before get_xlim() can be called, - # so only grab the limits if we really need them. - old_bottom, old_top = self.get_ylim() - if bottom <= 0: - _api.warn_external( - 'Attempted to set non-positive bottom ylim on a ' - 'log-scaled axis.\n' - 'Invalid limit will be ignored.') - bottom = old_bottom - if top <= 0: - _api.warn_external( - 'Attempted to set non-positive top ylim on a ' - 'log-scaled axis.\n' - 'Invalid limit will be ignored.') - top = old_top - if bottom == top: - _api.warn_external( - f"Attempting to set identical bottom == top == {bottom} " - f"results in singular transformations; automatically " - f"expanding.") - reverse = bottom > top - bottom, top = self.yaxis.get_major_locator().nonsingular(bottom, top) - bottom, top = self.yaxis.limit_range_for_scale(bottom, top) - # cast to bool to avoid bad interaction between python 3.8 and np.bool_ - bottom, top = sorted([bottom, top], reverse=bool(reverse)) - - self._viewLim.intervaly = (bottom, top) - # Mark viewlims as no longer stale without triggering an autoscale. - for ax in self._shared_y_axes.get_siblings(self): - ax._stale_viewlim_y = False - if auto is not None: - self._autoscaleYon = bool(auto) - - if emit: - self.callbacks.process('ylim_changed', self) - # Call all of the other y-axes that are shared with this one - for other in self._shared_y_axes.get_siblings(self): - if other is not self: - other.set_ylim(self.viewLim.intervaly, - emit=False, auto=auto) - if other.figure != self.figure: - other.figure.canvas.draw_idle() - self.stale = True - return bottom, top + return self.yaxis._set_lim(bottom, top, emit=emit, auto=auto) get_yscale = _axis_method_wrapper("yaxis", "get_scale") - - def set_yscale(self, value, **kwargs): - """ - Set the y-axis scale. - - Parameters - ---------- - value : {"linear", "log", "symlog", "logit", ...} or `.ScaleBase` - The axis scale type to apply. - - **kwargs - Different keyword arguments are accepted, depending on the scale. - See the respective class keyword arguments: - - - `matplotlib.scale.LinearScale` - - `matplotlib.scale.LogScale` - - `matplotlib.scale.SymmetricalLogScale` - - `matplotlib.scale.LogitScale` - - `matplotlib.scale.FuncScale` - - Notes - ----- - By default, Matplotlib supports the above mentioned scales. - Additionally, custom scales may be registered using - `matplotlib.scale.register_scale`. These scales can then also - be used here. - """ - old_default_lims = (self.yaxis.get_major_locator() - .nonsingular(-np.inf, np.inf)) - g = self.get_shared_y_axes() - for ax in g.get_siblings(self): - ax.yaxis._set_scale(value, **kwargs) - ax._update_transScale() - ax.stale = True - new_default_lims = (self.yaxis.get_major_locator() - .nonsingular(-np.inf, np.inf)) - if old_default_lims != new_default_lims: - # Force autoscaling now, to take advantage of the scale locator's - # nonsingular() before it possibly gets swapped out by the user. - self.autoscale_view(scalex=False) - + set_yscale = _axis_method_wrapper("yaxis", "_set_axes_scale") get_yticks = _axis_method_wrapper("yaxis", "get_ticklocs") set_yticks = _axis_method_wrapper("yaxis", "set_ticks") get_ymajorticklabels = _axis_method_wrapper("yaxis", "get_majorticklabels") @@ -3983,19 +3960,14 @@ def format_ydata(self, y): def format_coord(self, x, y): """Return a format string formatting the *x*, *y* coordinates.""" - if x is None: - xs = '???' - else: - xs = self.format_xdata(x) - if y is None: - ys = '???' - else: - ys = self.format_ydata(y) - return 'x=%s y=%s' % (xs, ys) + return "x={} y={}".format( + "???" if x is None else self.format_xdata(x), + "???" if y is None else self.format_ydata(y), + ) def minorticks_on(self): """ - Display minor ticks on the axes. + Display minor ticks on the Axes. Displaying minor ticks may reduce performance; you may turn them off using `minorticks_off()` if drawing speed is a problem. @@ -4013,7 +3985,7 @@ def minorticks_on(self): ax.set_minor_locator(mticker.AutoMinorLocator()) def minorticks_off(self): - """Remove minor ticks from the axes.""" + """Remove minor ticks from the Axes.""" self.xaxis.set_minor_locator(mticker.NullLocator()) self.yaxis.set_minor_locator(mticker.NullLocator()) @@ -4021,25 +3993,25 @@ def minorticks_off(self): def can_zoom(self): """ - Return whether this axes supports the zoom box button functionality. + Return whether this Axes supports the zoom box button functionality. """ return True def can_pan(self): """ - Return whether this axes supports any pan/zoom button functionality. + Return whether this Axes supports any pan/zoom button functionality. """ return True def get_navigate(self): """ - Get whether the axes responds to navigation commands + Get whether the Axes responds to navigation commands. """ return self._navigate def set_navigate(self, b): """ - Set whether the axes responds to navigation toolbar commands + Set whether the Axes responds to navigation toolbar commands. Parameters ---------- @@ -4049,16 +4021,16 @@ def set_navigate(self, b): def get_navigate_mode(self): """ - Get the navigation toolbar button status: 'PAN', 'ZOOM', or None + Get the navigation toolbar button status: 'PAN', 'ZOOM', or None. """ return self._navigate_mode def set_navigate_mode(self, b): """ - Set the navigation toolbar button status; + Set the navigation toolbar button status. - .. warning :: - this is not a user-API function. + .. warning:: + This is not a user-API function. """ self._navigate_mode = b @@ -4097,62 +4069,27 @@ def _set_view(self, view): self.set_xlim((xmin, xmax)) self.set_ylim((ymin, ymax)) - def _set_view_from_bbox(self, bbox, direction='in', - mode=None, twinx=False, twiny=False): + def _prepare_view_from_bbox(self, bbox, direction='in', + mode=None, twinx=False, twiny=False): """ - Update view from a selection bbox. - - .. note:: + Helper function to prepare the new bounds from a bbox. - Intended to be overridden by new projection types, but if not, the - default implementation sets the view limits to the bbox directly. - - Parameters - ---------- - bbox : 4-tuple or 3 tuple - * If bbox is a 4 tuple, it is the selected bounding box limits, - in *display* coordinates. - * If bbox is a 3 tuple, it is an (xp, yp, scl) triple, where - (xp, yp) is the center of zooming and scl the scale factor to - zoom by. - - direction : str - The direction to apply the bounding box. - * `'in'` - The bounding box describes the view directly, i.e., - it zooms in. - * `'out'` - The bounding box describes the size to make the - existing view, i.e., it zooms out. - - mode : str or None - The selection mode, whether to apply the bounding box in only the - `'x'` direction, `'y'` direction or both (`None`). - - twinx : bool - Whether this axis is twinned in the *x*-direction. - - twiny : bool - Whether this axis is twinned in the *y*-direction. + This helper function returns the new x and y bounds from the zoom + bbox. This a convenience method to abstract the bbox logic + out of the base setter. """ if len(bbox) == 3: - Xmin, Xmax = self.get_xlim() - Ymin, Ymax = self.get_ylim() - xp, yp, scl = bbox # Zooming code - if scl == 0: # Should not happen scl = 1. - if scl > 1: direction = 'in' else: direction = 'out' scl = 1/scl - # get the limits of the axes - tranD2C = self.transData.transform - xmin, ymin = tranD2C((Xmin, Ymin)) - xmax, ymax = tranD2C((Xmax, Ymax)) - + (xmin, ymin), (xmax, ymax) = self.transData.transform( + np.transpose([self.get_xlim(), self.get_ylim()])) # set the range xwidth = xmax - xmin ywidth = ymax - ymin @@ -4160,7 +4097,6 @@ def _set_view_from_bbox(self, bbox, direction='in', ycen = (ymax + ymin)*.5 xzc = (xp*(scl - 1) + xcen)/scl yzc = (yp*(scl - 1) + ycen)/scl - bbox = [xzc - xwidth/2./scl, yzc - ywidth/2./scl, xzc + xwidth/2./scl, yzc + ywidth/2./scl] elif len(bbox) != 4: @@ -4197,7 +4133,7 @@ def _set_view_from_bbox(self, bbox, direction='in', [xmin0, xmax0, xmin, xmax]) # To screen space. factor = (sxmax0 - sxmin0) / (sxmax - sxmin) # Unzoom factor. # Move original bounds away by - # (factor) x (distance between unzoom box and axes bbox). + # (factor) x (distance between unzoom box and Axes bbox). sxmin1 = sxmin0 - factor * (sxmin - sxmin0) sxmax1 = sxmax0 + factor * (sxmax0 - sxmax) # And back to data space. @@ -4211,10 +4147,52 @@ def _set_view_from_bbox(self, bbox, direction='in', symax1 = symax0 + factor * (symax0 - symax) new_ybound = y_trf.inverted().transform([symin1, symax1]) + return new_xbound, new_ybound + + def _set_view_from_bbox(self, bbox, direction='in', + mode=None, twinx=False, twiny=False): + """ + Update view from a selection bbox. + + .. note:: + + Intended to be overridden by new projection types, but if not, the + default implementation sets the view limits to the bbox directly. + + Parameters + ---------- + bbox : 4-tuple or 3 tuple + * If bbox is a 4 tuple, it is the selected bounding box limits, + in *display* coordinates. + * If bbox is a 3 tuple, it is an (xp, yp, scl) triple, where + (xp, yp) is the center of zooming and scl the scale factor to + zoom by. + + direction : str + The direction to apply the bounding box. + * `'in'` - The bounding box describes the view directly, i.e., + it zooms in. + * `'out'` - The bounding box describes the size to make the + existing view, i.e., it zooms out. + + mode : str or None + The selection mode, whether to apply the bounding box in only the + `'x'` direction, `'y'` direction or both (`None`). + + twinx : bool + Whether this axis is twinned in the *x*-direction. + + twiny : bool + Whether this axis is twinned in the *y*-direction. + """ + new_xbound, new_ybound = self._prepare_view_from_bbox( + bbox, direction=direction, mode=mode, twinx=twinx, twiny=twiny) if not twinx and mode != "y": self.set_xbound(new_xbound) + self.set_autoscalex_on(False) if not twiny and mode != "x": self.set_ybound(new_ybound) + self.set_autoscaley_on(False) def start_pan(self, x, y, button): """ @@ -4249,22 +4227,13 @@ def end_pan(self): """ del self._pan_start - def drag_pan(self, button, key, x, y): + def _get_pan_points(self, button, key, x, y): """ - Called when the mouse moves during a pan operation. - - Parameters - ---------- - button : `.MouseButton` - The pressed mouse button. - key : str or None - The pressed key, if any. - x, y : float - The mouse coordinates in display coords. + Helper function to return the new points after a pan. - Notes - ----- - This is intended to be overridden by new projection types. + This helper function returns the points on the axis after a pan has + occurred. This is a convenience method to abstract the pan logic + out of the base setter. """ def format_deltas(key, dx, dy): if key == 'control': @@ -4318,22 +4287,37 @@ def format_deltas(key, dx, dy): points = result.get_points().astype(object) # Just ignore invalid limits (typically, underflow in log-scale). points[~valid] = None - self.set_xlim(points[:, 0]) - self.set_ylim(points[:, 1]) + return points + + def drag_pan(self, button, key, x, y): + """ + Called when the mouse moves during a pan operation. + + Parameters + ---------- + button : `.MouseButton` + The pressed mouse button. + key : str or None + The pressed key, if any. + x, y : float + The mouse coordinates in display coords. + + Notes + ----- + This is intended to be overridden by new projection types. + """ + points = self._get_pan_points(button, key, x, y) + if points is not None: + self.set_xlim(points[:, 0]) + self.set_ylim(points[:, 1]) def get_children(self): # docstring inherited. return [ - *self.collections, - *self.patches, - *self.lines, - *self.texts, - *self.artists, + *self._children, *self.spines.values(), - *self._get_axis_list(), + *self._axis_map.values(), self.title, self._left_title, self._right_title, - *self.tables, - *self.images, *self.child_axes, *([self.legend_] if self.legend_ is not None else []), self.patch, @@ -4348,7 +4332,7 @@ def contains(self, mouseevent): def contains_point(self, point): """ - Return whether *point* (pair of pixel coordinates) is inside the axes + Return whether *point* (pair of pixel coordinates) is inside the Axes patch. """ return self.patch.contains_point(point, radius=1.0) @@ -4364,26 +4348,31 @@ def get_default_bbox_extra_artists(self): artists = self.get_children() + for axis in self._axis_map.values(): + # axis tight bboxes are calculated separately inside + # Axes.get_tightbbox() using for_layout_only=True + artists.remove(axis) if not (self.axison and self._frameon): # don't do bbox on spines if frame not on. for spine in self.spines.values(): artists.remove(spine) - if not self.axison: - for _axis in self._get_axis_list(): - artists.remove(_axis) - artists.remove(self.title) artists.remove(self._left_title) artists.remove(self._right_title) - return [artist for artist in artists - if (artist.get_visible() and artist.get_in_layout())] + # always include types that do not internally implement clipping + # to Axes. may have clip_on set to True and clip_box equivalent + # to ax.bbox but then ignore these properties during draws. + noclip = (_AxesBase, maxis.Axis, + offsetbox.AnnotationBbox, offsetbox.OffsetBox) + return [a for a in artists if a.get_visible() and a.get_in_layout() + and (isinstance(a, noclip) or not a._fully_clipped_to_axes())] - def get_tightbbox(self, renderer, call_axes_locator=True, + def get_tightbbox(self, renderer=None, call_axes_locator=True, bbox_extra_artists=None, *, for_layout_only=False): """ - Return the tight bounding box of the axes, including axis and their + Return the tight bounding box of the Axes, including axis and their decorators (xlabel, title, etc). Artists that have ``artist.set_in_layout(False)`` are not included @@ -4397,7 +4386,7 @@ def get_tightbbox(self, renderer, call_axes_locator=True, bbox_extra_artists : list of `.Artist` or ``None`` List of artists to include in the tight bounding box. If - ``None`` (default), then all artist children of the axes are + ``None`` (default), then all artist children of the Axes are included in the tight bounding box. call_axes_locator : bool, default: True @@ -4405,7 +4394,7 @@ def get_tightbbox(self, renderer, call_axes_locator=True, ``_axes_locator`` attribute, which is necessary to get the correct bounding box. ``call_axes_locator=False`` can be used if the caller is only interested in the relative size of the tightbbox - compared to the axes bbox. + compared to the Axes bbox. for_layout_only : default: False The bounding box will *not* include the x-extent of the title and @@ -4424,36 +4413,21 @@ def get_tightbbox(self, renderer, call_axes_locator=True, """ bb = [] + if renderer is None: + renderer = self.figure._get_renderer() if not self.get_visible(): return None locator = self.get_axes_locator() - if locator and call_axes_locator: - pos = locator(self, renderer) - self.apply_aspect(pos) - else: - self.apply_aspect() - - if self.axison: - if self.xaxis.get_visible(): - try: - bb_xaxis = self.xaxis.get_tightbbox( - renderer, for_layout_only=for_layout_only) - except TypeError: - # in case downstream library has redefined axis: - bb_xaxis = self.xaxis.get_tightbbox(renderer) - if bb_xaxis: - bb.append(bb_xaxis) - if self.yaxis.get_visible(): - try: - bb_yaxis = self.yaxis.get_tightbbox( - renderer, for_layout_only=for_layout_only) - except TypeError: - # in case downstream library has redefined axis: - bb_yaxis = self.yaxis.get_tightbbox(renderer) - if bb_yaxis: - bb.append(bb_yaxis) + self.apply_aspect( + locator(self, renderer) if locator and call_axes_locator else None) + + for axis in self._axis_map.values(): + if self.axison and axis.get_visible(): + ba = martist._get_tightbbox_for_layout_only(axis, renderer) + if ba: + bb.append(ba) self._update_title_position(renderer) axbbox = self.get_window_extent(renderer) bb.append(axbbox) @@ -4474,17 +4448,6 @@ def get_tightbbox(self, renderer, call_axes_locator=True, bbox_artists = self.get_default_bbox_extra_artists() for a in bbox_artists: - # Extra check here to quickly see if clipping is on and - # contained in the axes. If it is, don't get the tightbbox for - # this artist because this can be expensive: - clip_extent = a._get_clipping_extent_bbox() - if clip_extent is not None: - clip_extent = mtransforms.Bbox.intersection( - clip_extent, axbbox) - if np.all(clip_extent.extents == axbbox.extents): - # clip extent is inside the axes bbox so don't check - # this artist - continue bbox = a.get_tightbbox(renderer) if (bbox is not None and 0 < bbox.width < np.inf @@ -4494,7 +4457,7 @@ def get_tightbbox(self, renderer, call_axes_locator=True, [b for b in bb if b.width != 0 or b.height != 0]) def _make_twin_axes(self, *args, **kwargs): - """Make a twinx axes of self. This is used for twinx and twiny.""" + """Make a twinx Axes of self. This is used for twinx and twiny.""" # Typically, SubplotBase._make_twin_axes is called instead of this. if 'sharex' in kwargs and 'sharey' in kwargs: raise ValueError("Twinned Axes may share only one axis") @@ -4525,7 +4488,7 @@ def twinx(self): Notes ----- For those who are 'picking' artists while using twinx, pick - events are only called for the artists in the top-most axes. + events are only called for the artists in the top-most Axes. """ ax2 = self._make_twin_axes(sharex=self) ax2.yaxis.tick_right() @@ -4555,7 +4518,7 @@ def twiny(self): Notes ----- For those who are 'picking' artists while using twiny, pick - events are only called for the artists in the top-most axes. + events are only called for the artists in the top-most Axes. """ ax2 = self._make_twin_axes(sharey=self) ax2.xaxis.tick_top() @@ -4567,9 +4530,9 @@ def twiny(self): return ax2 def get_shared_x_axes(self): - """Return a reference to the shared axes Grouper object for x axes.""" - return self._shared_x_axes + """Return an immutable view on the shared x-axes Grouper.""" + return cbook.GrouperView(self._shared_axes["x"]) def get_shared_y_axes(self): - """Return a reference to the shared axes Grouper object for y axes.""" - return self._shared_y_axes + """Return an immutable view on the shared y-axes Grouper.""" + return cbook.GrouperView(self._shared_axes["y"]) diff --git a/lib/matplotlib/axes/_secondary_axes.py b/lib/matplotlib/axes/_secondary_axes.py index 98a612013a16..55aeafa391b3 100644 --- a/lib/matplotlib/axes/_secondary_axes.py +++ b/lib/matplotlib/axes/_secondary_axes.py @@ -1,9 +1,9 @@ import numpy as np -from matplotlib import _api -import matplotlib.docstring as docstring +from matplotlib import _api, _docstring import matplotlib.ticker as mticker from matplotlib.axes._base import _AxesBase, _TransformedBoundsLocator +from matplotlib.axis import Axis class SecondaryAxis(_AxesBase): @@ -123,18 +123,9 @@ def apply_aspect(self, position=None): self._set_lims() super().apply_aspect(position) - def set_ticks(self, ticks, *, minor=False): - """ - Set the x ticks with list of *ticks* - - Parameters - ---------- - ticks : list - List of x-axis tick locations. - minor : bool, default: False - If ``False`` sets major ticks, if ``True`` sets minor ticks. - """ - ret = self._axis.set_ticks(ticks, minor=minor) + @_docstring.copy(Axis.set_ticks) + def set_ticks(self, ticks, labels=None, *, minor=False, **kwargs): + ret = self._axis.set_ticks(ticks, labels, minor=minor, **kwargs) self.stale = True self._ticks_set = True return ret @@ -284,7 +275,8 @@ def set_color(self, color): If a 2-tuple of functions, the user specifies the transform function and its inverse. i.e. ``functions=(lambda x: 2 / x, lambda x: 2 / x)`` would be an - reciprocal transform with a factor of 2. + reciprocal transform with a factor of 2. Both functions must accept + numpy arrays as input. The user can also directly supply a subclass of `.transforms.Transform` so long as it has an inverse. @@ -301,4 +293,4 @@ def set_color(self, color): **kwargs : `~matplotlib.axes.Axes` properties. Other miscellaneous axes parameters. ''' -docstring.interpd.update(_secax_docstring=_secax_docstring) +_docstring.interpd.update(_secax_docstring=_secax_docstring) diff --git a/lib/matplotlib/axes/_subplots.py b/lib/matplotlib/axes/_subplots.py index 5042e3bbd860..b31388a2154f 100644 --- a/lib/matplotlib/axes/_subplots.py +++ b/lib/matplotlib/axes/_subplots.py @@ -1,9 +1,7 @@ -import functools - -from matplotlib import _api, docstring -import matplotlib.artist as martist +import matplotlib as mpl +from matplotlib import cbook from matplotlib.axes._axes import Axes -from matplotlib.gridspec import GridSpec, SubplotSpec +from matplotlib.gridspec import SubplotSpec class SubplotBase: @@ -37,31 +35,6 @@ def __init__(self, fig, *args, **kwargs): # This will also update the axes position. self.set_subplotspec(SubplotSpec._from_subplot_args(fig, args)) - def __reduce__(self): - # get the first axes class which does not inherit from a subplotbase - axes_class = next( - c for c in type(self).__mro__ - if issubclass(c, Axes) and not issubclass(c, SubplotBase)) - return (_picklable_subplot_class_constructor, - (axes_class,), - self.__getstate__()) - - @_api.deprecated( - "3.4", alternative="get_subplotspec", - addendum="(get_subplotspec returns a SubplotSpec instance.)") - def get_geometry(self): - """Get the subplot geometry, e.g., (2, 2, 3).""" - rows, cols, num1, num2 = self.get_subplotspec().get_geometry() - return rows, cols, num1 + 1 # for compatibility - - @_api.deprecated("3.4", alternative="set_subplotspec") - def change_geometry(self, numrows, numcols, num): - """Change subplot geometry, e.g., from (1, 1, 1) to (2, 2, 3).""" - self._subplotspec = GridSpec(numrows, numcols, - figure=self.figure)[num - 1] - self.update_params() - self.set_position(self.figbox) - def get_subplotspec(self): """Return the `.SubplotSpec` instance associated with the subplot.""" return self._subplotspec @@ -75,64 +48,54 @@ def get_gridspec(self): """Return the `.GridSpec` instance associated with the subplot.""" return self._subplotspec.get_gridspec() - @_api.deprecated( - "3.4", alternative="get_subplotspec().get_position(self.figure)") - @property - def figbox(self): - return self.get_subplotspec().get_position(self.figure) - - @_api.deprecated("3.4", alternative="get_gridspec().nrows") - @property - def numRows(self): - return self.get_gridspec().nrows - - @_api.deprecated("3.4", alternative="get_gridspec().ncols") - @property - def numCols(self): - return self.get_gridspec().ncols - - @_api.deprecated("3.4") - def update_params(self): - """Update the subplot position from ``self.figure.subplotpars``.""" - # Now a no-op, as figbox/numRows/numCols are (deprecated) auto-updating - # properties. - - @_api.deprecated("3.4", alternative="ax.get_subplotspec().is_first_row()") - def is_first_row(self): - return self.get_subplotspec().rowspan.start == 0 - - @_api.deprecated("3.4", alternative="ax.get_subplotspec().is_last_row()") - def is_last_row(self): - return self.get_subplotspec().rowspan.stop == self.get_gridspec().nrows - - @_api.deprecated("3.4", alternative="ax.get_subplotspec().is_first_col()") - def is_first_col(self): - return self.get_subplotspec().colspan.start == 0 - - @_api.deprecated("3.4", alternative="ax.get_subplotspec().is_last_col()") - def is_last_col(self): - return self.get_subplotspec().colspan.stop == self.get_gridspec().ncols - def label_outer(self): """ Only show "outer" labels and tick labels. - x-labels are only kept for subplots on the last row; y-labels only for - subplots on the first column. + x-labels are only kept for subplots on the last row (or first row, if + labels are on the top side); y-labels only for subplots on the first + column (or last column, if labels are on the right side). """ + self._label_outer_xaxis(check_patch=False) + self._label_outer_yaxis(check_patch=False) + + def _label_outer_xaxis(self, *, check_patch): + # see documentation in label_outer. + if check_patch and not isinstance(self.patch, mpl.patches.Rectangle): + return ss = self.get_subplotspec() - lastrow = ss.is_last_row() - firstcol = ss.is_first_col() - if not lastrow: - for label in self.get_xticklabels(which="both"): - label.set_visible(False) - self.xaxis.get_offset_text().set_visible(False) - self.set_xlabel("") - if not firstcol: - for label in self.get_yticklabels(which="both"): - label.set_visible(False) - self.yaxis.get_offset_text().set_visible(False) - self.set_ylabel("") + label_position = self.xaxis.get_label_position() + if not ss.is_first_row(): # Remove top label/ticklabels/offsettext. + if label_position == "top": + self.set_xlabel("") + self.xaxis.set_tick_params(which="both", labeltop=False) + if self.xaxis.offsetText.get_position()[1] == 1: + self.xaxis.offsetText.set_visible(False) + if not ss.is_last_row(): # Remove bottom label/ticklabels/offsettext. + if label_position == "bottom": + self.set_xlabel("") + self.xaxis.set_tick_params(which="both", labelbottom=False) + if self.xaxis.offsetText.get_position()[1] == 0: + self.xaxis.offsetText.set_visible(False) + + def _label_outer_yaxis(self, *, check_patch): + # see documentation in label_outer. + if check_patch and not isinstance(self.patch, mpl.patches.Rectangle): + return + ss = self.get_subplotspec() + label_position = self.yaxis.get_label_position() + if not ss.is_first_col(): # Remove left label/ticklabels/offsettext. + if label_position == "left": + self.set_ylabel("") + self.yaxis.set_tick_params(which="both", labelleft=False) + if self.yaxis.offsetText.get_position()[0] == 0: + self.yaxis.offsetText.set_visible(False) + if not ss.is_last_col(): # Remove right label/ticklabels/offsettext. + if label_position == "right": + self.set_ylabel("") + self.yaxis.set_tick_params(which="both", labelright=False) + if self.yaxis.offsetText.get_position()[0] == 1: + self.yaxis.offsetText.set_visible(False) def _make_twin_axes(self, *args, **kwargs): """Make a twinx axes of self. This is used for twinx and twiny.""" @@ -148,59 +111,6 @@ def _make_twin_axes(self, *args, **kwargs): return twin -# this here to support cartopy which was using a private part of the -# API to register their Axes subclasses. - -# In 3.1 this should be changed to a dict subclass that warns on use -# In 3.3 to a dict subclass that raises a useful exception on use -# In 3.4 should be removed - -# The slow timeline is to give cartopy enough time to get several -# release out before we break them. -_subplot_classes = {} - - -@functools.lru_cache(None) -def subplot_class_factory(axes_class=None): - """ - Make a new class that inherits from `.SubplotBase` and the - given axes_class (which is assumed to be a subclass of `.axes.Axes`). - This is perhaps a little bit roundabout to make a new class on - the fly like this, but it means that a new Subplot class does - not have to be created for every type of Axes. - """ - if axes_class is None: - _api.warn_deprecated( - "3.3", message="Support for passing None to subplot_class_factory " - "is deprecated since %(since)s; explicitly pass the default Axes " - "class instead. This will become an error %(removal)s.") - axes_class = Axes - try: - # Avoid creating two different instances of GeoAxesSubplot... - # Only a temporary backcompat fix. This should be removed in - # 3.4 - return next(cls for cls in SubplotBase.__subclasses__() - if cls.__bases__ == (SubplotBase, axes_class)) - except StopIteration: - return type("%sSubplot" % axes_class.__name__, - (SubplotBase, axes_class), - {'_axes_class': axes_class}) - - +subplot_class_factory = cbook._make_class_factory( + SubplotBase, "{}Subplot", "_axes_class") Subplot = subplot_class_factory(Axes) # Provided for backward compatibility. - - -def _picklable_subplot_class_constructor(axes_class): - """ - Stub factory that returns an empty instance of the appropriate subplot - class when called with an axes class. This is purely to allow pickling of - Axes and Subplots. - """ - subplot_class = subplot_class_factory(axes_class) - return subplot_class.__new__(subplot_class) - - -docstring.interpd.update(Axes_kwdoc=martist.kwdoc(Axes)) -docstring.dedent_interpd(Axes.__init__) - -docstring.interpd.update(Subplot_kwdoc=martist.kwdoc(Axes)) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index c28d185057b8..66279bf1bc37 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -5,13 +5,14 @@ import datetime import functools import logging +from numbers import Number import numpy as np import matplotlib as mpl -from matplotlib import _api +from matplotlib import _api, cbook import matplotlib.artist as martist -import matplotlib.cbook as cbook +import matplotlib.colors as mcolors import matplotlib.lines as mlines import matplotlib.scale as mscale import matplotlib.text as mtext @@ -54,30 +55,29 @@ class Tick(martist.Artist): The right/top tick label. """ - @_api.delete_parameter("3.3", "label") - def __init__(self, axes, loc, label=None, - size=None, # points - width=None, - color=None, - tickdir=None, - pad=None, - labelsize=None, - labelcolor=None, - zorder=None, - gridOn=None, # defaults to axes.grid depending on - # axes.grid.which - tick1On=True, - tick2On=True, - label1On=True, - label2On=False, - major=True, - labelrotation=0, - grid_color=None, - grid_linestyle=None, - grid_linewidth=None, - grid_alpha=None, - **kw # Other Line2D kwargs applied to gridlines. - ): + def __init__( + self, axes, loc, *, + size=None, # points + width=None, + color=None, + tickdir=None, + pad=None, + labelsize=None, + labelcolor=None, + zorder=None, + gridOn=None, # defaults to axes.grid depending on axes.grid.which + tick1On=True, + tick2On=True, + label1On=True, + label2On=False, + major=True, + labelrotation=0, + grid_color=None, + grid_linestyle=None, + grid_linewidth=None, + grid_alpha=None, + **kwargs, # Other Line2D kwargs applied to gridlines. + ): """ bbox is the Bound2D bounding box in display coords of the Axes loc is the tick location in data coords @@ -144,11 +144,14 @@ def __init__(self, axes, loc, label=None, grid_linestyle = mpl.rcParams["grid.linestyle"] if grid_linewidth is None: grid_linewidth = mpl.rcParams["grid.linewidth"] - if grid_alpha is None: + if grid_alpha is None and not mcolors._has_alpha_channel(grid_color): + # alpha precedence: kwarg > color alpha > rcParams['grid.alpha'] + # Note: only resolve to rcParams if the color does not have alpha + # otherwise `grid(color=(1, 1, 1, 0.5))` would work like + # grid(color=(1, 1, 1, 0.5), alpha=rcParams['grid.alpha']) + # so the that the rcParams default would override color alpha. grid_alpha = mpl.rcParams["grid.alpha"] - grid_kw = {k[5:]: v for k, v in kw.items()} - - self.apply_tickdir(tickdir) + grid_kw = {k[5:]: v for k, v in kwargs.items()} self.tick1line = mlines.Line2D( [], [], @@ -170,22 +173,15 @@ def __init__(self, axes, loc, label=None, GRIDLINE_INTERPOLATION_STEPS self.label1 = mtext.Text( np.nan, np.nan, - fontsize=labelsize, color=labelcolor, visible=label1On) + fontsize=labelsize, color=labelcolor, visible=label1On, + rotation=self._labelrotation[1]) self.label2 = mtext.Text( np.nan, np.nan, - fontsize=labelsize, color=labelcolor, visible=label2On) - for meth, attr in [("_get_tick1line", "tick1line"), - ("_get_tick2line", "tick2line"), - ("_get_gridline", "gridline"), - ("_get_text1", "label1"), - ("_get_text2", "label2")]: - overridden_method = _api.deprecate_method_override( - getattr(__class__, meth), self, since="3.3", message="Relying " - f"on {meth} to initialize Tick.{attr} is deprecated since " - f"%(since)s and will not work %(removal)s; please directly " - f"set the attribute in the subclass' __init__ instead.") - if overridden_method: - setattr(self, attr, overridden_method()) + fontsize=labelsize, color=labelcolor, visible=label2On, + rotation=self._labelrotation[1]) + + self._apply_tickdir(tickdir) + for artist in [self.tick1line, self.tick2line, self.gridline, self.label1, self.label2]: self._set_artist_props(artist) @@ -193,7 +189,7 @@ def __init__(self, axes, loc, label=None, self.update_position(loc) @property - @_api.deprecated("3.1", alternative="Tick.label1", pending=True) + @_api.deprecated("3.1", alternative="Tick.label1", removal="3.8") def label(self): return self.label1 @@ -209,21 +205,28 @@ def _set_labelrotation(self, labelrotation): _api.check_in_list(['auto', 'default'], labelrotation=mode) self._labelrotation = (mode, angle) - def apply_tickdir(self, tickdir): + def _apply_tickdir(self, tickdir): """Set tick direction. Valid values are 'out', 'in', 'inout'.""" + # This method is responsible for updating `_pad`, and, in subclasses, + # for setting the tick{1,2}line markers as well. From the user + # perspective this should always be called though _apply_params, which + # further updates ticklabel positions using the new pads. if tickdir is None: tickdir = mpl.rcParams[f'{self.__name__}.direction'] _api.check_in_list(['in', 'out', 'inout'], tickdir=tickdir) self._tickdir = tickdir self._pad = self._base_pad + self.get_tick_padding() + + @_api.deprecated("3.5", alternative="`.Axis.set_tick_params`") + def apply_tickdir(self, tickdir): + self._apply_tickdir(tickdir) self.stale = True - # Subclass overrides should compute _tickmarkers as appropriate here. def get_tickdir(self): return self._tickdir def get_tick_padding(self): - """Get the length of the tick outside of the axes.""" + """Get the length of the tick outside of the Axes.""" padding = { 'in': 0.0, 'inout': 0.5, @@ -242,6 +245,7 @@ def set_clip_path(self, clippath, transform=None): self.gridline.set_clip_path(clippath, transform) self.stale = True + @_api.deprecated("3.6") def get_pad_pixels(self): return self.figure.dpi * self._base_pad / 72 @@ -349,52 +353,50 @@ def get_view_interval(self): """ raise NotImplementedError('Derived must override') - def _apply_params(self, **kw): + def _apply_params(self, **kwargs): for name, target in [("gridOn", self.gridline), ("tick1On", self.tick1line), ("tick2On", self.tick2line), ("label1On", self.label1), ("label2On", self.label2)]: - if name in kw: - target.set_visible(kw.pop(name)) - if any(k in kw for k in ['size', 'width', 'pad', 'tickdir']): - self._size = kw.pop('size', self._size) + if name in kwargs: + target.set_visible(kwargs.pop(name)) + if any(k in kwargs for k in ['size', 'width', 'pad', 'tickdir']): + self._size = kwargs.pop('size', self._size) # Width could be handled outside this block, but it is # convenient to leave it here. - self._width = kw.pop('width', self._width) - self._base_pad = kw.pop('pad', self._base_pad) - # apply_tickdir uses _size and _base_pad to make _pad, - # and also makes _tickmarkers. - self.apply_tickdir(kw.pop('tickdir', self._tickdir)) - self.tick1line.set_marker(self._tickmarkers[0]) - self.tick2line.set_marker(self._tickmarkers[1]) + self._width = kwargs.pop('width', self._width) + self._base_pad = kwargs.pop('pad', self._base_pad) + # _apply_tickdir uses _size and _base_pad to make _pad, and also + # sets the ticklines markers. + self._apply_tickdir(kwargs.pop('tickdir', self._tickdir)) for line in (self.tick1line, self.tick2line): line.set_markersize(self._size) line.set_markeredgewidth(self._width) - # _get_text1_transform uses _pad from apply_tickdir. + # _get_text1_transform uses _pad from _apply_tickdir. trans = self._get_text1_transform()[0] self.label1.set_transform(trans) trans = self._get_text2_transform()[0] self.label2.set_transform(trans) - tick_kw = {k: v for k, v in kw.items() if k in ['color', 'zorder']} - if 'color' in kw: - tick_kw['markeredgecolor'] = kw['color'] + tick_kw = {k: v for k, v in kwargs.items() if k in ['color', 'zorder']} + if 'color' in kwargs: + tick_kw['markeredgecolor'] = kwargs['color'] self.tick1line.set(**tick_kw) self.tick2line.set(**tick_kw) for k, v in tick_kw.items(): setattr(self, '_' + k, v) - if 'labelrotation' in kw: - self._set_labelrotation(kw.pop('labelrotation')) + if 'labelrotation' in kwargs: + self._set_labelrotation(kwargs.pop('labelrotation')) self.label1.set(rotation=self._labelrotation[1]) self.label2.set(rotation=self._labelrotation[1]) - label_kw = {k[5:]: v for k, v in kw.items() + label_kw = {k[5:]: v for k, v in kwargs.items() if k in ['labelsize', 'labelcolor']} self.label1.set(**label_kw) self.label2.set(**label_kw) - grid_kw = {k[5:]: v for k, v in kw.items() + grid_kw = {k[5:]: v for k, v in kwargs.items() if k in _gridline_param_names} self.gridline.set(**grid_kw) @@ -419,20 +421,13 @@ class XTick(Tick): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # x in data coords, y in axes coords + ax = self.axes self.tick1line.set( - xdata=[0], ydata=[0], - transform=self.axes.get_xaxis_transform(which="tick1"), - marker=self._tickmarkers[0], - ) + data=([0], [0]), transform=ax.get_xaxis_transform("tick1")) self.tick2line.set( - xdata=[0], ydata=[1], - transform=self.axes.get_xaxis_transform(which="tick2"), - marker=self._tickmarkers[1], - ) + data=([0], [1]), transform=ax.get_xaxis_transform("tick2")) self.gridline.set( - xdata=[0, 0], ydata=[0, 1], - transform=self.axes.get_xaxis_transform(which="grid"), - ) + data=([0, 0], [0, 1]), transform=ax.get_xaxis_transform("grid")) # the y loc is 3 points below the min of y axis trans, va, ha = self._get_text1_transform() self.label1.set( @@ -451,15 +446,16 @@ def _get_text1_transform(self): def _get_text2_transform(self): return self.axes.get_xaxis_text2_transform(self._pad) - def apply_tickdir(self, tickdir): + def _apply_tickdir(self, tickdir): # docstring inherited - super().apply_tickdir(tickdir) - self._tickmarkers = { + super()._apply_tickdir(tickdir) + mark1, mark2 = { 'out': (mlines.TICKDOWN, mlines.TICKUP), 'in': (mlines.TICKUP, mlines.TICKDOWN), 'inout': ('|', '|'), }[self._tickdir] - self.stale = True + self.tick1line.set_marker(mark1) + self.tick2line.set_marker(mark2) def update_position(self, loc): """Set the location of tick in data coords with scalar *loc*.""" @@ -486,20 +482,13 @@ class YTick(Tick): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # x in axes coords, y in data coords + ax = self.axes self.tick1line.set( - xdata=[0], ydata=[0], - transform=self.axes.get_yaxis_transform(which="tick1"), - marker=self._tickmarkers[0], - ) + data=([0], [0]), transform=ax.get_yaxis_transform("tick1")) self.tick2line.set( - xdata=[1], ydata=[0], - transform=self.axes.get_yaxis_transform(which="tick2"), - marker=self._tickmarkers[1], - ) + data=([1], [0]), transform=ax.get_yaxis_transform("tick2")) self.gridline.set( - xdata=[0, 1], ydata=[0, 0], - transform=self.axes.get_yaxis_transform(which="grid"), - ) + data=([0, 1], [0, 0]), transform=ax.get_yaxis_transform("grid")) # the y loc is 3 points below the min of y axis trans, va, ha = self._get_text1_transform() self.label1.set( @@ -518,15 +507,16 @@ def _get_text1_transform(self): def _get_text2_transform(self): return self.axes.get_yaxis_text2_transform(self._pad) - def apply_tickdir(self, tickdir): + def _apply_tickdir(self, tickdir): # docstring inherited - super().apply_tickdir(tickdir) - self._tickmarkers = { + super()._apply_tickdir(tickdir) + mark1, mark2 = { 'out': (mlines.TICKLEFT, mlines.TICKRIGHT), 'in': (mlines.TICKRIGHT, mlines.TICKLEFT), 'inout': ('_', '_'), }[self._tickdir] - self.stale = True + self.tick1line.set_marker(mark1) + self.tick2line.set_marker(mark2) def update_position(self, loc): """Set the location of tick in data coords with scalar *loc*.""" @@ -558,6 +548,8 @@ class Ticker: def __init__(self): self._locator = None self._formatter = None + self._locator_is_default = True + self._formatter_is_default = True @property def locator(self): @@ -645,11 +637,15 @@ class Axis(martist.Artist): The minor ticks. """ OFFSETTEXTPAD = 3 + # The class used in _get_tick() to create tick instances. Must either be + # overwritten in subclasses, or subclasses must reimplement _get_tick(). + _tick_class = None def __str__(self): return "{}({},{})".format( type(self).__name__, *self.axes.transAxes.transform((0, 0))) + @_api.make_keyword_only("3.6", name="pickradius") def __init__(self, axes, pickradius=15): """ Parameters @@ -670,7 +666,8 @@ def __init__(self, axes, pickradius=15): self.axes = axes self.major = Ticker() self.minor = Ticker() - self.callbacks = cbook.CallbackRegistry() + self.callbacks = cbook.CallbackRegistry( + signals=["units", "units finalize"]) self._autolabelpos = True @@ -693,7 +690,39 @@ def __init__(self, axes, pickradius=15): self._minor_tick_kw = dict() self.clear() - self._set_scale('linear') + self._autoscale_on = True + + @property + def isDefault_majloc(self): + return self.major._locator_is_default + + @isDefault_majloc.setter + def isDefault_majloc(self, value): + self.major._locator_is_default = value + + @property + def isDefault_majfmt(self): + return self.major._formatter_is_default + + @isDefault_majfmt.setter + def isDefault_majfmt(self, value): + self.major._formatter_is_default = value + + @property + def isDefault_minloc(self): + return self.minor._locator_is_default + + @isDefault_minloc.setter + def isDefault_minloc(self, value): + self.minor._locator_is_default = value + + @property + def isDefault_minfmt(self): + return self.minor._formatter_is_default + + @isDefault_minfmt.setter + def isDefault_minfmt(self, value): + self.minor._formatter_is_default = value # During initialization, Axis objects often create ticks that are later # unused; this turns out to be a very slow step. Instead, use a custom @@ -751,13 +780,84 @@ def _set_scale(self, value, **kwargs): self.isDefault_majfmt = True self.isDefault_minfmt = True + # This method is directly wrapped by Axes.set_{x,y}scale. + def _set_axes_scale(self, value, **kwargs): + """ + Set this Axis' scale. + + Parameters + ---------- + value : {"linear", "log", "symlog", "logit", ...} or `.ScaleBase` + The axis scale type to apply. + + **kwargs + Different keyword arguments are accepted, depending on the scale. + See the respective class keyword arguments: + + - `matplotlib.scale.LinearScale` + - `matplotlib.scale.LogScale` + - `matplotlib.scale.SymmetricalLogScale` + - `matplotlib.scale.LogitScale` + - `matplotlib.scale.FuncScale` + + Notes + ----- + By default, Matplotlib supports the above mentioned scales. + Additionally, custom scales may be registered using + `matplotlib.scale.register_scale`. These scales can then also + be used here. + """ + name, = [name for name, axis in self.axes._axis_map.items() + if axis is self] # The axis name. + old_default_lims = (self.get_major_locator() + .nonsingular(-np.inf, np.inf)) + g = self.axes._shared_axes[name] + for ax in g.get_siblings(self.axes): + ax._axis_map[name]._set_scale(value, **kwargs) + ax._update_transScale() + ax.stale = True + new_default_lims = (self.get_major_locator() + .nonsingular(-np.inf, np.inf)) + if old_default_lims != new_default_lims: + # Force autoscaling now, to take advantage of the scale locator's + # nonsingular() before it possibly gets swapped out by the user. + self.axes.autoscale_view( + **{f"scale{k}": k == name for k in self.axes._axis_names}) + def limit_range_for_scale(self, vmin, vmax): return self._scale.limit_range_for_scale(vmin, vmax, self.get_minpos()) + def _get_autoscale_on(self): + """Return whether this Axis is autoscaled.""" + return self._autoscale_on + + def _set_autoscale_on(self, b): + """ + Set whether this Axis is autoscaled when drawing or by + `.Axes.autoscale_view`. + + Parameters + ---------- + b : bool + """ + self._autoscale_on = b + def get_children(self): return [self.label, self.offsetText, *self.get_major_ticks(), *self.get_minor_ticks()] + def _reset_major_tick_kw(self): + self._major_tick_kw.clear() + self._major_tick_kw['gridOn'] = ( + mpl.rcParams['axes.grid'] and + mpl.rcParams['axes.grid.which'] in ('both', 'major')) + + def _reset_minor_tick_kw(self): + self._minor_tick_kw.clear() + self._minor_tick_kw['gridOn'] = ( + mpl.rcParams['axes.grid'] and + mpl.rcParams['axes.grid.which'] in ('both', 'minor')) + def clear(self): """ Clear the axis. @@ -777,7 +877,8 @@ def clear(self): self._set_scale('linear') # Clear the callback registry for this axis, or it may "leak" - self.callbacks = cbook.CallbackRegistry() + self.callbacks = cbook.CallbackRegistry( + signals=["units", "units finalize"]) # whether the grids are on self._major_tick_kw['gridOn'] = ( @@ -786,7 +887,6 @@ def clear(self): self._minor_tick_kw['gridOn'] = ( mpl.rcParams['axes.grid'] and mpl.rcParams['axes.grid.which'] in ('both', 'minor')) - self.reset_ticks() self.converter = None @@ -794,11 +894,6 @@ def clear(self): self.set_units(None) self.stale = True - @_api.deprecated("3.4", alternative="Axis.clear()") - def cla(self): - """Clear this axis.""" - return self.clear() - def reset_ticks(self): """ Re-initialize the major and minor Tick lists. @@ -819,7 +914,7 @@ def reset_ticks(self): except AttributeError: pass - def set_tick_params(self, which='major', reset=False, **kw): + def set_tick_params(self, which='major', reset=False, **kwargs): """ Set appearance parameters for ticks, ticklabels, and gridlines. @@ -827,16 +922,16 @@ def set_tick_params(self, which='major', reset=False, **kw): :meth:`matplotlib.axes.Axes.tick_params`. """ _api.check_in_list(['major', 'minor', 'both'], which=which) - kwtrans = self._translate_tick_kw(kw) + kwtrans = self._translate_tick_params(kwargs) # the kwargs are stored in self._major/minor_tick_kw so that any # future new ticks will automatically get them if reset: if which in ['major', 'both']: - self._major_tick_kw.clear() + self._reset_major_tick_kw() self._major_tick_kw.update(kwtrans) if which in ['minor', 'both']: - self._minor_tick_kw.clear() + self._reset_minor_tick_kw() self._minor_tick_kw.update(kwtrans) self.reset_ticks() else: @@ -859,47 +954,56 @@ def set_tick_params(self, which='major', reset=False, **kw): self.stale = True @staticmethod - def _translate_tick_kw(kw): + def _translate_tick_params(kw): + """ + Translate the kwargs supported by `.Axis.set_tick_params` to kwargs + supported by `.Tick._apply_params`. + + In particular, this maps axis specific names like 'top', 'left' + to the generic tick1, tick2 logic of the axis. Additionally, there + are some other name translations. + + Returns a new dict of translated kwargs. + + Note: The input *kwargs* are currently modified, but that's ok for + the only caller. + """ # The following lists may be moved to a more accessible location. - kwkeys = ['size', 'width', 'color', 'tickdir', 'pad', - 'labelsize', 'labelcolor', 'zorder', 'gridOn', - 'tick1On', 'tick2On', 'label1On', 'label2On', - 'length', 'direction', 'left', 'bottom', 'right', 'top', - 'labelleft', 'labelbottom', 'labelright', 'labeltop', - 'labelrotation'] + _gridline_param_names - kwtrans = {} - if 'length' in kw: - kwtrans['size'] = kw.pop('length') - if 'direction' in kw: - kwtrans['tickdir'] = kw.pop('direction') - if 'rotation' in kw: - kwtrans['labelrotation'] = kw.pop('rotation') - if 'left' in kw: - kwtrans['tick1On'] = kw.pop('left') - if 'bottom' in kw: - kwtrans['tick1On'] = kw.pop('bottom') - if 'right' in kw: - kwtrans['tick2On'] = kw.pop('right') - if 'top' in kw: - kwtrans['tick2On'] = kw.pop('top') - if 'labelleft' in kw: - kwtrans['label1On'] = kw.pop('labelleft') - if 'labelbottom' in kw: - kwtrans['label1On'] = kw.pop('labelbottom') - if 'labelright' in kw: - kwtrans['label2On'] = kw.pop('labelright') - if 'labeltop' in kw: - kwtrans['label2On'] = kw.pop('labeltop') + allowed_keys = [ + 'size', 'width', 'color', 'tickdir', 'pad', + 'labelsize', 'labelcolor', 'zorder', 'gridOn', + 'tick1On', 'tick2On', 'label1On', 'label2On', + 'length', 'direction', 'left', 'bottom', 'right', 'top', + 'labelleft', 'labelbottom', 'labelright', 'labeltop', + 'labelrotation', + *_gridline_param_names] + + keymap = { + # tick_params key -> axis key + 'length': 'size', + 'direction': 'tickdir', + 'rotation': 'labelrotation', + 'left': 'tick1On', + 'bottom': 'tick1On', + 'right': 'tick2On', + 'top': 'tick2On', + 'labelleft': 'label1On', + 'labelbottom': 'label1On', + 'labelright': 'label2On', + 'labeltop': 'label2On', + } + kwtrans = {newkey: kw.pop(oldkey) + for oldkey, newkey in keymap.items() if oldkey in kw} if 'colors' in kw: c = kw.pop('colors') kwtrans['color'] = c kwtrans['labelcolor'] = c # Maybe move the checking up to the caller of this method. for key in kw: - if key not in kwkeys: + if key not in allowed_keys: raise ValueError( "keyword %s is not recognized; valid keywords are %s" - % (key, kwkeys)) + % (key, allowed_keys)) kwtrans.update(kw) return kwtrans @@ -910,7 +1014,7 @@ def set_clip_path(self, clippath, transform=None): self.stale = True def get_view_interval(self): - """Return the view limits ``(min, max)`` of this axis.""" + """Return the ``(min, max)`` view limits of this axis.""" raise NotImplementedError('Derived must override') def set_view_interval(self, vmin, vmax, ignore=False): @@ -929,7 +1033,7 @@ def set_view_interval(self, vmin, vmax, ignore=False): raise NotImplementedError('Derived must override') def get_data_interval(self): - """Return the Interval instance for this axis data limits.""" + """Return the ``(min, max)`` data limits of this axis.""" raise NotImplementedError('Derived must override') def set_data_interval(self, vmin, vmax, ignore=False): @@ -965,10 +1069,9 @@ def set_inverted(self, inverted): the top for the y-axis; the "inverse" direction is increasing to the left for the x-axis and to the bottom for the y-axis. """ - # Currently, must be implemented in subclasses using set_xlim/set_ylim - # rather than generically using set_view_interval, so that shared - # axes get updated as well. - raise NotImplementedError('Derived must override') + a, b = self.get_view_interval() + # cast to bool to avoid bad interaction between python 3.8 and np.bool_ + self._set_lim(*sorted((a, b), reverse=bool(inverted)), auto=None) def set_default_intervals(self): """ @@ -982,32 +1085,102 @@ def set_default_intervals(self): # interface provides a hook for custom types to register # default limits through the AxisInfo.default_limits # attribute, and the derived code below will check for that - # and use it if is available (else just use 0..1) + # and use it if it's available (else just use 0..1) + + def _set_lim(self, v0, v1, *, emit=True, auto): + """ + Set view limits. + + This method is a helper for the Axes ``set_xlim``, ``set_ylim``, and + ``set_zlim`` methods. + + Parameters + ---------- + v0, v1 : float + The view limits. (Passing *v0* as a (low, high) pair is not + supported; normalization must occur in the Axes setters.) + emit : bool, default: True + Whether to notify observers of limit change. + auto : bool or None, default: False + Whether to turn on autoscaling of the x-axis. True turns on, False + turns off, None leaves unchanged. + """ + name, = [name for name, axis in self.axes._axis_map.items() + if axis is self] # The axis name. + + self.axes._process_unit_info([(name, (v0, v1))], convert=False) + v0 = self.axes._validate_converted_limits(v0, self.convert_units) + v1 = self.axes._validate_converted_limits(v1, self.convert_units) + + if v0 is None or v1 is None: + # Axes init calls set_xlim(0, 1) before get_xlim() can be called, + # so only grab the limits if we really need them. + old0, old1 = self.get_view_interval() + if v0 is None: + v0 = old0 + if v1 is None: + v1 = old1 + + if self.get_scale() == 'log' and (v0 <= 0 or v1 <= 0): + # Axes init calls set_xlim(0, 1) before get_xlim() can be called, + # so only grab the limits if we really need them. + old0, old1 = self.get_view_interval() + if v0 <= 0: + _api.warn_external(f"Attempt to set non-positive {name}lim on " + f"a log-scaled axis will be ignored.") + v0 = old0 + if v1 <= 0: + _api.warn_external(f"Attempt to set non-positive {name}lim on " + f"a log-scaled axis will be ignored.") + v1 = old1 + if v0 == v1: + _api.warn_external( + f"Attempting to set identical low and high {name}lims " + f"makes transformation singular; automatically expanding.") + reverse = bool(v0 > v1) # explicit cast needed for python3.8+np.bool_. + v0, v1 = self.get_major_locator().nonsingular(v0, v1) + v0, v1 = self.limit_range_for_scale(v0, v1) + v0, v1 = sorted([v0, v1], reverse=bool(reverse)) + + self.set_view_interval(v0, v1, ignore=True) + # Mark viewlims as no longer stale without triggering an autoscale. + for ax in self.axes._shared_axes[name].get_siblings(self.axes): + ax._stale_viewlims[name] = False + if auto is not None: + self._set_autoscale_on(bool(auto)) + + if emit: + self.axes.callbacks.process(f"{name}lim_changed", self.axes) + # Call all of the other axes that are shared with this one + for other in self.axes._shared_axes[name].get_siblings(self.axes): + if other is not self.axes: + other._axis_map[name]._set_lim( + v0, v1, emit=False, auto=auto) + if other.figure != self.figure: + other.figure.canvas.draw_idle() + + self.stale = True + return v0, v1 def _set_artist_props(self, a): if a is None: return a.set_figure(self.figure) + @_api.deprecated("3.6") def get_ticklabel_extents(self, renderer): - """ - Get the extents of the tick labels on either side - of the axes. - """ - + """Get the extents of the tick labels on either side of the axes.""" ticks_to_draw = self._update_ticks() - ticklabelBoxes, ticklabelBoxes2 = self._get_tick_bboxes(ticks_to_draw, - renderer) - - if len(ticklabelBoxes): - bbox = mtransforms.Bbox.union(ticklabelBoxes) + tlb1, tlb2 = self._get_ticklabel_bboxes(ticks_to_draw, renderer) + if len(tlb1): + bbox1 = mtransforms.Bbox.union(tlb1) else: - bbox = mtransforms.Bbox.from_extents(0, 0, 0, 0) - if len(ticklabelBoxes2): - bbox2 = mtransforms.Bbox.union(ticklabelBoxes2) + bbox1 = mtransforms.Bbox.from_extents(0, 0, 0, 0) + if len(tlb2): + bbox2 = mtransforms.Bbox.union(tlb2) else: bbox2 = mtransforms.Bbox.from_extents(0, 0, 0, 0) - return bbox, bbox2 + return bbox1, bbox2 def _update_ticks(self): """ @@ -1052,14 +1225,16 @@ def _update_ticks(self): return ticks_to_draw - def _get_tick_bboxes(self, ticks, renderer): + def _get_ticklabel_bboxes(self, ticks, renderer=None): """Return lists of bboxes for ticks' label1's and label2's.""" + if renderer is None: + renderer = self.figure._get_renderer() return ([tick.label1.get_window_extent(renderer) for tick in ticks if tick.label1.get_visible()], [tick.label2.get_window_extent(renderer) for tick in ticks if tick.label2.get_visible()]) - def get_tightbbox(self, renderer, *, for_layout_only=False): + def get_tightbbox(self, renderer=None, *, for_layout_only=False): """ Return a bounding box that encloses the axis. It only accounts tick labels, axis label, and offsetText. @@ -1071,24 +1246,23 @@ def get_tightbbox(self, renderer, *, for_layout_only=False): """ if not self.get_visible(): return - + if renderer is None: + renderer = self.figure._get_renderer() ticks_to_draw = self._update_ticks() self._update_label_position(renderer) # go back to just this axis's tick labels - ticklabelBoxes, ticklabelBoxes2 = self._get_tick_bboxes( - ticks_to_draw, renderer) + tlb1, tlb2 = self._get_ticklabel_bboxes(ticks_to_draw, renderer) - self._update_offset_text_position(ticklabelBoxes, ticklabelBoxes2) + self._update_offset_text_position(tlb1, tlb2) self.offsetText.set_text(self.major.formatter.get_offset()) bboxes = [ *(a.get_window_extent(renderer) for a in [self.offsetText] if a.get_visible()), - *ticklabelBoxes, - *ticklabelBoxes2, + *tlb1, *tlb2, ] # take care of label if self.label.get_visible(): @@ -1128,22 +1302,20 @@ def draw(self, renderer, *args, **kwargs): renderer.open_group(__name__, gid=self.get_gid()) ticks_to_draw = self._update_ticks() - ticklabelBoxes, ticklabelBoxes2 = self._get_tick_bboxes(ticks_to_draw, - renderer) + tlb1, tlb2 = self._get_ticklabel_bboxes(ticks_to_draw, renderer) for tick in ticks_to_draw: tick.draw(renderer) - # scale up the axis label box to also find the neighbors, not - # just the tick labels that actually overlap note we need a - # *copy* of the axis label box because we don't want to scale - # the actual bbox + # Scale up the axis label box to also find the neighbors, not just the + # tick labels that actually overlap. We need a *copy* of the axis + # label box because we don't want to scale the actual bbox. self._update_label_position(renderer) self.label.draw(renderer) - self._update_offset_text_position(ticklabelBoxes, ticklabelBoxes2) + self._update_offset_text_position(tlb1, tlb2) self.offsetText.set_text(self.major.formatter.get_offset()) self.offsetText.draw(renderer) @@ -1166,10 +1338,11 @@ def get_offset_text(self): def get_pickradius(self): """Return the depth of the axis used by the picker.""" - return self.pickradius + return self._pickradius def get_majorticklabels(self): """Return this Axis' major tick labels, as a list of `~.text.Text`.""" + self._update_ticks() ticks = self.get_major_ticks() labels1 = [tick.label1 for tick in ticks if tick.label1.get_visible()] labels2 = [tick.label2 for tick in ticks if tick.label2.get_visible()] @@ -1177,6 +1350,7 @@ def get_majorticklabels(self): def get_minorticklabels(self): """Return this Axis' minor tick labels, as a list of `~.text.Text`.""" + self._update_ticks() ticks = self.get_minor_ticks() labels1 = [tick.label1 for tick in ticks if tick.label1.get_visible()] labels2 = [tick.label2 for tick in ticks if tick.label2.get_visible()] @@ -1199,13 +1373,6 @@ def get_ticklabels(self, minor=False, which=None): Returns ------- list of `~matplotlib.text.Text` - - Notes - ----- - The tick label strings are not populated until a ``draw`` method has - been called. - - See also: `~.pyplot.draw` and `~.FigureCanvasBase.draw`. """ if which is not None: if which == 'minor': @@ -1251,24 +1418,38 @@ def get_majorticklocs(self): def get_minorticklocs(self): """Return this Axis' minor tick locations in data coordinates.""" # Remove minor ticks duplicating major ticks. - major_locs = self.major.locator() - minor_locs = self.minor.locator() - transform = self._scale.get_transform() - tr_minor_locs = transform.transform(minor_locs) - tr_major_locs = transform.transform(major_locs) - lo, hi = sorted(transform.transform(self.get_view_interval())) - # Use the transformed view limits as scale. 1e-5 is the default rtol - # for np.isclose. - tol = (hi - lo) * 1e-5 + minor_locs = np.asarray(self.minor.locator()) if self.remove_overlapping_locs: - minor_locs = [ - loc for loc, tr_loc in zip(minor_locs, tr_minor_locs) - if ~np.isclose(tr_loc, tr_major_locs, atol=tol, rtol=0).any()] + major_locs = self.major.locator() + transform = self._scale.get_transform() + tr_minor_locs = transform.transform(minor_locs) + tr_major_locs = transform.transform(major_locs) + lo, hi = sorted(transform.transform(self.get_view_interval())) + # Use the transformed view limits as scale. 1e-5 is the default + # rtol for np.isclose. + tol = (hi - lo) * 1e-5 + mask = np.isclose(tr_minor_locs[:, None], tr_major_locs[None, :], + atol=tol, rtol=0).any(axis=1) + minor_locs = minor_locs[~mask] return minor_locs - @_api.make_keyword_only("3.3", "minor") - def get_ticklocs(self, minor=False): - """Return this Axis' tick locations in data coordinates.""" + def get_ticklocs(self, *, minor=False): + """ + Return this Axis' tick locations in data coordinates. + + The locations are not clipped to the current axis limits and hence + may contain locations that are not visible in the output. + + Parameters + ---------- + minor : bool, default: False + True to return the minor tick directions, + False to return the major tick directions. + + Returns + ------- + numpy array of tick locations + """ return self.get_minorticklocs() if minor else self.get_majorticklocs() def get_ticks_direction(self, minor=False): @@ -1294,7 +1475,12 @@ def get_ticks_direction(self, minor=False): def _get_tick(self, major): """Return the default tick instance.""" - raise NotImplementedError('derived must override') + if self._tick_class is None: + raise NotImplementedError( + f"The Axis subclass {self.__class__.__name__} must define " + "_tick_class or reimplement _get_tick()") + tick_kw = self._major_tick_kw if major else self._minor_tick_kw + return self._tick_class(self.axes, 0, major=major, **tick_kw) def _get_tick_label_size(self, axis_name): """ @@ -1364,17 +1550,18 @@ def get_minor_ticks(self, numticks=None): return self.minorTicks[:numticks] - def grid(self, b=None, which='major', **kwargs): + @_api.rename_parameter("3.5", "b", "visible") + def grid(self, visible=None, which='major', **kwargs): """ Configure the grid lines. Parameters ---------- - b : bool or None - Whether to show the grid lines. If any *kwargs* are supplied, - it is assumed you want the grid on and *b* will be set to True. + visible : bool or None + Whether to show the grid lines. If any *kwargs* are supplied, it + is assumed you want the grid on and *visible* will be set to True. - If *b* is *None* and there are no *kwargs*, this toggles the + If *visible* is *None* and there are no *kwargs*, this toggles the visibility of the lines. which : {'major', 'minor', 'both'} @@ -1385,35 +1572,24 @@ def grid(self, b=None, which='major', **kwargs): grid(color='r', linestyle='-', linewidth=2) """ - if b is not None: - if 'visible' in kwargs and bool(b) != bool(kwargs['visible']): - raise ValueError( - "'b' and 'visible' specify inconsistent grid visibilities") - if kwargs and not b: # something false-like but not None + if kwargs: + if visible is None: + visible = True + elif not visible: # something false-like but not None _api.warn_external('First parameter to grid() is false, ' 'but line properties are supplied. The ' 'grid will be enabled.') - b = True + visible = True which = which.lower() _api.check_in_list(['major', 'minor', 'both'], which=which) - gridkw = {'grid_' + item[0]: item[1] for item in kwargs.items()} - if 'grid_visible' in gridkw: - forced_visibility = True - gridkw['gridOn'] = gridkw.pop('grid_visible') - else: - forced_visibility = False - + gridkw = {f'grid_{name}': value for name, value in kwargs.items()} if which in ['minor', 'both']: - if b is None and not forced_visibility: - gridkw['gridOn'] = not self._minor_tick_kw['gridOn'] - elif b is not None: - gridkw['gridOn'] = b + gridkw['gridOn'] = (not self._minor_tick_kw['gridOn'] + if visible is None else visible) self.set_tick_params(which='minor', **gridkw) if which in ['major', 'both']: - if b is None and not forced_visibility: - gridkw['gridOn'] = not self._major_tick_kw['gridOn'] - elif b is not None: - gridkw['gridOn'] = b + gridkw['gridOn'] = (not self._major_tick_kw['gridOn'] + if visible is None else visible) self.set_tick_params(which='major', **gridkw) self.stale = True @@ -1433,7 +1609,7 @@ def update_units(self, data): if default is not None and self.units is None: self.set_units(default) - if neednew: + elif neednew: self._update_axisinfo() self.stale = True return True @@ -1506,16 +1682,13 @@ def set_units(self, u): """ if u == self.units: return - if self is self.axes.xaxis: - shared = [ - ax.xaxis - for ax in self.axes.get_shared_x_axes().get_siblings(self.axes) - ] - elif self is self.axes.yaxis: - shared = [ - ax.yaxis - for ax in self.axes.get_shared_y_axes().get_siblings(self.axes) - ] + for name, axis in self.axes._axis_map.items(): + if self is axis: + shared = [ + getattr(ax, f"{name}axis") + for ax + in self.axes._shared_axes[name].get_siblings(self.axes)] + break else: shared = [self] for axis in shared: @@ -1654,23 +1827,37 @@ def set_pickradius(self, pickradius): Parameters ---------- - pickradius : float + pickradius : float + The acceptance radius for containment tests. + See also `.Axis.contains`. """ - self.pickradius = pickradius + if not isinstance(pickradius, Number) or pickradius < 0: + raise ValueError("pick radius should be a distance") + self._pickradius = pickradius + + pickradius = property( + get_pickradius, set_pickradius, doc="The acceptance radius for " + "containment tests. See also `.Axis.contains`.") - # Helper for set_ticklabels. Defining it here makes it pickleable. + # Helper for set_ticklabels. Defining it here makes it picklable. @staticmethod def _format_with_dict(tickd, x, pos): return tickd.get(x, "") def set_ticklabels(self, ticklabels, *, minor=False, **kwargs): r""" - Set the text values of the tick labels. + [*Discouraged*] Set the text values of the tick labels. - .. warning:: - This method should only be used after fixing the tick positions - using `.Axis.set_ticks`. Otherwise, the labels may end up in - unexpected positions. + .. admonition:: Discouraged + + The use of this method is discouraged, because of the dependency + on tick positions. In most cases, you'll want to use + ``set_[x/y]ticks(positions, labels)`` instead. + + If you are using this method, you should always fix the tick + positions before, e.g. by using `.Axis.set_ticks` or by explicitly + setting a `~.ticker.FixedLocator`. Otherwise, ticks are free to + move and the labels may end up in unexpected positions. Parameters ---------- @@ -1689,8 +1876,11 @@ def set_ticklabels(self, ticklabels, *, minor=False, **kwargs): For each tick, includes ``tick.label1`` if it is visible, then ``tick.label2`` if it is visible, in that order. """ - ticklabels = [t.get_text() if hasattr(t, 'get_text') else t - for t in ticklabels] + try: + ticklabels = [t.get_text() if hasattr(t, 'get_text') else t + for t in ticklabels] + except TypeError: + raise TypeError(f"{ticklabels:=} must be a sequence") from None locator = (self.get_minor_locator() if minor else self.get_major_locator()) if isinstance(locator, mticker.FixedLocator): @@ -1723,10 +1913,10 @@ def set_ticklabels(self, ticklabels, *, minor=False, **kwargs): tick_label = formatter(loc, pos) # deal with label1 tick.label1.set_text(tick_label) - tick.label1.update(kwargs) + tick.label1._internal_update(kwargs) # deal with label2 tick.label2.set_text(tick_label) - tick.label2.update(kwargs) + tick.label2._internal_update(kwargs) # only return visible tick labels if tick.label1.get_visible(): ret.append(tick.label1) @@ -1738,8 +1928,7 @@ def set_ticklabels(self, ticklabels, *, minor=False, **kwargs): # Wrapper around set_ticklabels used to generate Axes.set_x/ytickabels; can # go away once the API of Axes.set_x/yticklabels becomes consistent. - @_api.make_keyword_only("3.3", "fontdict") - def _set_ticklabels(self, labels, fontdict=None, minor=False, **kwargs): + def _set_ticklabels(self, labels, *, fontdict=None, minor=False, **kwargs): """ Set this Axis' labels with list of string labels. @@ -1767,7 +1956,7 @@ def _set_ticklabels(self, labels, fontdict=None, minor=False, **kwargs): Returns ------- - list of `~.Text` + list of `.Text` The labels. Other Parameters @@ -1778,9 +1967,35 @@ def _set_ticklabels(self, labels, fontdict=None, minor=False, **kwargs): kwargs.update(fontdict) return self.set_ticklabels(labels, minor=minor, **kwargs) - def set_ticks(self, ticks, *, minor=False): + def _set_tick_locations(self, ticks, *, minor=False): + # see docstring of set_ticks + + # XXX if the user changes units, the information will be lost here + ticks = self.convert_units(ticks) + for name, axis in self.axes._axis_map.items(): + if self is axis: + shared = [ + getattr(ax, f"{name}axis") + for ax + in self.axes._shared_axes[name].get_siblings(self.axes)] + break + else: + shared = [self] + if len(ticks): + for axis in shared: + # set_view_interval maintains any preexisting inversion. + axis.set_view_interval(min(ticks), max(ticks)) + self.axes.stale = True + if minor: + self.set_minor_locator(mticker.FixedLocator(ticks)) + return self.get_minor_ticks(len(ticks)) + else: + self.set_major_locator(mticker.FixedLocator(ticks)) + return self.get_major_ticks(len(ticks)) + + def set_ticks(self, ticks, labels=None, *, minor=False, **kwargs): """ - Set this Axis' tick locations. + Set this Axis' tick locations and optionally labels. If necessary, the view limits of the Axis are expanded so that all given ticks are visible. @@ -1788,9 +2003,22 @@ def set_ticks(self, ticks, *, minor=False): Parameters ---------- ticks : list of floats - List of tick locations. + List of tick locations. The axis `.Locator` is replaced by a + `~.ticker.FixedLocator`. + + Some tick formatters will not label arbitrary tick positions; + e.g. log formatters only label decade ticks by default. In + such a case you can set a formatter explicitly on the axis + using `.Axis.set_major_formatter` or provide formatted + *labels* yourself. + labels : list of str, optional + List of tick labels. If not set, the labels are generated with + the axis tick `.Formatter`. minor : bool, default: False If ``False``, set the major ticks; if ``True``, the minor ticks. + **kwargs + `.Text` properties for the labels. These take effect only if you + pass *labels*. In other cases, please use `~.Axes.tick_params`. Notes ----- @@ -1799,39 +2027,10 @@ def set_ticks(self, ticks, *, minor=False): other limits, you should set the limits explicitly after setting the ticks. """ - # XXX if the user changes units, the information will be lost here - ticks = self.convert_units(ticks) - if self is self.axes.xaxis: - shared = [ - ax.xaxis - for ax in self.axes.get_shared_x_axes().get_siblings(self.axes) - ] - elif self is self.axes.yaxis: - shared = [ - ax.yaxis - for ax in self.axes.get_shared_y_axes().get_siblings(self.axes) - ] - elif hasattr(self.axes, "zaxis") and self is self.axes.zaxis: - shared = [ - ax.zaxis - for ax in self.axes._shared_z_axes.get_siblings(self.axes) - ] - else: - shared = [self] - for axis in shared: - if len(ticks) > 1: - xleft, xright = axis.get_view_interval() - if xright > xleft: - axis.set_view_interval(min(ticks), max(ticks)) - else: - axis.set_view_interval(max(ticks), min(ticks)) - self.axes.stale = True - if minor: - self.set_minor_locator(mticker.FixedLocator(ticks)) - return self.get_minor_ticks(len(ticks)) - else: - self.set_major_locator(mticker.FixedLocator(ticks)) - return self.get_major_ticks(len(ticks)) + result = self._set_tick_locations(ticks, minor=minor) + if labels is not None: + self.set_ticklabels(labels, minor=minor, **kwargs) + return result def _get_tick_boxes_siblings(self, renderer): """ @@ -1842,7 +2041,7 @@ def _get_tick_boxes_siblings(self, renderer): """ # Get the Grouper keeping track of x or y label groups for this figure. axis_names = [ - name for name, axis in self.axes._get_axis_map().items() + name for name, axis in self.axes._axis_map.items() if name in self.figure._align_label_groups and axis is self] if len(axis_names) != 1: return [], [] @@ -1850,11 +2049,11 @@ def _get_tick_boxes_siblings(self, renderer): grouper = self.figure._align_label_groups[axis_name] bboxes = [] bboxes2 = [] - # If we want to align labels from other axes: + # If we want to align labels from other Axes: for ax in grouper.get_siblings(self.axes): - axis = ax._get_axis_map()[axis_name] + axis = getattr(ax, f"{axis_name}axis") ticks_to_draw = axis._update_ticks() - tlb, tlb2 = axis._get_tick_bboxes(ticks_to_draw, renderer) + tlb, tlb2 = axis._get_ticklabel_bboxes(ticks_to_draw, renderer) bboxes.extend(tlb) bboxes2.extend(tlb2) return bboxes, bboxes2 @@ -1873,16 +2072,6 @@ def _update_offset_text_position(self, bboxes, bboxes2): """ raise NotImplementedError('Derived must override') - @_api.deprecated("3.3") - def pan(self, numsteps): - """Pan by *numsteps* (can be positive or negative).""" - self.major.locator.pan(numsteps) - - @_api.deprecated("3.3") - def zoom(self, direction): - """Zoom in/out on axis; if *direction* is >0 zoom in, else zoom out.""" - self.major.locator.zoom(direction) - def axis_date(self, tz=None): """ Set up axis ticks and labels to treat data along this Axis as dates. @@ -1995,6 +2184,7 @@ def setter(self, vmin, vmax, ignore=False): class XAxis(Axis): __name__ = 'xaxis' axis_name = 'x' #: Read-only name identifying the axis. + _tick_class = XTick def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -2031,17 +2221,10 @@ def contains(self, mouseevent): return False, {} (l, b), (r, t) = self.axes.transAxes.transform([(0, 0), (1, 1)]) inaxis = 0 <= xaxes <= 1 and ( - b - self.pickradius < y < b or - t < y < t + self.pickradius) + b - self._pickradius < y < b or + t < y < t + self._pickradius) return inaxis, {} - def _get_tick(self, major): - if major: - tick_kw = self._major_tick_kw - else: - tick_kw = self._minor_tick_kw - return XTick(self.axes, 0, major=major, **tick_kw) - def set_label_position(self, position): """ Set the label position (top or bottom) @@ -2072,10 +2255,9 @@ def _update_label_position(self, renderer): if self.label_position == 'bottom': try: spine = self.axes.spines['bottom'] - spinebbox = spine.get_transform().transform_path( - spine.get_path()).get_extents() + spinebbox = spine.get_window_extent() except KeyError: - # use axes if spine doesn't exist + # use Axes if spine doesn't exist spinebbox = self.axes.bbox bbox = mtransforms.Bbox.union(bboxes + [spinebbox]) bottom = bbox.y0 @@ -2083,14 +2265,12 @@ def _update_label_position(self, renderer): self.label.set_position( (x, bottom - self.labelpad * self.figure.dpi / 72) ) - else: try: spine = self.axes.spines['top'] - spinebbox = spine.get_transform().transform_path( - spine.get_path()).get_extents() + spinebbox = spine.get_window_extent() except KeyError: - # use axes if spine doesn't exist + # use Axes if spine doesn't exist spinebbox = self.axes.bbox bbox = mtransforms.Bbox.union(bboxes2 + [spinebbox]) top = bbox.y1 @@ -2123,26 +2303,27 @@ def _update_offset_text_position(self, bboxes, bboxes2): y = top + self.OFFSETTEXTPAD * self.figure.dpi / 72 self.offsetText.set_position((x, y)) + @_api.deprecated("3.6") def get_text_heights(self, renderer): """ Return how much space should be reserved for text above and below the - axes, as a pair of floats. + Axes, as a pair of floats. """ bbox, bbox2 = self.get_ticklabel_extents(renderer) # MGDTODO: Need a better way to get the pad - padPixels = self.majorTicks[0].get_pad_pixels() + pad_pixels = self.majorTicks[0].get_pad_pixels() above = 0.0 if bbox2.height: - above += bbox2.height + padPixels + above += bbox2.height + pad_pixels below = 0.0 if bbox.height: - below += bbox.height + padPixels + below += bbox.height + pad_pixels if self.get_label_position() == 'top': - above += self.label.get_window_extent(renderer).height + padPixels + above += self.label.get_window_extent(renderer).height + pad_pixels else: - below += self.label.get_window_extent(renderer).height + padPixels + below += self.label.get_window_extent(renderer).height + pad_pixels return above, below def set_ticks_position(self, position): @@ -2187,7 +2368,7 @@ def set_ticks_position(self, position): def tick_top(self): """ - Move ticks and ticklabels (if present) to the top of the axes. + Move ticks and ticklabels (if present) to the top of the Axes. """ label = True if 'label1On' in self._major_tick_kw: @@ -2199,7 +2380,7 @@ def tick_top(self): def tick_bottom(self): """ - Move ticks and ticklabels (if present) to the bottom of the axes. + Move ticks and ticklabels (if present) to the bottom of the Axes. """ label = True if 'label1On' in self._major_tick_kw: @@ -2225,33 +2406,23 @@ def get_ticks_position(self): def get_minpos(self): return self.axes.dataLim.minposx - def set_inverted(self, inverted): - # docstring inherited - a, b = self.get_view_interval() - # cast to bool to avoid bad interaction between python 3.8 and np.bool_ - self.axes.set_xlim(sorted((a, b), reverse=bool(inverted)), auto=None) - def set_default_intervals(self): # docstring inherited - xmin, xmax = 0., 1. - dataMutated = self.axes.dataLim.mutatedx() - viewMutated = self.axes.viewLim.mutatedx() - if not dataMutated or not viewMutated: + # only change view if dataLim has not changed and user has + # not changed the view: + if (not self.axes.dataLim.mutatedx() and + not self.axes.viewLim.mutatedx()): if self.converter is not None: info = self.converter.axisinfo(self.units, self) if info.default_limits is not None: - valmin, valmax = info.default_limits - xmin = self.converter.convert(valmin, self.units, self) - xmax = self.converter.convert(valmax, self.units, self) - if not dataMutated: - self.axes.dataLim.intervalx = xmin, xmax - if not viewMutated: - self.axes.viewLim.intervalx = xmin, xmax + xmin, xmax = self.convert_units(info.default_limits) + self.axes.viewLim.intervalx = xmin, xmax self.stale = True def get_tick_space(self): - ends = self.axes.transAxes.transform([[0, 0], [1, 0]]) - length = ((ends[1][0] - ends[0][0]) / self.axes.figure.dpi) * 72 + ends = mtransforms.Bbox.unit().transformed( + self.axes.transAxes - self.figure.dpi_scale_trans) + length = ends.width * 72 # There is a heuristic here that the aspect ratio of tick text # is no more than 3:1 size = self._get_tick_label_size('x') * 3 @@ -2264,6 +2435,7 @@ def get_tick_space(self): class YAxis(Axis): __name__ = 'yaxis' axis_name = 'y' #: Read-only name identifying the axis. + _tick_class = YTick def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -2302,17 +2474,10 @@ def contains(self, mouseevent): return False, {} (l, b), (r, t) = self.axes.transAxes.transform([(0, 0), (1, 1)]) inaxis = 0 <= yaxes <= 1 and ( - l - self.pickradius < x < l or - r < x < r + self.pickradius) + l - self._pickradius < x < l or + r < x < r + self._pickradius) return inaxis, {} - def _get_tick(self, major): - if major: - tick_kw = self._major_tick_kw - else: - tick_kw = self._minor_tick_kw - return YTick(self.axes, 0, major=major, **tick_kw) - def set_label_position(self, position): """ Set the label position (left or right) @@ -2339,15 +2504,13 @@ def _update_label_position(self, renderer): # get bounding boxes for this axis and any siblings # that have been set by `fig.align_ylabels()` bboxes, bboxes2 = self._get_tick_boxes_siblings(renderer=renderer) - x, y = self.label.get_position() if self.label_position == 'left': try: spine = self.axes.spines['left'] - spinebbox = spine.get_transform().transform_path( - spine.get_path()).get_extents() + spinebbox = spine.get_window_extent() except KeyError: - # use axes if spine doesn't exist + # use Axes if spine doesn't exist spinebbox = self.axes.bbox bbox = mtransforms.Bbox.union(bboxes + [spinebbox]) left = bbox.x0 @@ -2358,14 +2521,13 @@ def _update_label_position(self, renderer): else: try: spine = self.axes.spines['right'] - spinebbox = spine.get_transform().transform_path( - spine.get_path()).get_extents() + spinebbox = spine.get_window_extent() except KeyError: - # use axes if spine doesn't exist + # use Axes if spine doesn't exist spinebbox = self.axes.bbox + bbox = mtransforms.Bbox.union(bboxes2 + [spinebbox]) right = bbox.x1 - self.label.set_position( (right + self.labelpad * self.figure.dpi / 72, y) ) @@ -2375,8 +2537,13 @@ def _update_offset_text_position(self, bboxes, bboxes2): Update the offset_text position based on the sequence of bounding boxes of all the ticklabels """ - x, y = self.offsetText.get_position() - top = self.axes.bbox.ymax + x, _ = self.offsetText.get_position() + if 'outline' in self.axes.spines: + # Special case for colorbars: + bbox = self.axes.spines['outline'].get_window_extent() + else: + bbox = self.axes.bbox + top = bbox.ymax self.offsetText.set_position( (x, top + self.OFFSETTEXTPAD * self.figure.dpi / 72) ) @@ -2394,22 +2561,23 @@ def set_offset_position(self, position): self.offsetText.set_position((x, y)) self.stale = True + @_api.deprecated("3.6") def get_text_widths(self, renderer): bbox, bbox2 = self.get_ticklabel_extents(renderer) # MGDTODO: Need a better way to get the pad - padPixels = self.majorTicks[0].get_pad_pixels() + pad_pixels = self.majorTicks[0].get_pad_pixels() left = 0.0 if bbox.width: - left += bbox.width + padPixels + left += bbox.width + pad_pixels right = 0.0 if bbox2.width: - right += bbox2.width + padPixels + right += bbox2.width + pad_pixels if self.get_label_position() == 'left': - left += self.label.get_window_extent(renderer).width + padPixels + left += self.label.get_window_extent(renderer).width + pad_pixels else: - right += self.label.get_window_extent(renderer).width + padPixels + right += self.label.get_window_extent(renderer).width + pad_pixels return left, right def set_ticks_position(self, position): @@ -2450,7 +2618,7 @@ def set_ticks_position(self, position): def tick_right(self): """ - Move ticks and ticklabels (if present) to the right of the axes. + Move ticks and ticklabels (if present) to the right of the Axes. """ label = True if 'label1On' in self._major_tick_kw: @@ -2463,7 +2631,7 @@ def tick_right(self): def tick_left(self): """ - Move ticks and ticklabels (if present) to the left of the axes. + Move ticks and ticklabels (if present) to the left of the Axes. """ label = True if 'label1On' in self._major_tick_kw: @@ -2490,33 +2658,23 @@ def get_ticks_position(self): def get_minpos(self): return self.axes.dataLim.minposy - def set_inverted(self, inverted): - # docstring inherited - a, b = self.get_view_interval() - # cast to bool to avoid bad interaction between python 3.8 and np.bool_ - self.axes.set_ylim(sorted((a, b), reverse=bool(inverted)), auto=None) - def set_default_intervals(self): # docstring inherited - ymin, ymax = 0., 1. - dataMutated = self.axes.dataLim.mutatedy() - viewMutated = self.axes.viewLim.mutatedy() - if not dataMutated or not viewMutated: + # only change view if dataLim has not changed and user has + # not changed the view: + if (not self.axes.dataLim.mutatedy() and + not self.axes.viewLim.mutatedy()): if self.converter is not None: info = self.converter.axisinfo(self.units, self) if info.default_limits is not None: - valmin, valmax = info.default_limits - ymin = self.converter.convert(valmin, self.units, self) - ymax = self.converter.convert(valmax, self.units, self) - if not dataMutated: - self.axes.dataLim.intervaly = ymin, ymax - if not viewMutated: - self.axes.viewLim.intervaly = ymin, ymax + ymin, ymax = self.convert_units(info.default_limits) + self.axes.viewLim.intervaly = ymin, ymax self.stale = True def get_tick_space(self): - ends = self.axes.transAxes.transform([[0, 0], [0, 1]]) - length = ((ends[1][1] - ends[0][1]) / self.axes.figure.dpi) * 72 + ends = mtransforms.Bbox.unit().transformed( + self.axes.transAxes - self.figure.dpi_scale_trans) + length = ends.height * 72 # Having a spacing of at least 2 just looks good. size = self._get_tick_label_size('y') * 2 if size > 0: diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 32e069ed69cf..6e603cafbef0 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -26,30 +26,30 @@ """ from collections import namedtuple -from contextlib import contextmanager, suppress +from contextlib import ExitStack, contextmanager, nullcontext from enum import Enum, IntEnum import functools import importlib import inspect import io +import itertools import logging import os -import re import sys import time -import traceback from weakref import WeakKeyDictionary import numpy as np import matplotlib as mpl from matplotlib import ( - _api, backend_tools as tools, cbook, colors, docstring, textpath, - tight_bbox, transforms, widgets, get_backend, is_interactive, rcParams) + _api, backend_tools as tools, cbook, colors, _docstring, textpath, + _tight_bbox, transforms, widgets, get_backend, is_interactive, rcParams) from matplotlib._pylab_helpers import Gcf from matplotlib.backend_managers import ToolManager from matplotlib.cbook import _setattr_cm from matplotlib.path import Path +from matplotlib.texmanager import TexManager from matplotlib.transforms import Affine2D from matplotlib._enums import JoinStyle, CapStyle @@ -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', } @@ -98,13 +100,15 @@ def _safe_pyplot_import(): current_framework = cbook._get_running_interactive_framework() if current_framework is None: raise # No, something else went wrong, likely with the install... - backend_mapping = {'qt5': 'qt5agg', - 'qt4': 'qt4agg', - 'gtk3': 'gtk3agg', - 'wx': 'wxagg', - 'tk': 'tkagg', - 'macosx': 'macosx', - 'headless': 'agg'} + backend_mapping = { + 'qt': 'qtagg', + 'gtk3': 'gtk3agg', + 'gtk4': 'gtk4agg', + 'wx': 'wxagg', + 'tk': 'tkagg', + 'macosx': 'macosx', + 'headless': 'agg', + } backend = backend_mapping[current_framework] rcParams["backend"] = mpl.rcParamsOrig["backend"] = backend import matplotlib.pyplot as plt # Now this should succeed. @@ -149,20 +153,20 @@ class RendererBase: An abstract base class to handle drawing/rendering operations. The following methods must be implemented in the backend for full - functionality (though just implementing :meth:`draw_path` alone would - give a highly capable backend): + functionality (though just implementing `draw_path` alone would give a + highly capable backend): - * :meth:`draw_path` - * :meth:`draw_image` - * :meth:`draw_gouraud_triangle` + * `draw_path` + * `draw_image` + * `draw_gouraud_triangle` The following methods *should* be implemented in the backend for optimization reasons: - * :meth:`draw_text` - * :meth:`draw_markers` - * :meth:`draw_path_collection` - * :meth:`draw_quad_mesh` + * `draw_text` + * `draw_markers` + * `draw_path_collection` + * `draw_quad_mesh` """ def __init__(self): @@ -195,10 +199,9 @@ def draw_markers(self, gc, marker_path, marker_trans, path, """ Draw a marker at each of *path*'s vertices (excluding control points). - This provides a fallback implementation of draw_markers that - makes multiple calls to :meth:`draw_path`. Some backends may - want to override this method in order to draw the marker only - once and reuse it multiple times. + The base (fallback) implementation makes multiple calls to `draw_path`. + Backends may want to override this method in order to draw the marker + only once and reuse it multiple times. Parameters ---------- @@ -218,38 +221,38 @@ def draw_markers(self, gc, marker_path, marker_trans, path, rgbFace) def draw_path_collection(self, gc, master_transform, paths, all_transforms, - offsets, offsetTrans, facecolors, edgecolors, + offsets, offset_trans, facecolors, edgecolors, linewidths, linestyles, antialiaseds, urls, offset_position): """ - Draw a collection of paths selecting drawing properties from - the lists *facecolors*, *edgecolors*, *linewidths*, - *linestyles* and *antialiaseds*. *offsets* is a list of - offsets to apply to each of the paths. The offsets in - *offsets* are first transformed by *offsetTrans* before being - applied. - - *offset_position* may be either "screen" or "data" depending on the - space that the offsets are in; "data" is deprecated. - - This provides a fallback implementation of - :meth:`draw_path_collection` that makes multiple calls to - :meth:`draw_path`. Some backends may want to override this in - order to render each set of path data only once, and then - reference that path multiple times with the different offsets, - colors, styles etc. The generator methods - :meth:`_iter_collection_raw_paths` and - :meth:`_iter_collection` are provided to help with (and - standardize) the implementation across backends. It is highly - recommended to use those generators, so that changes to the - behavior of :meth:`draw_path_collection` can be made globally. + Draw a collection of *paths*. + + Each path is first transformed by the corresponding entry + in *all_transforms* (a list of (3, 3) matrices) and then by + *master_transform*. They are then translated by the corresponding + entry in *offsets*, which has been first transformed by *offset_trans*. + + *facecolors*, *edgecolors*, *linewidths*, *linestyles*, and + *antialiased* are lists that set the corresponding properties. + + *offset_position* is unused now, but the argument is kept for + backwards compatibility. + + The base (fallback) implementation makes multiple calls to `draw_path`. + Backends may want to override this in order to render each set of + path data only once, and then reference that path multiple times with + the different offsets, colors, styles etc. The generator methods + `_iter_collection_raw_paths` and `_iter_collection` are provided to + help with (and standardize) the implementation across backends. It + is highly recommended to use those generators, so that changes to the + behavior of `draw_path_collection` can be made globally. """ path_ids = self._iter_collection_raw_paths(master_transform, paths, all_transforms) for xo, yo, path_id, gc0, rgbFace in self._iter_collection( - gc, master_transform, all_transforms, list(path_ids), offsets, - offsetTrans, facecolors, edgecolors, linewidths, linestyles, + gc, list(path_ids), offsets, offset_trans, + facecolors, edgecolors, linewidths, linestyles, antialiaseds, urls, offset_position): path, transform = path_id # Only apply another translation if we have an offset, else we @@ -266,13 +269,14 @@ def draw_quad_mesh(self, gc, master_transform, meshWidth, meshHeight, coordinates, offsets, offsetTrans, facecolors, antialiased, edgecolors): """ - Fallback implementation of :meth:`draw_quad_mesh` that generates paths - and then calls :meth:`draw_path_collection`. + Draw a quadmesh. + + The base (fallback) implementation converts the quadmesh to paths and + then calls `draw_path_collection`. """ from matplotlib.collections import QuadMesh - paths = QuadMesh.convert_mesh_to_paths( - meshWidth, meshHeight, coordinates) + paths = QuadMesh._convert_mesh_to_paths(coordinates) if edgecolors is None: edgecolors = facecolors @@ -320,18 +324,17 @@ def draw_gouraud_triangles(self, gc, triangles_array, colors_array, def _iter_collection_raw_paths(self, master_transform, paths, all_transforms): """ - Helper method (along with :meth:`_iter_collection`) to implement - :meth:`draw_path_collection` in a space-efficient manner. + Helper method (along with `_iter_collection`) to implement + `draw_path_collection` in a memory-efficient manner. - This method yields all of the base path/transform - combinations, given a master transform, a list of paths and - list of transforms. + This method yields all of the base path/transform combinations, given a + master transform, a list of paths and list of transforms. The arguments should be exactly what is passed in to - :meth:`draw_path_collection`. + `draw_path_collection`. - The backend should take each yielded path and transform and - create an object that can be referenced (reused) later. + The backend should take each yielded path and transform and create an + object that can be referenced (reused) later. """ Npaths = len(paths) Ntransforms = len(all_transforms) @@ -351,8 +354,8 @@ def _iter_collection_uses_per_path(self, paths, all_transforms, offsets, facecolors, edgecolors): """ Compute how many times each raw path object returned by - _iter_collection_raw_paths would be used when calling - _iter_collection. This is intended for the backend to decide + `_iter_collection_raw_paths` would be used when calling + `_iter_collection`. This is intended for the backend to decide on the tradeoff between using the paths in-line and storing them once and reusing. Rounds up in case the number of uses is not the same for every path. @@ -364,24 +367,22 @@ def _iter_collection_uses_per_path(self, paths, all_transforms, N = max(Npath_ids, len(offsets)) return (N + Npath_ids - 1) // Npath_ids - def _iter_collection(self, gc, master_transform, all_transforms, - path_ids, offsets, offsetTrans, facecolors, + def _iter_collection(self, gc, path_ids, offsets, offset_trans, facecolors, edgecolors, linewidths, linestyles, antialiaseds, urls, offset_position): """ - Helper method (along with :meth:`_iter_collection_raw_paths`) to - implement :meth:`draw_path_collection` in a space-efficient manner. + Helper method (along with `_iter_collection_raw_paths`) to implement + `draw_path_collection` in a memory-efficient manner. - This method yields all of the path, offset and graphics - context combinations to draw the path collection. The caller - should already have looped over the results of - :meth:`_iter_collection_raw_paths` to draw this collection. + This method yields all of the path, offset and graphics context + combinations to draw the path collection. The caller should already + have looped over the results of `_iter_collection_raw_paths` to draw + this collection. The arguments should be the same as that passed into - :meth:`draw_path_collection`, with the exception of - *path_ids*, which is a list of arbitrary objects that the - backend will use to reference one of the paths created in the - :meth:`_iter_collection_raw_paths` stage. + `draw_path_collection`, with the exception of *path_ids*, which is a + list of arbitrary objects that the backend will use to reference one of + the paths created in the `_iter_collection_raw_paths` stage. Each yielded result is of the form:: @@ -391,7 +392,6 @@ def _iter_collection(self, gc, master_transform, all_transforms, *path_ids*; *gc* is a graphics context and *rgbFace* is a color to use for filling the path. """ - Ntransforms = len(all_transforms) Npaths = len(path_ids) Noffsets = len(offsets) N = max(Npaths, Noffsets) @@ -399,74 +399,55 @@ def _iter_collection(self, gc, master_transform, all_transforms, Nedgecolors = len(edgecolors) Nlinewidths = len(linewidths) Nlinestyles = len(linestyles) - Naa = len(antialiaseds) Nurls = len(urls) - if offset_position == "data": - _api.warn_deprecated( - "3.3", message="Support for offset_position='data' is " - "deprecated since %(since)s and will be removed %(removal)s.") - if (Nfacecolors == 0 and Nedgecolors == 0) or Npaths == 0: return - if Noffsets: - toffsets = offsetTrans.transform(offsets) gc0 = self.new_gc() gc0.copy_properties(gc) - if Nfacecolors == 0: - rgbFace = None + def cycle_or_default(seq, default=None): + # Cycle over *seq* if it is not empty; else always yield *default*. + return (itertools.cycle(seq) if len(seq) + else itertools.repeat(default)) + + pathids = cycle_or_default(path_ids) + toffsets = cycle_or_default(offset_trans.transform(offsets), (0, 0)) + fcs = cycle_or_default(facecolors) + ecs = cycle_or_default(edgecolors) + lws = cycle_or_default(linewidths) + lss = cycle_or_default(linestyles) + aas = cycle_or_default(antialiaseds) + urls = cycle_or_default(urls) if Nedgecolors == 0: gc0.set_linewidth(0.0) - xo, yo = 0, 0 - for i in range(N): - path_id = path_ids[i % Npaths] - if Noffsets: - xo, yo = toffsets[i % Noffsets] - if offset_position == 'data': - if Ntransforms: - transform = ( - Affine2D(all_transforms[i % Ntransforms]) + - master_transform) - else: - transform = master_transform - (xo, yo), (xp, yp) = transform.transform( - [(xo, yo), (0, 0)]) - xo = -(xp - xo) - yo = -(yp - yo) + for pathid, (xo, yo), fc, ec, lw, ls, aa, url in itertools.islice( + zip(pathids, toffsets, fcs, ecs, lws, lss, aas, urls), N): if not (np.isfinite(xo) and np.isfinite(yo)): continue - if Nfacecolors: - rgbFace = facecolors[i % Nfacecolors] if Nedgecolors: if Nlinewidths: - gc0.set_linewidth(linewidths[i % Nlinewidths]) + gc0.set_linewidth(lw) if Nlinestyles: - gc0.set_dashes(*linestyles[i % Nlinestyles]) - fg = edgecolors[i % Nedgecolors] - if len(fg) == 4: - if fg[3] == 0.0: - gc0.set_linewidth(0) - else: - gc0.set_foreground(fg) + gc0.set_dashes(*ls) + if len(ec) == 4 and ec[3] == 0.0: + gc0.set_linewidth(0) else: - gc0.set_foreground(fg) - if rgbFace is not None and len(rgbFace) == 4: - if rgbFace[3] == 0: - rgbFace = None - gc0.set_antialiased(antialiaseds[i % Naa]) + gc0.set_foreground(ec) + if fc is not None and len(fc) == 4 and fc[3] == 0: + fc = None + gc0.set_antialiased(aa) if Nurls: - gc0.set_url(urls[i % Nurls]) - - yield xo, yo, path_id, gc0, rgbFace + gc0.set_url(url) + yield xo, yo, pathid, gc0, fc gc0.restore() def get_image_magnification(self): """ - Get the factor by which to magnify images passed to :meth:`draw_image`. + Get the factor by which to magnify images passed to `draw_image`. Allows a backend to have images at a different resolution to other artists. """ @@ -494,14 +475,13 @@ def draw_image(self, gc, x, y, im, transform=None): transform : `matplotlib.transforms.Affine2DBase` If and only if the concrete backend is written such that - :meth:`option_scale_image` returns ``True``, an affine - transformation (i.e., an `.Affine2DBase`) *may* be passed to - :meth:`draw_image`. The translation vector of the transformation - is given in physical units (i.e., dots or pixels). Note that - the transformation does not override *x* and *y*, and has to be - applied *before* translating the result by *x* and *y* (this can - be accomplished by adding *x* and *y* to the translation vector - defined by *transform*). + `option_scale_image` returns ``True``, an affine transformation + (i.e., an `.Affine2DBase`) *may* be passed to `draw_image`. The + translation vector of the transformation is given in physical units + (i.e., dots or pixels). Note that the transformation does not + override *x* and *y*, and has to be applied *before* translating + the result by *x* and *y* (this can be accomplished by adding *x* + and *y* to the translation vector defined by *transform*). """ raise NotImplementedError @@ -517,20 +497,19 @@ def option_image_nocomposite(self): def option_scale_image(self): """ - Return whether arbitrary affine transformations in :meth:`draw_image` - are supported (True for most vector backends). + Return whether arbitrary affine transformations in `draw_image` are + supported (True for most vector backends). """ return False - @_api.delete_parameter("3.3", "ismath") - def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!', mtext=None): + def draw_tex(self, gc, x, y, s, prop, angle, *, mtext=None): """ """ self._draw_text_as_path(gc, x, y, s, prop, angle, ismath="TeX") def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): """ - Draw the text instance. + Draw a text instance. Parameters ---------- @@ -626,13 +605,12 @@ def get_text_width_height_descent(self, s, prop, ismath): to the baseline), in display coords, of the string *s* with `.FontProperties` *prop*. """ + fontsize = prop.get_size_in_points() + if ismath == 'TeX': # todo: handle props - texmanager = self._text2path.get_texmanager() - fontsize = prop.get_size_in_points() - w, h, d = texmanager.get_text_width_height_descent( + return TexManager().get_text_width_height_descent( s, fontsize, renderer=self) - return w, h, d dpi = self.points_to_pixels(72) if ismath: @@ -641,8 +619,7 @@ def get_text_width_height_descent(self, s, prop, ismath): flags = self._text2path._get_hinting_flag() font = self._text2path._get_font(prop) - size = prop.get_size_in_points() - font.set_size(size, dpi) + font.set_size(fontsize, dpi) # the width and height of unrotated string font.set_text(s, 0.0, flags=flags) w, h = font.get_width_height() @@ -656,7 +633,7 @@ def flipy(self): """ Return whether y values increase from top to bottom. - Note that this only affects drawing of texts and images. + Note that this only affects drawing of texts. """ return True @@ -667,7 +644,6 @@ def get_canvas_width_height(self): def get_texmanager(self): """Return the `.TexManager` instance.""" if self._texmanager is None: - from matplotlib.texmanager import TexManager self._texmanager = TexManager() return self._texmanager @@ -835,12 +811,9 @@ def get_dashes(self): """ Return the dash style as an (offset, dash-list) pair. - The dash list is a even-length list that gives the ink on, ink off in - points. See p. 107 of to PostScript `blue book`_ for more info. + See `.set_dashes` for details. Default value is (None, None). - - .. _blue book: https://www-cdf.fnal.gov/offline/PostScript/BLUEBOOK.PDF """ return self._dashes @@ -904,7 +877,7 @@ def set_antialiased(self, b): # Use ints to make life easier on extension code trying to read the gc. self._antialiased = int(bool(b)) - @docstring.interpd + @_docstring.interpd def set_capstyle(self, cs): """ Set how to draw endpoints of lines. @@ -930,24 +903,28 @@ def set_dashes(self, dash_offset, dash_list): Parameters ---------- - dash_offset : float or None - The offset (usually 0). + dash_offset : float + Distance, in points, into the dash pattern at which to + start the pattern. It is usually set to 0. dash_list : array-like or None - The on-off sequence as points. + The on-off sequence as points. None specifies a solid line. All + values must otherwise be non-negative (:math:`\\ge 0`). Notes ----- - ``(None, None)`` specifies a solid line. - - See p. 107 of to PostScript `blue book`_ for more info. - - .. _blue book: https://www-cdf.fnal.gov/offline/PostScript/BLUEBOOK.PDF + See p. 666 of the PostScript + `Language Reference + `_ + for more info. """ if dash_list is not None: dl = np.asarray(dash_list) if np.any(dl < 0.0): raise ValueError( - "All values in the dash list must be positive") + "All values in the dash list must be non-negative") + if dl.size and not np.any(dl > 0.0): + raise ValueError( + 'At least one value in the dash list must be positive') self._dashes = dash_offset, dash_list def set_foreground(self, fg, isRGBA=False): @@ -970,7 +947,7 @@ def set_foreground(self, fg, isRGBA=False): else: self._rgb = colors.to_rgba(fg) - @docstring.interpd + @_docstring.interpd def set_joinstyle(self, js): """ Set how to draw connections between line segments. @@ -1228,9 +1205,10 @@ def _on_timer(self): class Event: """ - A Matplotlib event. Attach additional attributes as defined in - :meth:`FigureCanvasBase.mpl_connect`. The following attributes - are defined and shown with their default values + A Matplotlib event. + + The following attributes are defined and shown with their default values. + Subclasses may define additional attributes. Attributes ---------- @@ -1241,28 +1219,33 @@ class Event: guiEvent The GUI event that triggered the Matplotlib event. """ + def __init__(self, name, canvas, guiEvent=None): self.name = name self.canvas = canvas self.guiEvent = guiEvent + def _process(self): + """Generate an event with name ``self.name`` on ``self.canvas``.""" + self.canvas.callbacks.process(self.name, self) + class DrawEvent(Event): """ - An event triggered by a draw operation on the canvas + An event triggered by a draw operation on the canvas. - In most backends callbacks subscribed to this callback will be - fired after the rendering is complete but before the screen is - updated. Any extra artists drawn to the canvas's renderer will - be reflected without an explicit call to ``blit``. + In most backends, callbacks subscribed to this event will be fired after + the rendering is complete but before the screen is updated. Any extra + artists drawn to the canvas's renderer will be reflected without an + explicit call to ``blit``. .. warning:: Calling ``canvas.draw`` and ``canvas.blit`` in these callbacks may not be safe with all backends and may cause infinite recursion. - In addition to the `Event` attributes, the following event - attributes are defined: + A DrawEvent has a number of special attributes in addition to those defined + by the parent `Event` class. Attributes ---------- @@ -1276,10 +1259,10 @@ def __init__(self, name, canvas, renderer): class ResizeEvent(Event): """ - An event triggered by a canvas resize + An event triggered by a canvas resize. - In addition to the `Event` attributes, the following event - attributes are defined: + A ResizeEvent has a number of special attributes in addition to those + defined by the parent `Event` class. Attributes ---------- @@ -1288,6 +1271,7 @@ class ResizeEvent(Event): height : int Height of the canvas in pixels. """ + def __init__(self, name, canvas): super().__init__(name, canvas) self.width, self.height = canvas.get_width_height() @@ -1301,44 +1285,34 @@ class LocationEvent(Event): """ An event that has a screen location. - The following additional attributes are defined and shown with - their default values. - - In addition to the `Event` attributes, the following - event attributes are defined: + A LocationEvent has a number of special attributes in addition to those + defined by the parent `Event` class. Attributes ---------- - x : int - x position - pixels from left of canvas. - y : int - y position - pixels from bottom of canvas. + x, y : int or None + Event location in pixels from bottom left of canvas. inaxes : `~.axes.Axes` or None The `~.axes.Axes` instance over which the mouse is, if any. - xdata : float or None - x data coordinate of the mouse. - ydata : float or None - y data coordinate of the mouse. + xdata, ydata : float or None + Data coordinates of the mouse within *inaxes*, or *None* if the mouse + is not over an Axes. """ - lastevent = None # the last event that was triggered before this one + lastevent = None # The last event processed so far. def __init__(self, name, canvas, x, y, guiEvent=None): - """ - (*x*, *y*) in figure coords ((0, 0) = bottom left). - """ super().__init__(name, canvas, guiEvent=guiEvent) # x position - pixels from left of canvas self.x = int(x) if x is not None else x # y position - pixels from right of canvas self.y = int(y) if y is not None else y - self.inaxes = None # the Axes instance if mouse us over axes + self.inaxes = None # the Axes instance the mouse is over self.xdata = None # x coord of mouse in data coords self.ydata = None # y coord of mouse in data coords if x is None or y is None: - # cannot check if event was in axes if no (x, y) info - self._update_enter_leave() + # cannot check if event was in Axes if no (x, y) info return if self.canvas.mouse_grabber is None: @@ -1356,34 +1330,6 @@ def __init__(self, name, canvas, x, y, guiEvent=None): self.xdata = xdata self.ydata = ydata - self._update_enter_leave() - - def _update_enter_leave(self): - """Process the figure/axes enter leave events.""" - if LocationEvent.lastevent is not None: - last = LocationEvent.lastevent - if last.inaxes != self.inaxes: - # process axes enter/leave events - try: - if last.inaxes is not None: - last.canvas.callbacks.process('axes_leave_event', last) - except Exception: - pass - # See ticket 2901582. - # I think this is a valid exception to the rule - # against catching all exceptions; if anything goes - # wrong, we simply want to move on and process the - # current event. - if self.inaxes is not None: - self.canvas.callbacks.process('axes_enter_event', self) - - else: - # process a figure enter event - if self.inaxes is not None: - self.canvas.callbacks.process('axes_enter_event', self) - - LocationEvent.lastevent = self - class MouseButton(IntEnum): LEFT = 1 @@ -1395,23 +1341,25 @@ class MouseButton(IntEnum): class MouseEvent(LocationEvent): """ - A mouse event ('button_press_event', - 'button_release_event', - 'scroll_event', - 'motion_notify_event'). + A mouse event ('button_press_event', 'button_release_event', \ +'scroll_event', 'motion_notify_event'). - In addition to the `Event` and `LocationEvent` - attributes, the following attributes are defined: + A MouseEvent has a number of special attributes in addition to those + defined by the parent `Event` and `LocationEvent` classes. Attributes ---------- button : None or `MouseButton` or {'up', 'down'} The button pressed. 'up' and 'down' are used for scroll events. + Note that LEFT and RIGHT actually refer to the "primary" and "secondary" buttons, i.e. if the user inverts their left and right buttons ("left-handed setting") then the LEFT button will be the one physically on the right. + If this is unset, *name* is "scroll_event", and *step* is nonzero, then + this will be set to "up" or "down" depending on the sign of *step*. + key : None or str The key pressed when the mouse event triggered, e.g. 'shift'. See `KeyEvent`. @@ -1443,21 +1391,19 @@ def on_press(event): def __init__(self, name, canvas, x, y, button=None, key=None, step=0, dblclick=False, guiEvent=None): - """ - (*x*, *y*) in figure coords ((0, 0) = bottom left) - button pressed None, 1, 2, 3, 'up', 'down' - """ + super().__init__(name, canvas, x, y, guiEvent=guiEvent) if button in MouseButton.__members__.values(): button = MouseButton(button) + if name == "scroll_event" and button is None: + if step > 0: + button = "up" + elif step < 0: + button = "down" self.button = button self.key = key self.step = step self.dblclick = dblclick - # super-init is deferred to the end because it calls back on - # 'axes_enter_event', which requires a fully initialized event. - super().__init__(name, canvas, x, y, guiEvent=guiEvent) - def __str__(self): return (f"{self.name}: " f"xy=({self.x}, {self.y}) xydata=({self.xdata}, {self.ydata}) " @@ -1467,11 +1413,14 @@ def __str__(self): class PickEvent(Event): """ - A pick event, fired when the user picks a location on the canvas + A pick event. + + This event is fired when the user picks a location on the canvas sufficiently close to an artist that has been made pickable with `.Artist.set_picker`. - Attrs: all the `Event` attributes plus + A PickEvent has a number of special attributes in addition to those defined + by the parent `Event` class. Attributes ---------- @@ -1482,8 +1431,8 @@ class PickEvent(Event): (see `.Artist.set_picker`). other Additional attributes may be present depending on the type of the - picked object; e.g., a `~.Line2D` pick may define different extra - attributes than a `~.PatchCollection` pick. + picked object; e.g., a `.Line2D` pick may define different extra + attributes than a `.PatchCollection` pick. Examples -------- @@ -1500,8 +1449,11 @@ def on_pick(event): cid = fig.canvas.mpl_connect('pick_event', on_pick) """ + def __init__(self, name, canvas, mouseevent, artist, guiEvent=None, **kwargs): + if guiEvent is None: + guiEvent = mouseevent.guiEvent super().__init__(name, canvas, guiEvent) self.mouseevent = mouseevent self.artist = artist @@ -1512,19 +1464,16 @@ class KeyEvent(LocationEvent): """ A key event (key press, key release). - Attach additional attributes as defined in - :meth:`FigureCanvasBase.mpl_connect`. - - In addition to the `Event` and `LocationEvent` - attributes, the following attributes are defined: + A KeyEvent has a number of special attributes in addition to those defined + by the parent `Event` and `LocationEvent` classes. Attributes ---------- key : None or str - the key(s) pressed. Could be **None**, a single case sensitive ascii - character ("g", "G", "#", etc.), a special key - ("control", "shift", "f1", "up", etc.) or a - combination of the above (e.g., "ctrl+alt+g", "ctrl+alt+G"). + The key(s) pressed. Could be *None*, a single case sensitive Unicode + character ("g", "G", "#", etc.), a special key ("control", "shift", + "f1", "up", etc.) or a combination of the above (e.g., "ctrl+alt+g", + "ctrl+alt+G"). Notes ----- @@ -1542,16 +1491,51 @@ def on_key(event): cid = fig.canvas.mpl_connect('key_press_event', on_key) """ + def __init__(self, name, canvas, key, x=0, y=0, guiEvent=None): - self.key = key - # super-init deferred to the end: callback errors if called before super().__init__(name, canvas, x, y, guiEvent=guiEvent) + self.key = key + + +# Default callback for key events. +def _key_handler(event): + # Dead reckoning of key. + if event.name == "key_press_event": + event.canvas._key = event.key + elif event.name == "key_release_event": + event.canvas._key = None + + +# Default callback for mouse events. +def _mouse_handler(event): + # Dead-reckoning of button and key. + if event.name == "button_press_event": + event.canvas._button = event.button + elif event.name == "button_release_event": + event.canvas._button = None + elif event.name == "motion_notify_event" and event.button is None: + event.button = event.canvas._button + if event.key is None: + event.key = event.canvas._key + # Emit axes_enter/axes_leave. + if event.name == "motion_notify_event": + last = LocationEvent.lastevent + last_axes = last.inaxes if last is not None else None + if last_axes != event.inaxes: + if last_axes is not None: + try: + last.canvas.callbacks.process("axes_leave_event", last) + except Exception: + pass # The last canvas may already have been torn down. + if event.inaxes is not None: + event.canvas.callbacks.process("axes_enter_event", event) + LocationEvent.lastevent = ( + None if event.name == "figure_leave_event" else event) def _get_renderer(figure, print_method=None): """ - Get the renderer that would be used to save a `~.Figure`, and cache it on - the figure. + Get the renderer that would be used to save a `.Figure`. If you need a renderer without any active draw methods use renderer._draw_disabled to temporary patch them out at your call site. @@ -1564,35 +1548,32 @@ class Done(Exception): def _draw(renderer): raise Done(renderer) - with cbook._setattr_cm(figure, draw=_draw): - orig_canvas = figure.canvas + with cbook._setattr_cm(figure, draw=_draw), ExitStack() as stack: if print_method is None: fmt = figure.canvas.get_default_filetype() # Even for a canvas' default output type, a canvas switch may be # needed, e.g. for FigureCanvasBase. - print_method = getattr( - figure.canvas._get_output_canvas(None, fmt), f"print_{fmt}") + print_method = stack.enter_context( + figure.canvas._switch_canvas_and_return_print_method(fmt)) try: print_method(io.BytesIO()) except Done as exc: - renderer, = figure._cachedRenderer, = exc.args + renderer, = exc.args return renderer else: raise RuntimeError(f"{print_method} did not call Figure.draw, so " f"no renderer is available") - finally: - figure.canvas = orig_canvas def _no_output_draw(figure): - renderer = _get_renderer(figure) - with renderer._draw_disabled(): - figure.draw(renderer) + # _no_output_draw was promoted to the figure level, but + # keep this here in case someone was calling it... + figure.draw_without_rendering() def _is_non_interactive_terminal_ipython(ip): """ - Return whether we are in a a terminal IPython, but non interactive. + Return whether we are in a terminal IPython, but non interactive. When in _terminal_ IPython, ip.parent will have and `interact` attribute, if this attribute is False we do not setup eventloop integration as the @@ -1604,73 +1585,6 @@ def _is_non_interactive_terminal_ipython(ip): and getattr(ip.parent, 'interact', None) is False) -def _check_savefig_extra_args(func=None, extra_kwargs=()): - """ - Decorator for the final print_* methods that accept keyword arguments. - - If any unused keyword arguments are left, this decorator will warn about - them, and as part of the warning, will attempt to specify the function that - the user actually called, instead of the backend-specific method. If unable - to determine which function the user called, it will specify `.savefig`. - - For compatibility across backends, this does not warn about keyword - arguments added by `FigureCanvasBase.print_figure` for use in a subset of - backends, because the user would not have added them directly. - """ - - if func is None: - return functools.partial(_check_savefig_extra_args, - extra_kwargs=extra_kwargs) - - old_sig = inspect.signature(func) - - @functools.wraps(func) - def wrapper(*args, **kwargs): - name = 'savefig' # Reasonable default guess. - public_api = re.compile( - r'^savefig|print_[A-Za-z0-9]+|_no_output_draw$' - ) - seen_print_figure = False - for frame, line in traceback.walk_stack(None): - if frame is None: - # when called in embedded context may hit frame is None. - break - if re.match(r'\A(matplotlib|mpl_toolkits)(\Z|\.(?!tests\.))', - # Work around sphinx-gallery not setting __name__. - frame.f_globals.get('__name__', '')): - if public_api.match(frame.f_code.co_name): - name = frame.f_code.co_name - if name in ('print_figure', '_no_output_draw'): - seen_print_figure = True - - else: - break - - accepted_kwargs = {*old_sig.parameters, *extra_kwargs} - if seen_print_figure: - for kw in ['dpi', 'facecolor', 'edgecolor', 'orientation', - 'bbox_inches_restore']: - # Ignore keyword arguments that are passed in by print_figure - # for the use of other renderers. - if kw not in accepted_kwargs: - kwargs.pop(kw, None) - - for arg in list(kwargs): - if arg in accepted_kwargs: - continue - _api.warn_deprecated( - '3.3', name=name, - message='%(name)s() got unexpected keyword argument "' - + arg + '" which is no longer supported as of ' - '%(since)s and will become an error ' - '%(removal)s') - kwargs.pop(arg) - - return func(*args, **kwargs) - - return wrapper - - class FigureCanvasBase: """ The canvas the figure renders into. @@ -1681,10 +1595,17 @@ class FigureCanvasBase: A high-level figure instance. """ - # Set to one of {"qt5", "qt4", "gtk3", "wx", "tk", "macosx"} if an + # Set to one of {"qt", "gtk3", "gtk4", "wx", "tk", "macosx"} if an # interactive framework is required, or None otherwise. required_interactive_framework = None + # The manager class instantiated by new_manager. + # (This is defined as a classproperty because the manager class is + # currently defined *after* the canvas class, but one could also assign + # ``FigureCanvasBase.manager_class = FigureManagerBase`` + # after defining both classes.) + manager_class = _api.classproperty(lambda cls: FigureManagerBase) + events = [ 'resize_event', 'draw_event', @@ -1726,9 +1647,13 @@ def __init__(self, figure=None): self._button = None # the button pressed self._key = None # the key pressed self._lastx, self._lasty = None, None - self.mouse_grabber = None # the axes currently grabbing mouse + self.mouse_grabber = None # the Axes currently grabbing mouse self.toolbar = None # NavigationToolbar2 will set me self._is_idle_drawing = False + # We don't want to scale up the figure DPI more than once. + figure._original_dpi = figure.dpi + self._device_pixel_ratio = 1 + super().__init__() # Typically the GUI widget init (if any). callbacks = property(lambda self: self.figure._canvas_callbacks) button_pick_id = property(lambda self: self.figure._button_pick_id) @@ -1753,13 +1678,30 @@ def _fix_ipython_backend2gui(cls): # In case we ever move the patch to IPython and remove these APIs, # don't break on our side. return - rif = getattr(cls, "required_interactive_framework", None) - backend2gui_rif = {"qt5": "qt", "qt4": "qt", "gtk3": "gtk3", - "wx": "wx", "macosx": "osx"}.get(rif) + backend2gui_rif = { + "qt": "qt", + "gtk3": "gtk3", + "gtk4": "gtk4", + "wx": "wx", + "macosx": "osx", + }.get(cls.required_interactive_framework) if backend2gui_rif: if _is_non_interactive_terminal_ipython(ip): ip.enable_gui(backend2gui_rif) + @classmethod + def new_manager(cls, figure, num): + """ + Create a new figure manager for *figure*, using this canvas class. + + Notes + ----- + This method should not be reimplemented in subclasses. If + custom manager creation logic is needed, please reimplement + ``FigureManager.create_with_canvas``. + """ + return cls.manager_class.create_with_canvas(cls, figure, num) + @contextmanager def _idle_draw_cntx(self): self._is_idle_drawing = True @@ -1775,6 +1717,7 @@ def is_saving(self): """ return self._is_saving + @_api.deprecated("3.6", alternative="canvas.figure.pick") def pick(self, mouseevent): if not self.widgetlock.locked(): self.figure.pick(mouseevent) @@ -1783,14 +1726,30 @@ def blit(self, bbox=None): """Blit the canvas in bbox (default entire canvas).""" def resize(self, w, h): - """Set the canvas size in pixels.""" + """ + UNUSED: Set the canvas size in pixels. + + Certain backends may implement a similar method internally, but this is + not a requirement of, nor is it used by, Matplotlib itself. + """ + # The entire method is actually deprecated, but we allow pass-through + # to a parent class to support e.g. QWidget.resize. + if hasattr(super(), "resize"): + return super().resize(w, h) + else: + _api.warn_deprecated("3.6", name="resize", obj_type="method", + alternative="FigureManagerBase.resize") + @_api.deprecated("3.6", alternative=( + "callbacks.process('draw_event', DrawEvent(...))")) def draw_event(self, renderer): """Pass a `DrawEvent` to all functions connected to ``draw_event``.""" s = 'draw_event' event = DrawEvent(s, self, renderer) self.callbacks.process(s, event) + @_api.deprecated("3.6", alternative=( + "callbacks.process('resize_event', ResizeEvent(...))")) def resize_event(self): """ Pass a `ResizeEvent` to all functions connected to ``resize_event``. @@ -1800,6 +1759,8 @@ def resize_event(self): self.callbacks.process(s, event) self.draw_idle() + @_api.deprecated("3.6", alternative=( + "callbacks.process('close_event', CloseEvent(...))")) def close_event(self, guiEvent=None): """ Pass a `CloseEvent` to all functions connected to ``close_event``. @@ -1816,6 +1777,8 @@ def close_event(self, guiEvent=None): # AttributeError occurs on OSX with qt4agg upon exiting # with an open window; 'callbacks' attribute no longer exists. + @_api.deprecated("3.6", alternative=( + "callbacks.process('key_press_event', KeyEvent(...))")) def key_press_event(self, key, guiEvent=None): """ Pass a `KeyEvent` to all functions connected to ``key_press_event``. @@ -1826,6 +1789,8 @@ def key_press_event(self, key, guiEvent=None): s, self, key, self._lastx, self._lasty, guiEvent=guiEvent) self.callbacks.process(s, event) + @_api.deprecated("3.6", alternative=( + "callbacks.process('key_release_event', KeyEvent(...))")) def key_release_event(self, key, guiEvent=None): """ Pass a `KeyEvent` to all functions connected to ``key_release_event``. @@ -1836,6 +1801,8 @@ def key_release_event(self, key, guiEvent=None): self.callbacks.process(s, event) self._key = None + @_api.deprecated("3.6", alternative=( + "callbacks.process('pick_event', PickEvent(...))")) def pick_event(self, mouseevent, artist, **kwargs): """ Callback processing for pick events. @@ -1852,6 +1819,8 @@ def pick_event(self, mouseevent, artist, **kwargs): **kwargs) self.callbacks.process(s, event) + @_api.deprecated("3.6", alternative=( + "callbacks.process('scroll_event', MouseEvent(...))")) def scroll_event(self, x, y, step, guiEvent=None): """ Callback processing for scroll events. @@ -1872,6 +1841,8 @@ def scroll_event(self, x, y, step, guiEvent=None): step=step, guiEvent=guiEvent) self.callbacks.process(s, mouseevent) + @_api.deprecated("3.6", alternative=( + "callbacks.process('button_press_event', MouseEvent(...))")) def button_press_event(self, x, y, button, dblclick=False, guiEvent=None): """ Callback processing for mouse button press events. @@ -1889,6 +1860,8 @@ def button_press_event(self, x, y, button, dblclick=False, guiEvent=None): dblclick=dblclick, guiEvent=guiEvent) self.callbacks.process(s, mouseevent) + @_api.deprecated("3.6", alternative=( + "callbacks.process('button_release_event', MouseEvent(...))")) def button_release_event(self, x, y, button, guiEvent=None): """ Callback processing for mouse button release events. @@ -1913,6 +1886,9 @@ def button_release_event(self, x, y, button, guiEvent=None): self.callbacks.process(s, event) self._button = None + # Also remove _lastx, _lasty when this goes away. + @_api.deprecated("3.6", alternative=( + "callbacks.process('motion_notify_event', MouseEvent(...))")) def motion_notify_event(self, x, y, guiEvent=None): """ Callback processing for mouse movement events. @@ -1938,6 +1914,8 @@ def motion_notify_event(self, x, y, guiEvent=None): guiEvent=guiEvent) self.callbacks.process(s, event) + @_api.deprecated("3.6", alternative=( + "callbacks.process('leave_notify_event', LocationEvent(...))")) def leave_notify_event(self, guiEvent=None): """ Callback processing for the mouse cursor leaving the canvas. @@ -1954,6 +1932,8 @@ def leave_notify_event(self, guiEvent=None): LocationEvent.lastevent = None self._lastx, self._lasty = None, None + @_api.deprecated("3.6", alternative=( + "callbacks.process('enter_notify_event', LocationEvent(...))")) def enter_notify_event(self, guiEvent=None, xy=None): """ Callback processing for the mouse cursor entering the canvas. @@ -1995,7 +1975,8 @@ def inaxes(self, xy): Returns ------- `~matplotlib.axes.Axes` or None - The topmost visible axes containing the point, or None if no axes. + The topmost visible Axes containing the point, or None if there + is no Axes at the point. """ axes_list = [a for a in self.figure.get_axes() if a.patch.contains_point(xy) and a.get_visible()] @@ -2011,7 +1992,7 @@ def grab_mouse(self, ax): Set the child `~.axes.Axes` which is grabbing the mouse events. Usually called by the widgets themselves. It is an error to call this - if the mouse is already grabbed by another axes. + if the mouse is already grabbed by another Axes. """ if self.mouse_grabber not in (None, ax): raise RuntimeError("Another Axes already grabs mouse input") @@ -2027,14 +2008,32 @@ def release_mouse(self, ax): if self.mouse_grabber is ax: self.mouse_grabber = None + def set_cursor(self, cursor): + """ + Set the current cursor. + + This may have no effect if the backend does not display anything. + + If required by the backend, this method should trigger an update in + the backend event loop after the cursor is set, as this method may be + called e.g. before a long-running task during which the GUI is not + updated. + + Parameters + ---------- + cursor : `.Cursors` + The cursor to display over the canvas. Note: some backends may + change the cursor for the entire window. + """ + def draw(self, *args, **kwargs): """ Render the `.Figure`. - It is important that this method actually walk the artist tree - even if not output is produced because this will trigger - deferred work (like computing limits auto-limits and tick - values) that users may want access to before saving to disk. + This method must walk the artist tree, even if no output is produced, + because it triggers deferred work that users may want to access + before saving output to disk. For example computing limits, + auto-limits, and tick values. """ def draw_idle(self, *args, **kwargs): @@ -2054,12 +2053,80 @@ def draw_idle(self, *args, **kwargs): with self._idle_draw_cntx(): self.draw(*args, **kwargs) - def get_width_height(self): + @property + def device_pixel_ratio(self): + """ + The ratio of physical to logical pixels used for the canvas on screen. + + By default, this is 1, meaning physical and logical pixels are the same + size. Subclasses that support High DPI screens may set this property to + indicate that said ratio is different. All Matplotlib interaction, + unless working directly with the canvas, remains in logical pixels. + + """ + return self._device_pixel_ratio + + def _set_device_pixel_ratio(self, ratio): """ - Return the figure width and height in points or pixels - (depending on the backend), truncated to integers. + Set the ratio of physical to logical pixels used for the canvas. + + Subclasses that support High DPI screens can set this property to + indicate that said ratio is different. The canvas itself will be + created at the physical size, while the client side will use the + logical size. Thus the DPI of the Figure will change to be scaled by + this ratio. Implementations that support High DPI screens should use + physical pixels for events so that transforms back to Axes space are + correct. + + By default, this is 1, meaning physical and logical pixels are the same + size. + + Parameters + ---------- + ratio : float + The ratio of logical to physical pixels used for the canvas. + + Returns + ------- + bool + Whether the ratio has changed. Backends may interpret this as a + signal to resize the window, repaint the canvas, or change any + other relevant properties. + """ + if self._device_pixel_ratio == ratio: + return False + # In cases with mixed resolution displays, we need to be careful if the + # device pixel ratio changes - in this case we need to resize the + # canvas accordingly. Some backends provide events that indicate a + # change in DPI, but those that don't will update this before drawing. + dpi = ratio * self.figure._original_dpi + self.figure._set_dpi(dpi, forward=False) + self._device_pixel_ratio = ratio + return True + + def get_width_height(self, *, physical=False): """ - return int(self.figure.bbox.width), int(self.figure.bbox.height) + Return the figure width and height in integral points or pixels. + + When the figure is used on High DPI screens (and the backend supports + it), the truncation to integers occurs after scaling by the device + pixel ratio. + + Parameters + ---------- + physical : bool, default: False + Whether to return true physical pixels or logical pixels. Physical + pixels may be used by backends that support HiDPI, but still + configure the canvas using its actual size. + + Returns + ------- + width, height : int + The size of the figure, in points or pixels, depending on the + backend. + """ + return tuple(int(size / (1 if physical else self.device_pixel_ratio)) + for size in self.figure.bbox.max) @classmethod def get_supported_filetypes(cls): @@ -2080,21 +2147,30 @@ def get_supported_filetypes_grouped(cls): groupings[name].sort() return groupings - def _get_output_canvas(self, backend, fmt): + @contextmanager + def _switch_canvas_and_return_print_method(self, fmt, backend=None): """ - Set the canvas in preparation for saving the figure. + Context manager temporarily setting the canvas for saving the figure:: + + with canvas._switch_canvas_and_return_print_method(fmt, backend) \\ + as print_method: + # ``print_method`` is a suitable ``print_{fmt}`` method, and + # the figure's canvas is temporarily switched to the method's + # canvas within the with... block. ``print_method`` is also + # wrapped to suppress extra kwargs passed by ``print_figure``. Parameters ---------- - backend : str or None - If not None, switch the figure canvas to the ``FigureCanvas`` class - of the given backend. fmt : str If *backend* is None, then determine a suitable canvas class for saving to format *fmt* -- either the current canvas class, if it supports *fmt*, or whatever `get_registered_canvas_class` returns; switch the figure canvas to that canvas class. + backend : str or None, default: None + If not None, switch the figure canvas to the ``FigureCanvas`` class + of the given backend. """ + canvas = None if backend is not None: # Return a specific canvas class, if requested. canvas_class = ( @@ -2105,16 +2181,34 @@ def _get_output_canvas(self, backend, fmt): f"The {backend!r} backend does not support {fmt} output") elif hasattr(self, f"print_{fmt}"): # Return the current canvas if it supports the requested format. - return self + canvas = self + canvas_class = None # Skip call to switch_backends. else: # Return a default canvas for the requested format, if it exists. canvas_class = get_registered_canvas_class(fmt) if canvas_class: - return self.switch_backends(canvas_class) - # Else report error for unsupported format. - raise ValueError( - "Format {!r} is not supported (supported formats: {})" - .format(fmt, ", ".join(sorted(self.get_supported_filetypes())))) + canvas = self.switch_backends(canvas_class) + if canvas is None: + raise ValueError( + "Format {!r} is not supported (supported formats: {})".format( + fmt, ", ".join(sorted(self.get_supported_filetypes())))) + meth = getattr(canvas, f"print_{fmt}") + mod = (meth.func.__module__ + if hasattr(meth, "func") # partialmethod, e.g. backend_wx. + else meth.__module__) + if mod.startswith(("matplotlib.", "mpl_toolkits.")): + optional_kws = { # Passed by print_figure for other renderers. + "dpi", "facecolor", "edgecolor", "orientation", + "bbox_inches_restore"} + skip = optional_kws - {*inspect.signature(meth).parameters} + print_method = functools.wraps(meth)(lambda *args, **kwargs: meth( + *args, **{k: v for k, v in kwargs.items() if k not in skip})) + else: # Let third-parties do as they see fit. + print_method = meth + try: + yield print_method + finally: + self.figure.canvas = self def print_figure( self, filename, dpi=None, facecolor=None, edgecolor=None, @@ -2182,10 +2276,6 @@ def print_figure( filename = filename.rstrip('.') + '.' + format format = format.lower() - # get canvas object and print method for format - canvas = self._get_output_canvas(backend, format) - print_method = getattr(canvas, 'print_%s' % format) - if dpi is None: dpi = rcParams['savefig.dpi'] if dpi == 'figure': @@ -2193,27 +2283,24 @@ def print_figure( # Remove the figure manager, if any, to avoid resizing the GUI widget. with cbook._setattr_cm(self, manager=None), \ - cbook._setattr_cm(self.figure, dpi=dpi), \ - cbook._setattr_cm(canvas, _is_saving=True): - origfacecolor = self.figure.get_facecolor() - origedgecolor = self.figure.get_edgecolor() - - if facecolor is None: - facecolor = rcParams['savefig.facecolor'] - if cbook._str_equal(facecolor, 'auto'): - facecolor = origfacecolor - if edgecolor is None: - edgecolor = rcParams['savefig.edgecolor'] - if cbook._str_equal(edgecolor, 'auto'): - edgecolor = origedgecolor - - self.figure.set_facecolor(facecolor) - self.figure.set_edgecolor(edgecolor) + self._switch_canvas_and_return_print_method(format, backend) \ + as print_method, \ + cbook._setattr_cm(self.figure, dpi=dpi), \ + cbook._setattr_cm(self.figure.canvas, _device_pixel_ratio=1), \ + cbook._setattr_cm(self.figure.canvas, _is_saving=True), \ + ExitStack() as stack: + + for prop in ["facecolor", "edgecolor"]: + color = locals()[prop] + if color is None: + color = rcParams[f"savefig.{prop}"] + if not cbook._str_equal(color, "auto"): + stack.enter_context(self.figure._cm_set(**{prop: color})) if bbox_inches is None: bbox_inches = rcParams['savefig.bbox'] - if (self.figure.get_constrained_layout() or + if (self.figure.get_layout_engine() is not None or bbox_inches == "tight"): # we need to trigger a draw before printing to make sure # CL works. "tight" also needs a draw to get the right @@ -2223,10 +2310,7 @@ def print_figure( functools.partial( print_method, orientation=orientation) ) - ctx = (renderer._draw_disabled() - if hasattr(renderer, '_draw_disabled') - else suppress()) - with ctx: + with getattr(renderer, "_draw_disabled", nullcontext)(): self.figure.draw(renderer) if bbox_inches: @@ -2238,16 +2322,15 @@ def print_figure( bbox_inches = bbox_inches.padded(pad_inches) # call adjust_bbox to save only the given area - restore_bbox = tight_bbox.adjust_bbox(self.figure, bbox_inches, - canvas.fixed_dpi) + restore_bbox = _tight_bbox.adjust_bbox( + self.figure, bbox_inches, self.figure.canvas.fixed_dpi) _bbox_inches_restore = (bbox_inches, restore_bbox) else: _bbox_inches_restore = None - # we have already done CL above, so turn it off: - cl_state = self.figure.get_constrained_layout() - self.figure.set_constrained_layout(False) + # we have already done layout above, so turn it off: + stack.enter_context(self.figure._cm_set(layout_engine='none')) try: # _get_renderer may change the figure dpi (as vector formats # force the figure dpi to 72), so we need to set it again here. @@ -2263,11 +2346,6 @@ def print_figure( if bbox_inches and restore_bbox: restore_bbox() - self.figure.set_facecolor(origfacecolor) - self.figure.set_edgecolor(origedgecolor) - self.figure.set_canvas(self) - # reset to cached state - self.figure.set_constrained_layout(cl_state) return result @classmethod @@ -2281,26 +2359,6 @@ def get_default_filetype(cls): """ return rcParams['savefig.format'] - @_api.deprecated( - "3.4", alternative="manager.get_window_title or GUI-specific methods") - def get_window_title(self): - """ - Return the title text of the window containing the figure, or None - if there is no window (e.g., a PS backend). - """ - if self.manager is not None: - return self.manager.get_window_title() - - @_api.deprecated( - "3.4", alternative="manager.set_window_title or GUI-specific methods") - def set_window_title(self, title): - """ - Set the title text of the window containing the figure. Note that - this has no effect if there is no window (e.g., a PS backend). - """ - if self.manager is not None: - self.manager.set_window_title(title) - def get_default_filename(self): """ Return a string, which includes extension, suitable for use as @@ -2357,7 +2415,7 @@ def mpl_connect(self, s, func): def func(event: Event) -> Any For the location events (button and key press/release), if the - mouse is over the axes, the ``inaxes`` attribute of the event will + mouse is over the Axes, the ``inaxes`` attribute of the event will be set to the `~matplotlib.axes.Axes` the event occurs is over, and additionally, the variables ``xdata`` and ``ydata`` attributes will be set to the mouse location in data coordinates. See `.KeyEvent` @@ -2487,7 +2545,7 @@ def key_press_handler(event, canvas=None, toolbar=None): back-compatibility, but, if set, should always be equal to ``event.canvas.toolbar``. """ - # these bindings happen whether you are over an axes or not + # these bindings happen whether you are over an Axes or not if event.key is None: return @@ -2510,7 +2568,6 @@ def key_press_handler(event, canvas=None, toolbar=None): grid_minor_keys = rcParams['keymap.grid_minor'] toggle_yscale_keys = rcParams['keymap.yscale'] toggle_xscale_keys = rcParams['keymap.xscale'] - all_keys = dict.__getitem__(rcParams, 'keymap.all_axes') # toggle fullscreen mode ('f', 'ctrl + f') if event.key in fullscreen_keys: @@ -2551,7 +2608,7 @@ def key_press_handler(event, canvas=None, toolbar=None): if event.inaxes is None: return - # these bindings require the mouse to be over an axes to trigger + # these bindings require the mouse to be over an Axes to trigger def _get_uniform_gridstate(ticks): # Return True/False if all grid lines are on or off, None if they are # not all in the same state. @@ -2563,7 +2620,7 @@ def _get_uniform_gridstate(ticks): return None ax = event.inaxes - # toggle major grids in current axes (default key 'g') + # toggle major grids in current Axes (default key 'g') # Both here and below (for 'G'), we do nothing if *any* grid (major or # minor, x or y) is not in a uniform state, to avoid messing up user # customization. @@ -2585,7 +2642,7 @@ def _get_uniform_gridstate(ticks): ax.grid(x_state, which="major" if x_state else "both", axis="x") ax.grid(y_state, which="major" if y_state else "both", axis="y") canvas.draw_idle() - # toggle major and minor grids in current axes (default key 'G') + # toggle major and minor grids in current Axes (default key 'G') if (event.key in grid_minor_keys # Exclude major grids not in a uniform state. and None not in [_get_uniform_gridstate(ax.xaxis.majorTicks), @@ -2629,29 +2686,6 @@ def _get_uniform_gridstate(ticks): _log.warning(str(exc)) ax.set_xscale('linear') ax.figure.canvas.draw_idle() - # enable navigation for all axes that contain the event (default key 'a') - elif event.key in all_keys: - for a in canvas.figure.get_axes(): - if (event.x is not None and event.y is not None - and a.in_axes(event)): # FIXME: Why only these? - _api.warn_deprecated( - "3.3", message="Toggling axes navigation from the " - "keyboard is deprecated since %(since)s and will be " - "removed %(removal)s.") - a.set_navigate(True) - # enable navigation only for axes with this index (if such an axes exist, - # otherwise do nothing) - elif event.key.isdigit() and event.key != '0': - n = int(event.key) - 1 - if n < len(canvas.figure.get_axes()): - for i, a in enumerate(canvas.figure.get_axes()): - if (event.x is not None and event.y is not None - and a.in_axes(event)): # FIXME: Why only these? - _api.warn_deprecated( - "3.3", message="Toggling axes navigation from the " - "keyboard is deprecated since %(since)s and will be " - "removed %(removal)s.") - a.set_navigate(i == n) def button_press_handler(event, canvas=None, toolbar=None): @@ -2737,7 +2771,8 @@ class FigureManagerBase: figure.canvas.manager.button_press_handler_id) """ - statusbar = _api.deprecated("3.3")(property(lambda self: None)) + _toolbar2_class = None + _toolmanager_toolbar_class = None def __init__(self, canvas, num): self.canvas = canvas @@ -2756,14 +2791,36 @@ def __init__(self, canvas, num): self.toolmanager = (ToolManager(canvas.figure) if mpl.rcParams['toolbar'] == 'toolmanager' else None) - self.toolbar = None + if (mpl.rcParams["toolbar"] == "toolbar2" + and self._toolbar2_class): + self.toolbar = self._toolbar2_class(self.canvas) + elif (mpl.rcParams["toolbar"] == "toolmanager" + and self._toolmanager_toolbar_class): + self.toolbar = self._toolmanager_toolbar_class(self.toolmanager) + else: + self.toolbar = None + + if self.toolmanager: + tools.add_tools_to_manager(self.toolmanager) + if self.toolbar: + tools.add_tools_to_container(self.toolbar) @self.canvas.figure.add_axobserver def notify_axes_change(fig): - # Called whenever the current axes is changed. + # Called whenever the current Axes is changed. if self.toolmanager is None and self.toolbar is not None: self.toolbar.update() + @classmethod + def create_with_canvas(cls, canvas_class, figure, num): + """ + Create a manager for a given *figure* using a specific *canvas_class*. + + Backends should override this method if they have specific needs for + setting up the canvas or the manager. + """ + return cls(canvas_class(figure), num) + def show(self): """ For GUI backends, show the figure window and redraw. @@ -2789,24 +2846,7 @@ def full_screen_toggle(self): pass def resize(self, w, h): - """For GUI backends, resize the window (in pixels).""" - - @_api.deprecated( - "3.4", alternative="self.canvas.callbacks.process(event.name, event)") - def key_press(self, event): - """ - Implement the default Matplotlib key bindings defined at - :ref:`key-event-handling`. - """ - if rcParams['toolbar'] != 'toolmanager': - key_press_handler(event) - - @_api.deprecated( - "3.4", alternative="self.canvas.callbacks.process(event.name, event)") - def button_press(self, event): - """The default Matplotlib button actions for extra mouse buttons.""" - if rcParams['toolbar'] != 'toolmanager': - button_press_handler(event) + """For GUI backends, resize the window (in physical pixels).""" def get_window_title(self): """ @@ -2852,9 +2892,6 @@ class NavigationToolbar2: :meth:`save_figure` save the current figure - :meth:`set_cursor` - if you want the pointer icon to change - :meth:`draw_rubberband` (optional) draw the zoom to rect "rubberband" rectangle @@ -2890,8 +2927,7 @@ class NavigationToolbar2: 'Left button pans, Right button zooms\n' 'x/y fixes axis, CTRL fixes aspect', 'move', 'pan'), - ('Zoom', 'Zoom to rectangle\nx/y fixes axis, CTRL fixes aspect', - 'zoom_to_rect', 'zoom'), + ('Zoom', 'Zoom to rectangle\nx/y fixes axis', 'zoom_to_rect', 'zoom'), ('Subplots', 'Configure subplots', 'subplots', 'configure_subplots'), (None, None, None, None), ('Save', 'Save the figure', 'filesave', 'save_figure'), @@ -2902,15 +2938,7 @@ def __init__(self, canvas): canvas.toolbar = self self._nav_stack = cbook.Stack() # This cursor will be set after the initial draw. - self._lastCursor = cursors.POINTER - - init = _api.deprecate_method_override( - __class__._init_toolbar, self, allow_empty=True, since="3.3", - addendum="Please fully initialize the toolbar in your subclass' " - "__init__; a fully empty _init_toolbar implementation may be kept " - "for compatibility with earlier versions of Matplotlib.") - if init: - init() + self._last_cursor = tools.Cursors.POINTER self._id_press = self.canvas.mpl_connect( 'button_press_event', self._zoom_pan_handler) @@ -2973,46 +3001,22 @@ def forward(self, *args): self.set_history_buttons() self._update_view() - @_api.deprecated("3.3", alternative="__init__") - def _init_toolbar(self): - """ - This is where you actually build the GUI widgets (called by - __init__). The icons ``home.xpm``, ``back.xpm``, ``forward.xpm``, - ``hand.xpm``, ``zoom_to_rect.xpm`` and ``filesave.xpm`` are standard - across backends (there are ppm versions in CVS also). - - You just need to set the callbacks - - home : self.home - back : self.back - forward : self.forward - hand : self.pan - zoom_to_rect : self.zoom - filesave : self.save_figure - - You only need to define the last one - the others are in the base - class implementation. - - """ - raise NotImplementedError - def _update_cursor(self, event): """ Update the cursor after a mouse move event or a tool (de)activation. """ - if not event.inaxes or not self.mode: - if self._lastCursor != cursors.POINTER: - self.set_cursor(cursors.POINTER) - self._lastCursor = cursors.POINTER - else: + if self.mode and event.inaxes and event.inaxes.get_navigate(): if (self.mode == _Mode.ZOOM - and self._lastCursor != cursors.SELECT_REGION): - self.set_cursor(cursors.SELECT_REGION) - self._lastCursor = cursors.SELECT_REGION + and self._last_cursor != tools.Cursors.SELECT_REGION): + self.canvas.set_cursor(tools.Cursors.SELECT_REGION) + self._last_cursor = tools.Cursors.SELECT_REGION elif (self.mode == _Mode.PAN - and self._lastCursor != cursors.MOVE): - self.set_cursor(cursors.MOVE) - self._lastCursor = cursors.MOVE + and self._last_cursor != tools.Cursors.MOVE): + self.canvas.set_cursor(tools.Cursors.MOVE) + self._last_cursor = tools.Cursors.MOVE + elif self._last_cursor != tools.Cursors.POINTER: + self.canvas.set_cursor(tools.Cursors.POINTER) + self._last_cursor = tools.Cursors.POINTER @contextmanager def _wait_cursor_for_draw_cm(self): @@ -3029,10 +3033,10 @@ def _wait_cursor_for_draw_cm(self): time.time(), getattr(self, "_draw_time", -np.inf)) if self._draw_time - last_draw_time > 1: try: - self.set_cursor(cursors.WAIT) + self.canvas.set_cursor(tools.Cursors.WAIT) yield finally: - self.set_cursor(self._lastCursor) + self.canvas.set_cursor(self._last_cursor) else: yield @@ -3078,20 +3082,15 @@ def _zoom_pan_handler(self, event): elif event.name == "button_release_event": self.release_zoom(event) - @_api.deprecated("3.3") - def press(self, event): - """Called whenever a mouse button is pressed.""" - - @_api.deprecated("3.3") - def release(self, event): - """Callback for mouse button release.""" - def pan(self, *args): """ Toggle the pan/zoom tool. Pan with left button, zoom with right. """ + if not self.canvas.widgetlock.available(self): + self.set_message("pan unavailable") + return if self.mode == _Mode.PAN: self.mode = _Mode.NONE self.canvas.widgetlock.release(self) @@ -3121,12 +3120,6 @@ def press_pan(self, event): id_drag = self.canvas.mpl_connect("motion_notify_event", self.drag_pan) self._pan_info = self._PanInfo( button=event.button, axes=axes, cid=id_drag) - press = _api.deprecate_method_override( - __class__.press, self, since="3.3", message="Calling an " - "overridden press() at pan start is deprecated since %(since)s " - "and will be removed %(removal)s; override press_pan() instead.") - if press is not None: - press(event) def drag_pan(self, event): """Callback for dragging in pan/zoom mode.""" @@ -3145,17 +3138,14 @@ def release_pan(self, event): 'motion_notify_event', self.mouse_move) for ax in self._pan_info.axes: ax.end_pan() - release = _api.deprecate_method_override( - __class__.press, self, since="3.3", message="Calling an " - "overridden release() at pan stop is deprecated since %(since)s " - "and will be removed %(removal)s; override release_pan() instead.") - if release is not None: - release(event) - self._draw() + self.canvas.draw_idle() self._pan_info = None self.push_current() def zoom(self, *args): + if not self.canvas.widgetlock.available(self): + self.set_message("zoom unavailable") + return """Toggle zoom to rect mode.""" if self.mode == _Mode.ZOOM: self.mode = _Mode.NONE @@ -3167,7 +3157,7 @@ def zoom(self, *args): a.set_navigate_mode(self.mode._navigate_mode) self.set_message(self.mode) - _ZoomInfo = namedtuple("_ZoomInfo", "direction start_xy axes cid") + _ZoomInfo = namedtuple("_ZoomInfo", "direction start_xy axes cid cbar") def press_zoom(self, event): """Callback for mouse button press in zoom to rect mode.""" @@ -3182,15 +3172,16 @@ def press_zoom(self, event): self.push_current() # set the home button to this view id_zoom = self.canvas.mpl_connect( "motion_notify_event", self.drag_zoom) + # A colorbar is one-dimensional, so we extend the zoom rectangle out + # to the edge of the Axes bbox in the other dimension. To do that we + # store the orientation of the colorbar for later. + if hasattr(axes[0], "_colorbar"): + cbar = axes[0]._colorbar.orientation + else: + cbar = None self._zoom_info = self._ZoomInfo( direction="in" if event.button == 1 else "out", - start_xy=(event.x, event.y), axes=axes, cid=id_zoom) - press = _api.deprecate_method_override( - __class__.press, self, since="3.3", message="Calling an " - "overridden press() at zoom start is deprecated since %(since)s " - "and will be removed %(removal)s; override press_zoom() instead.") - if press is not None: - press(event) + start_xy=(event.x, event.y), axes=axes, cid=id_zoom, cbar=cbar) def drag_zoom(self, event): """Callback for dragging in zoom mode.""" @@ -3198,10 +3189,17 @@ def drag_zoom(self, event): ax = self._zoom_info.axes[0] (x1, y1), (x2, y2) = np.clip( [start_xy, [event.x, event.y]], ax.bbox.min, ax.bbox.max) - if event.key == "x": + key = event.key + # Force the key on colorbars to extend the short-axis bbox + if self._zoom_info.cbar == "horizontal": + key = "x" + elif self._zoom_info.cbar == "vertical": + key = "y" + if key == "x": y1, y2 = ax.bbox.intervaly - elif event.key == "y": + elif key == "y": x1, x2 = ax.bbox.intervalx + self.draw_rubberband(event, x1, y1, x2, y2) def release_zoom(self, event): @@ -3215,44 +3213,36 @@ def release_zoom(self, event): self.remove_rubberband() start_x, start_y = self._zoom_info.start_xy + key = event.key + # Force the key on colorbars to ignore the zoom-cancel on the + # short-axis side + if self._zoom_info.cbar == "horizontal": + key = "x" + elif self._zoom_info.cbar == "vertical": + key = "y" # Ignore single clicks: 5 pixels is a threshold that allows the user to # "cancel" a zoom action by zooming by less than 5 pixels. - if ((abs(event.x - start_x) < 5 and event.key != "y") - or (abs(event.y - start_y) < 5 and event.key != "x")): - self._draw() + if ((abs(event.x - start_x) < 5 and key != "y") or + (abs(event.y - start_y) < 5 and key != "x")): + self.canvas.draw_idle() self._zoom_info = None - release = _api.deprecate_method_override( - __class__.press, self, since="3.3", message="Calling an " - "overridden release() at zoom stop is deprecated since " - "%(since)s and will be removed %(removal)s; override " - "release_zoom() instead.") - if release is not None: - release(event) return for i, ax in enumerate(self._zoom_info.axes): - # Detect whether this axes is twinned with an earlier axes in the - # list of zoomed axes, to avoid double zooming. + # Detect whether this Axes is twinned with an earlier Axes in the + # list of zoomed Axes, to avoid double zooming. twinx = any(ax.get_shared_x_axes().joined(ax, prev) for prev in self._zoom_info.axes[:i]) twiny = any(ax.get_shared_y_axes().joined(ax, prev) for prev in self._zoom_info.axes[:i]) ax._set_view_from_bbox( (start_x, start_y, event.x, event.y), - self._zoom_info.direction, event.key, twinx, twiny) + self._zoom_info.direction, key, twinx, twiny) - self._draw() + self.canvas.draw_idle() self._zoom_info = None self.push_current() - release = _api.deprecate_method_override( - __class__.release, self, since="3.3", message="Calling an " - "overridden release() at zoom stop is deprecated since %(since)s " - "and will be removed %(removal)s; override release_zoom() " - "instead.") - if release is not None: - release(event) - def push_current(self): """Push the current view limits and position onto the stack.""" self._nav_stack.push( @@ -3264,33 +3254,10 @@ def push_current(self): for ax in self.canvas.figure.axes})) self.set_history_buttons() - @_api.deprecated("3.3", alternative="toolbar.canvas.draw_idle()") - def draw(self): - """Redraw the canvases, update the locators.""" - self._draw() - - # Can be removed once Locator.refresh() is removed, and replaced by an - # inline call to self.canvas.draw_idle(). - def _draw(self): - for a in self.canvas.figure.get_axes(): - xaxis = getattr(a, 'xaxis', None) - yaxis = getattr(a, 'yaxis', None) - locators = [] - if xaxis is not None: - locators.append(xaxis.get_major_locator()) - locators.append(xaxis.get_minor_locator()) - if yaxis is not None: - locators.append(yaxis.get_major_locator()) - locators.append(yaxis.get_minor_locator()) - - for loc in locators: - mpl.ticker._if_refresh_overridden_call_and_emit_deprec(loc) - self.canvas.draw_idle() - def _update_view(self): """ Update the viewlim and position from the view and position stack for - each axes. + each Axes. """ nav_info = self._nav_stack() if nav_info is None: @@ -3306,14 +3273,29 @@ def _update_view(self): self.canvas.draw_idle() def configure_subplots(self, *args): - plt = _safe_pyplot_import() - self.subplot_tool = plt.subplot_tool(self.canvas.figure) - self.subplot_tool.figure.canvas.manager.show() + if hasattr(self, "subplot_tool"): + self.subplot_tool.figure.canvas.manager.show() + return + # This import needs to happen here due to circular imports. + from matplotlib.figure import Figure + with mpl.rc_context({"toolbar": "none"}): # No navbar for the toolfig. + manager = type(self.canvas).new_manager(Figure(figsize=(6, 3)), -1) + manager.set_window_title("Subplot configuration tool") + tool_fig = manager.canvas.figure + tool_fig.subplots_adjust(top=0.9) + self.subplot_tool = widgets.SubplotTool(self.canvas.figure, tool_fig) + tool_fig.canvas.mpl_connect( + "close_event", lambda e: delattr(self, "subplot_tool")) + self.canvas.mpl_connect( + "close_event", lambda e: manager.destroy()) + manager.show() + return self.subplot_tool def save_figure(self, *args): """Save the current figure.""" raise NotImplementedError + @_api.deprecated("3.5", alternative="`.FigureCanvasBase.set_cursor`") def set_cursor(self, cursor): """ Set the current cursor to one of the :class:`Cursors` enums values. @@ -3323,9 +3305,10 @@ def set_cursor(self, cursor): called e.g. before a long-running task during which the GUI is not updated. """ + self.canvas.set_cursor(cursor) def update(self): - """Reset the axes stack.""" + """Reset the Axes stack.""" self._nav_stack.clear() self.set_history_buttons() @@ -3488,29 +3471,6 @@ def set_message(self, s): raise NotImplementedError -@_api.deprecated("3.3") -class StatusbarBase: - """Base class for the statusbar.""" - def __init__(self, toolmanager): - self.toolmanager = toolmanager - self.toolmanager.toolmanager_connect('tool_message_event', - self._message_cbk) - - def _message_cbk(self, event): - """Capture the 'tool_message_event' and set the message.""" - self.set_message(event.message) - - def set_message(self, s): - """ - Display a message on toolbar or in status bar. - - Parameters - ---------- - s : str - Message text. - """ - - class _Backend: # A backend can be defined by using the following pattern: # @@ -3547,9 +3507,7 @@ def new_figure_manager(cls, num, *args, **kwargs): @classmethod def new_figure_manager_given_figure(cls, num, figure): """Create a new figure manager instance for the given figure.""" - canvas = cls.FigureCanvas(figure) - manager = cls.FigureManager(canvas, num) - return manager + return cls.FigureCanvas.new_manager(figure, num) @classmethod def draw_if_interactive(cls): @@ -3578,19 +3536,12 @@ def show(cls, *, block=None): if cls.mainloop is None: return if block is None: - # Hack: Are we in IPython's pylab mode? + # Hack: Are we in IPython's %pylab mode? In pylab mode, IPython + # (>= 0.10) tacks a _needmain attribute onto pyplot.show (always + # set to False). from matplotlib import pyplot - try: - # IPython versions >= 0.10 tack the _needmain attribute onto - # pyplot.show, and always set it to False, when in %pylab mode. - ipython_pylab = not pyplot.show._needmain - except AttributeError: - ipython_pylab = False + ipython_pylab = hasattr(pyplot.show, "_needmain") block = not ipython_pylab and not is_interactive() - # TODO: The above is a hack to get the WebAgg backend working with - # ipython's `%pylab` mode until proper integration is implemented. - if get_backend() == "WebAgg": - block = True if block: cls.mainloop() diff --git a/lib/matplotlib/backend_managers.py b/lib/matplotlib/backend_managers.py index 384b453ede1c..e7dd748325e5 100644 --- a/lib/matplotlib/backend_managers.py +++ b/lib/matplotlib/backend_managers.py @@ -1,6 +1,4 @@ -from matplotlib import _api, cbook, widgets -from matplotlib.rcsetup import validate_stringlist -import matplotlib.backend_tools as tools +from matplotlib import _api, backend_tools, cbook, widgets class ToolEvent: @@ -175,8 +173,7 @@ def _remove_keys(self, name): for k in self.get_tool_keymap(name): del self._keys[k] - @_api.delete_parameter("3.3", "args") - def update_keymap(self, name, key, *args): + def update_keymap(self, name, key): """ Set the keymap to associate with the specified tool. @@ -188,23 +185,15 @@ def update_keymap(self, name, key, *args): Keys to associate with the tool. """ if name not in self._tools: - raise KeyError('%s not in Tools' % name) + raise KeyError(f'{name!r} not in Tools') self._remove_keys(name) - for key in [key, *args]: - if isinstance(key, str) and validate_stringlist(key) != [key]: - _api.warn_deprecated( - "3.3", message="Passing a list of keys as a single " - "comma-separated string is deprecated since %(since)s and " - "support will be removed %(removal)s; pass keys as a list " - "of strings instead.") - key = validate_stringlist(key) - if isinstance(key, str): - key = [key] - for k in key: - if k in self._keys: - _api.warn_external( - f'Key {k} changed from {self._keys[k]} to {name}') - self._keys[k] = name + if isinstance(key, str): + key = [key] + for k in key: + if k in self._keys: + _api.warn_external( + f'Key {k} changed from {self._keys[k]} to {name}') + self._keys[k] = name def remove_tool(self, name): """ @@ -217,17 +206,20 @@ def remove_tool(self, name): """ tool = self.get_tool(name) - tool.destroy() + destroy = _api.deprecate_method_override( + backend_tools.ToolBase.destroy, tool, since="3.6", + alternative="tool_removed_event") + if destroy is not None: + destroy() - # If is a toggle tool and toggled, untoggle + # If it's a toggle tool and toggled, untoggle if getattr(tool, 'toggled', False): self.trigger_tool(tool, 'toolmanager') self._remove_keys(name) - s = 'tool_removed_event' - event = ToolEvent(s, self, tool) - self._callbacks.process(s, event) + event = ToolEvent('tool_removed_event', self, tool) + self._callbacks.process(event.name, event) del self._tools[name] @@ -243,8 +235,9 @@ def add_tool(self, name, tool, *args, **kwargs): ---------- name : str Name of the tool, treated as the ID, has to be unique. - tool : class_like, i.e. str or type - Reference to find the class of the Tool to added. + tool : type + Class of the tool to be added. A subclass will be used + instead if one was registered for the current canvas class. Notes ----- @@ -255,7 +248,7 @@ def add_tool(self, name, tool, *args, **kwargs): matplotlib.backend_tools.ToolBase : The base class for tools. """ - tool_cls = self._get_cls_to_instantiate(tool) + tool_cls = backend_tools._find_tool_class(type(self.canvas), tool) if not tool_cls: raise ValueError('Impossible to find class for %s' % str(tool)) @@ -264,14 +257,24 @@ def add_tool(self, name, tool, *args, **kwargs): 'exists, not added') return self._tools[name] + if name == 'cursor' and tool_cls != backend_tools.SetCursorBase: + _api.warn_deprecated("3.5", + message="Overriding ToolSetCursor with " + f"{tool_cls.__qualname__} was only " + "necessary to provide the .set_cursor() " + "method, which is deprecated since " + "%(since)s and will be removed " + "%(removal)s. Please report this to the " + f"{tool_cls.__module__} author.") + tool_obj = tool_cls(self, name, *args, **kwargs) self._tools[name] = tool_obj - if tool_cls.default_keymap is not None: - self.update_keymap(name, tool_cls.default_keymap) + if tool_obj.default_keymap is not None: + self.update_keymap(name, tool_obj.default_keymap) # For toggle tools init the radio_group in self._toggled - if isinstance(tool_obj, tools.ToolToggleBase): + if isinstance(tool_obj, backend_tools.ToolToggleBase): # None group is not mutually exclusive, a set is used to keep track # of all toggled tools in this group if tool_obj.radio_group is None: @@ -281,18 +284,15 @@ def add_tool(self, name, tool, *args, **kwargs): # If initially toggled if tool_obj.toggled: - self._handle_toggle(tool_obj, None, None, None) + self._handle_toggle(tool_obj, None, None) tool_obj.set_figure(self.figure) - self._tool_added_event(tool_obj) - return tool_obj + event = ToolEvent('tool_added_event', self, tool_obj) + self._callbacks.process(event.name, event) - def _tool_added_event(self, tool): - s = 'tool_added_event' - event = ToolEvent(s, self, tool) - self._callbacks.process(s, event) + return tool_obj - def _handle_toggle(self, tool, sender, canvasevent, data): + def _handle_toggle(self, tool, canvasevent, data): """ Toggle tools, need to untoggle prior to using other Toggle tool. Called from trigger_tool. @@ -300,8 +300,6 @@ def _handle_toggle(self, tool, sender, canvasevent, data): Parameters ---------- tool : `.ToolBase` - sender : object - Object that wishes to trigger the tool. canvasevent : Event Original Canvas event or None. data : object @@ -337,23 +335,6 @@ def _handle_toggle(self, tool, sender, canvasevent, data): # Keep track of the toggled tool in the radio_group self._toggled[radio_group] = toggled - def _get_cls_to_instantiate(self, callback_class): - # Find the class that corresponds to the tool - if isinstance(callback_class, str): - # FIXME: make more complete searching structure - if callback_class in globals(): - callback_class = globals()[callback_class] - else: - mod = 'backend_tools' - current_module = __import__(mod, - globals(), locals(), [mod], 1) - - callback_class = getattr(current_module, callback_class, False) - if callable(callback_class): - return callback_class - else: - return None - def trigger_tool(self, name, sender=None, canvasevent=None, data=None): """ Trigger a tool and emit the ``tool_trigger_{name}`` event. @@ -376,23 +357,15 @@ def trigger_tool(self, name, sender=None, canvasevent=None, data=None): if sender is None: sender = self - self._trigger_tool(name, sender, canvasevent, data) + if isinstance(tool, backend_tools.ToolToggleBase): + self._handle_toggle(tool, canvasevent, data) + + tool.trigger(sender, canvasevent, data) # Actually trigger Tool. s = 'tool_trigger_%s' % name event = ToolTriggerEvent(s, sender, tool, canvasevent, data) self._callbacks.process(s, event) - def _trigger_tool(self, name, sender=None, canvasevent=None, data=None): - """Actually trigger a tool.""" - tool = self.get_tool(name) - - if isinstance(tool, tools.ToolToggleBase): - self._handle_toggle(tool, sender, canvasevent, data) - - # Important!!! - # This is where the Tool object gets triggered - tool.trigger(sender, canvasevent, data) - def _key_press(self, event): if event.key is None or self.keypresslock.locked(): return @@ -426,10 +399,12 @@ def get_tool(self, name, warn=True): `.ToolBase` or None The tool or None if no tool with the given name exists. """ - if isinstance(name, tools.ToolBase) and name.name in self._tools: + if (isinstance(name, backend_tools.ToolBase) + and name.name in self._tools): return name if name not in self._tools: if warn: - _api.warn_external(f"ToolManager does not control tool {name}") + _api.warn_external( + f"ToolManager does not control tool {name!r}") return None return self._tools[name] diff --git a/lib/matplotlib/backend_tools.py b/lib/matplotlib/backend_tools.py index a8bad1edab5a..bc13951793b2 100644 --- a/lib/matplotlib/backend_tools.py +++ b/lib/matplotlib/backend_tools.py @@ -11,7 +11,8 @@ `matplotlib.backend_managers.ToolManager` """ -from enum import IntEnum +import enum +import functools import re import time from types import SimpleNamespace @@ -25,11 +26,46 @@ from matplotlib import _api, cbook -class Cursors(IntEnum): # Must subclass int for the macOS backend. +class Cursors(enum.IntEnum): # Must subclass int for the macOS backend. """Backend-independent cursor types.""" - HAND, POINTER, SELECT_REGION, MOVE, WAIT = range(5) + POINTER = enum.auto() + HAND = enum.auto() + SELECT_REGION = enum.auto() + MOVE = enum.auto() + WAIT = enum.auto() + RESIZE_HORIZONTAL = enum.auto() + RESIZE_VERTICAL = enum.auto() cursors = Cursors # Backcompat. + +# _tool_registry, _register_tool_class, and _find_tool_class implement a +# mechanism through which ToolManager.add_tool can determine whether a subclass +# of the requested tool class has been registered (either for the current +# canvas class or for a parent class), in which case that tool subclass will be +# instantiated instead. This is the mechanism used e.g. to allow different +# GUI backends to implement different specializations for ConfigureSubplots. + + +_tool_registry = set() + + +def _register_tool_class(canvas_cls, tool_cls=None): + """Decorator registering *tool_cls* as a tool class for *canvas_cls*.""" + if tool_cls is None: + return functools.partial(_register_tool_class, canvas_cls) + _tool_registry.add((canvas_cls, tool_cls)) + return tool_cls + + +def _find_tool_class(canvas_cls, tool_cls): + """Find a subclass of *tool_cls* registered for *canvas_cls*.""" + for canvas_parent in canvas_cls.__mro__: + for tool_child in _api.recursive_subclasses(tool_cls): + if (canvas_parent, tool_child) in _tool_registry: + return tool_child + return tool_cls + + # Views positions tool _views_positions = 'viewpos' @@ -38,42 +74,33 @@ class ToolBase: """ Base tool class. - A base tool, only implements `trigger` method or not method at all. + A base tool, only implements `trigger` method or no method at all. The tool is instantiated by `matplotlib.backend_managers.ToolManager`. - - Attributes - ---------- - toolmanager : `matplotlib.backend_managers.ToolManager` - ToolManager that controls this Tool. - figure : `FigureCanvas` - Figure instance that is affected by this Tool. - name : str - Used as **Id** of the tool, has to be unique among tools of the same - ToolManager. """ default_keymap = None """ Keymap to associate with this tool. - **String**: List of comma separated keys that will be used to call this - tool when the keypress event of ``self.figure.canvas`` is emitted. + ``list[str]``: List of keys that will trigger this tool when a keypress + event is emitted on ``self.figure.canvas``. Note that this attribute is + looked up on the instance, and can therefore be a property (this is used + e.g. by the built-in tools to load the rcParams at instantiation time). """ description = None """ Description of the Tool. - **String**: If the Tool is included in the Toolbar this text is used - as a Tooltip. + `str`: Tooltip used if the Tool is included in a Toolbar. """ image = None """ Filename of the image. - **String**: Filename of the image to use in the toolbar. If None, the - *name* is used as a label in the toolbar button. + `str`: Filename of the image to use in a Toolbar. If None, the *name* is + used as a label in the toolbar button. """ def __init__(self, toolmanager, name): @@ -81,23 +108,26 @@ def __init__(self, toolmanager, name): self._toolmanager = toolmanager self._figure = None + name = property( + lambda self: self._name, + doc="The tool id (str, must be unique among tools of a tool manager).") + toolmanager = property( + lambda self: self._toolmanager, + doc="The `.ToolManager` that controls this tool.") + canvas = property( + lambda self: self._figure.canvas if self._figure is not None else None, + doc="The canvas of the figure affected by this tool, or None.") + @property def figure(self): + """The Figure affected by this tool, or None.""" return self._figure @figure.setter def figure(self, figure): - self.set_figure(figure) - - @property - def canvas(self): - if not self._figure: - return None - return self._figure.canvas + self._figure = figure - @property - def toolmanager(self): - return self._toolmanager + set_figure = figure.fset def _make_classic_style_pseudo_toolbar(self): """ @@ -108,22 +138,11 @@ def _make_classic_style_pseudo_toolbar(self): """ return SimpleNamespace(canvas=self.canvas) - def set_figure(self, figure): - """ - Assign a figure to the tool. - - Parameters - ---------- - figure : `.Figure` - """ - self._figure = figure - def trigger(self, sender, event, data=None): """ Called when this tool gets used. - This method is called by - `matplotlib.backend_managers.ToolManager.trigger_tool`. + This method is called by `.ToolManager.trigger_tool`. Parameters ---------- @@ -136,17 +155,12 @@ def trigger(self, sender, event, data=None): """ pass - @property - def name(self): - """Tool Id.""" - return self._name - + @_api.deprecated("3.6", alternative="tool_removed_event") def destroy(self): """ Destroy the tool. - This method is called when the tool is removed by - `matplotlib.backend_managers.ToolManager.remove_tool`. + This method is called by `.ToolManager.remove_tool`. """ pass @@ -167,10 +181,10 @@ class ToolToggleBase(ToolBase): """ radio_group = None - """Attribute to group 'radio' like tools (mutually exclusive). + """ + Attribute to group 'radio' like tools (mutually exclusive). - **String** that identifies the group or **None** if not belonging to a - group. + `str` that identifies the group or **None** if not belonging to a group. """ cursor = None @@ -217,7 +231,6 @@ def disable(self, event=None): @property def toggled(self): """State of the toggled tool.""" - return self._toggled def set_figure(self, figure): @@ -249,12 +262,11 @@ class SetCursorBase(ToolBase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._id_drag = None - self._cursor = None + self._current_tool = None self._default_cursor = cursors.POINTER self._last_cursor = self._default_cursor self.toolmanager.toolmanager_connect('tool_added_event', self._add_tool_cbk) - # process current tools for tool in self.toolmanager.tools.values(): self._add_tool(tool) @@ -269,10 +281,9 @@ def set_figure(self, figure): def _tool_trigger_cbk(self, event): if event.tool.toggled: - self._cursor = event.tool.cursor + self._current_tool = event.tool else: - self._cursor = None - + self._current_tool = None self._set_cursor_cbk(event.canvasevent) def _add_tool(self, tool): @@ -288,26 +299,28 @@ def _add_tool_cbk(self, event): self._add_tool(event.tool) def _set_cursor_cbk(self, event): - if not event: + if not event or not self.canvas: return - - if not getattr(event, 'inaxes', False) or not self._cursor: - if self._last_cursor != self._default_cursor: - self.set_cursor(self._default_cursor) - self._last_cursor = self._default_cursor - elif self._cursor: - cursor = self._cursor - if cursor and self._last_cursor != cursor: - self.set_cursor(cursor) - self._last_cursor = cursor - + if (self._current_tool and getattr(event, "inaxes", None) + and event.inaxes.get_navigate()): + if self._last_cursor != self._current_tool.cursor: + self.canvas.set_cursor(self._current_tool.cursor) + self._last_cursor = self._current_tool.cursor + elif self._last_cursor != self._default_cursor: + self.canvas.set_cursor(self._default_cursor) + self._last_cursor = self._default_cursor + + @_api.deprecated("3.5", alternative="`.FigureCanvasBase.set_cursor`") def set_cursor(self, cursor): """ Set the cursor. - - This method has to be implemented per backend. """ - raise NotImplementedError + self.canvas.set_cursor(cursor) + + +# This exists solely for deprecation warnings; remove with +# SetCursorBase.set_cursor. +ToolSetCursor = SetCursorBase class ToolCursorPosition(ToolBase): @@ -342,7 +355,7 @@ def send_message(self, event): class RubberbandBase(ToolBase): """Draw and remove a rubberband.""" - def trigger(self, sender, event, data): + def trigger(self, sender, event, data=None): """Call `draw_rubberband` or `remove_rubberband` based on data.""" if not self.figure.canvas.widgetlock.available(sender): return @@ -372,7 +385,7 @@ class ToolQuit(ToolBase): """Tool to call the figure manager destroy method.""" description = 'Quit the figure' - default_keymap = mpl.rcParams['keymap.quit'] + default_keymap = property(lambda self: mpl.rcParams['keymap.quit']) def trigger(self, sender, event, data=None): Gcf.destroy_fig(self.figure) @@ -382,47 +395,17 @@ class ToolQuitAll(ToolBase): """Tool to call the figure manager destroy method.""" description = 'Quit all figures' - default_keymap = mpl.rcParams['keymap.quit_all'] + default_keymap = property(lambda self: mpl.rcParams['keymap.quit_all']) def trigger(self, sender, event, data=None): Gcf.destroy_all() -class _ToolEnableAllNavigation(ToolBase): - """Tool to enable all axes for toolmanager interaction.""" - - description = 'Enable all axes toolmanager' - default_keymap = mpl.rcParams['keymap.all_axes'] - - def trigger(self, sender, event, data=None): - mpl.backend_bases.key_press_handler(event, self.figure.canvas, None) - - -@_api.deprecated("3.3") -class ToolEnableAllNavigation(_ToolEnableAllNavigation): - pass - - -class _ToolEnableNavigation(ToolBase): - """Tool to enable a specific axes for toolmanager interaction.""" - - description = 'Enable one axes toolmanager' - default_keymap = ('1', '2', '3', '4', '5', '6', '7', '8', '9') - - def trigger(self, sender, event, data=None): - mpl.backend_bases.key_press_handler(event, self.figure.canvas, None) - - -@_api.deprecated("3.3") -class ToolEnableNavigation(_ToolEnableNavigation): - pass - - class ToolGrid(ToolBase): """Tool to toggle the major grids of the figure.""" description = 'Toggle major grids' - default_keymap = mpl.rcParams['keymap.grid'] + default_keymap = property(lambda self: mpl.rcParams['keymap.grid']) def trigger(self, sender, event, data=None): sentinel = str(uuid.uuid4()) @@ -437,7 +420,7 @@ class ToolMinorGrid(ToolBase): """Tool to toggle the major and minor grids of the figure.""" description = 'Toggle major and minor grids' - default_keymap = mpl.rcParams['keymap.grid_minor'] + default_keymap = property(lambda self: mpl.rcParams['keymap.grid_minor']) def trigger(self, sender, event, data=None): sentinel = str(uuid.uuid4()) @@ -448,16 +431,13 @@ def trigger(self, sender, event, data=None): mpl.backend_bases.key_press_handler(event, self.figure.canvas) -class ToolFullScreen(ToolToggleBase): +class ToolFullScreen(ToolBase): """Tool to toggle full screen.""" description = 'Toggle fullscreen mode' - default_keymap = mpl.rcParams['keymap.fullscreen'] + default_keymap = property(lambda self: mpl.rcParams['keymap.fullscreen']) - def enable(self, event): - self.figure.canvas.manager.full_screen_toggle() - - def disable(self, event): + def trigger(self, sender, event, data=None): self.figure.canvas.manager.full_screen_toggle() @@ -469,11 +449,11 @@ def trigger(self, sender, event, data=None): return super().trigger(sender, event, data) - def enable(self, event): + def enable(self, event=None): self.set_scale(event.inaxes, 'log') self.figure.canvas.draw_idle() - def disable(self, event): + def disable(self, event=None): self.set_scale(event.inaxes, 'linear') self.figure.canvas.draw_idle() @@ -482,7 +462,7 @@ class ToolYScale(AxisScaleBase): """Tool to toggle between linear and logarithmic scales on the Y axis.""" description = 'Toggle scale Y axis' - default_keymap = mpl.rcParams['keymap.yscale'] + default_keymap = property(lambda self: mpl.rcParams['keymap.yscale']) def set_scale(self, ax, scale): ax.set_yscale(scale) @@ -492,7 +472,7 @@ class ToolXScale(AxisScaleBase): """Tool to toggle between linear and logarithmic scales on the X axis.""" description = 'Toggle scale X axis' - default_keymap = mpl.rcParams['keymap.xscale'] + default_keymap = property(lambda self: mpl.rcParams['keymap.xscale']) def set_scale(self, ax, scale): ax.set_xscale(scale) @@ -613,33 +593,6 @@ def update_home_views(self, figure=None): if a not in self.home_views[figure]: self.home_views[figure][a] = a._get_view() - @_api.deprecated("3.3", alternative="self.figure.canvas.draw_idle()") - def refresh_locators(self): - """Redraw the canvases, update the locators.""" - self._refresh_locators() - - # Can be removed once Locator.refresh() is removed, and replaced by an - # inline call to self.figure.canvas.draw_idle(). - def _refresh_locators(self): - for a in self.figure.get_axes(): - xaxis = getattr(a, 'xaxis', None) - yaxis = getattr(a, 'yaxis', None) - zaxis = getattr(a, 'zaxis', None) - locators = [] - if xaxis is not None: - locators.append(xaxis.get_major_locator()) - locators.append(xaxis.get_minor_locator()) - if yaxis is not None: - locators.append(yaxis.get_major_locator()) - locators.append(yaxis.get_minor_locator()) - if zaxis is not None: - locators.append(zaxis.get_major_locator()) - locators.append(zaxis.get_minor_locator()) - - for loc in locators: - mpl.ticker._if_refresh_overridden_call_and_emit_deprec(loc) - self.figure.canvas.draw_idle() - def home(self): """Recall the first view and position from the stack.""" self.views[self.figure].home() @@ -673,7 +626,7 @@ class ToolHome(ViewsPositionsBase): description = 'Reset original view' image = 'home' - default_keymap = mpl.rcParams['keymap.home'] + default_keymap = property(lambda self: mpl.rcParams['keymap.home']) _on_trigger = 'home' @@ -682,7 +635,7 @@ class ToolBack(ViewsPositionsBase): description = 'Back to previous view' image = 'back' - default_keymap = mpl.rcParams['keymap.back'] + default_keymap = property(lambda self: mpl.rcParams['keymap.back']) _on_trigger = 'back' @@ -691,7 +644,7 @@ class ToolForward(ViewsPositionsBase): description = 'Forward to next view' image = 'forward' - default_keymap = mpl.rcParams['keymap.forward'] + default_keymap = property(lambda self: mpl.rcParams['keymap.forward']) _on_trigger = 'forward' @@ -707,7 +660,7 @@ class SaveFigureBase(ToolBase): description = 'Save the figure' image = 'filesave' - default_keymap = mpl.rcParams['keymap.save'] + default_keymap = property(lambda self: mpl.rcParams['keymap.save']) class ZoomPanBase(ToolToggleBase): @@ -723,7 +676,7 @@ def __init__(self, *args): self.scrollthresh = .5 # .5 second scroll threshold self.lastscroll = time.time()-self.scrollthresh - def enable(self, event): + def enable(self, event=None): """Connect press/release events and lock the canvas.""" self.figure.canvas.widgetlock(self) self._idPress = self.figure.canvas.mpl_connect( @@ -733,7 +686,7 @@ def enable(self, event): self._idScroll = self.figure.canvas.mpl_connect( 'scroll_event', self.scroll_zoom) - def disable(self, event): + def disable(self, event=None): """Release the canvas and disconnect press/release events.""" self._cancel_action() self.figure.canvas.widgetlock.release(self) @@ -782,7 +735,7 @@ class ToolZoom(ZoomPanBase): description = 'Zoom to rectangle' image = 'zoom_to_rect' - default_keymap = mpl.rcParams['keymap.zoom'] + default_keymap = property(lambda self: mpl.rcParams['keymap.zoom']) cursor = cursors.SELECT_REGION radio_group = 'default' @@ -794,7 +747,7 @@ def _cancel_action(self): for zoom_id in self._ids_zoom: self.figure.canvas.mpl_disconnect(zoom_id) self.toolmanager.trigger_tool('rubberband', self) - self.toolmanager.get_tool(_views_positions)._refresh_locators() + self.figure.canvas.draw_idle() self._xypress = None self._button_pressed = None self._ids_zoom = [] @@ -868,7 +821,7 @@ def _release(self, event): self._cancel_action() return - last_a = [] + done_ax = [] for cur_xypress in self._xypress: x, y = event.x, event.y @@ -879,14 +832,9 @@ def _release(self, event): return # detect twinx, twiny axes and avoid double zooming - twinx, twiny = False, False - if last_a: - for la in last_a: - if a.get_shared_x_axes().joined(a, la): - twinx = True - if a.get_shared_y_axes().joined(a, la): - twiny = True - last_a.append(a) + twinx = any(a.get_shared_x_axes().joined(a, a1) for a1 in done_ax) + twiny = any(a.get_shared_y_axes().joined(a, a1) for a1 in done_ax) + done_ax.append(a) if self._button_pressed == 1: direction = 'in' @@ -906,7 +854,7 @@ def _release(self, event): class ToolPan(ZoomPanBase): """Pan axes with left mouse, zoom with right.""" - default_keymap = mpl.rcParams['keymap.pan'] + default_keymap = property(lambda self: mpl.rcParams['keymap.pan']) description = 'Pan axes with left mouse, zoom with right' image = 'move' cursor = cursors.MOVE @@ -921,7 +869,7 @@ def _cancel_action(self): self._xypress = [] self.figure.canvas.mpl_disconnect(self._id_drag) self.toolmanager.messagelock.release(self) - self.toolmanager.get_tool(_views_positions)._refresh_locators() + self.figure.canvas.draw_idle() def _press(self, event): if event.button == 1: @@ -971,13 +919,13 @@ def _mouse_move(self, event): class ToolHelpBase(ToolBase): description = 'Print tool list, shortcuts and description' - default_keymap = mpl.rcParams['keymap.help'] + default_keymap = property(lambda self: mpl.rcParams['keymap.help']) image = 'help' @staticmethod def format_shortcut(key_sequence): """ - Converts a shortcut string from the notation used in rc config to the + Convert a shortcut string from the notation used in rc config to the standard notation for displaying shortcuts, e.g. 'ctrl+a' -> 'Ctrl+A'. """ return (key_sequence if len(key_sequence) == 1 else @@ -1011,7 +959,7 @@ class ToolCopyToClipboardBase(ToolBase): """Tool to copy the figure to the clipboard.""" description = 'Copy the canvas figure to clipboard' - default_keymap = mpl.rcParams['keymap.copy'] + default_keymap = property(lambda self: mpl.rcParams['keymap.copy']) def trigger(self, *args, **kwargs): message = "Copy tool is not available" @@ -1020,30 +968,26 @@ def trigger(self, *args, **kwargs): default_tools = {'home': ToolHome, 'back': ToolBack, 'forward': ToolForward, 'zoom': ToolZoom, 'pan': ToolPan, - 'subplots': 'ToolConfigureSubplots', - 'save': 'ToolSaveFigure', + 'subplots': ConfigureSubplotsBase, + 'save': SaveFigureBase, 'grid': ToolGrid, 'grid_minor': ToolMinorGrid, 'fullscreen': ToolFullScreen, 'quit': ToolQuit, 'quit_all': ToolQuitAll, - 'allnav': _ToolEnableAllNavigation, - 'nav': _ToolEnableNavigation, 'xscale': ToolXScale, 'yscale': ToolYScale, 'position': ToolCursorPosition, _views_positions: ToolViewsPositions, - 'cursor': 'ToolSetCursor', - 'rubberband': 'ToolRubberband', - 'help': 'ToolHelp', - 'copy': 'ToolCopyToClipboard', + 'cursor': SetCursorBase, + 'rubberband': RubberbandBase, + 'help': ToolHelpBase, + 'copy': ToolCopyToClipboardBase, } -"""Default tools""" default_toolbar_tools = [['navigation', ['home', 'back', 'forward']], ['zoompan', ['pan', 'zoom', 'subplots']], ['io', ['save', 'help']]] -"""Default tools in the toolbar""" def add_tools_to_manager(toolmanager, tools=default_tools): @@ -1055,8 +999,8 @@ def add_tools_to_manager(toolmanager, tools=default_tools): toolmanager : `.backend_managers.ToolManager` Manager to which the tools are added. tools : {str: class_like}, optional - The tools to add in a {name: tool} dict, see `add_tool` for more - info. + The tools to add in a {name: tool} dict, see + `.backend_managers.ToolManager.add_tool` for more info. """ for name, tool in tools.items(): @@ -1070,11 +1014,12 @@ def add_tools_to_container(container, tools=default_toolbar_tools): Parameters ---------- container : Container - `backend_bases.ToolContainerBase` object that will get the tools added. + `.backend_bases.ToolContainerBase` object that will get the tools + added. tools : list, optional List in the form ``[[group1, [tool1, tool2 ...]], [group2, [...]]]`` where the tools ``[tool1, tool2, ...]`` will display in group1. - See `add_tool` for details. + See `.backend_bases.ToolContainerBase.add_tool` for details. """ for group, grouptools in tools: diff --git a/lib/matplotlib/backends/__init__.py b/lib/matplotlib/backends/__init__.py index 6f4015d6ea8e..3e687f85b0be 100644 --- a/lib/matplotlib/backends/__init__.py +++ b/lib/matplotlib/backends/__init__.py @@ -1,2 +1,3 @@ # NOTE: plt.switch_backend() (called at import time) will add a "backend" # attribute here for backcompat. +_QT_FORCE_QT5_BINDING = False diff --git a/lib/matplotlib/backends/_backend_gtk.py b/lib/matplotlib/backends/_backend_gtk.py new file mode 100644 index 000000000000..7eda5b924268 --- /dev/null +++ b/lib/matplotlib/backends/_backend_gtk.py @@ -0,0 +1,326 @@ +""" +Common code for GTK3 and GTK4 backends. +""" + +import logging +import sys + +import matplotlib as mpl +from matplotlib import _api, backend_tools, cbook +from matplotlib._pylab_helpers import Gcf +from matplotlib.backend_bases import ( + _Backend, FigureManagerBase, NavigationToolbar2, TimerBase) +from matplotlib.backend_tools import Cursors + +import gi +# The GTK3/GTK4 backends will have already called `gi.require_version` to set +# the desired GTK. +from gi.repository import Gdk, Gio, GLib, Gtk + + +try: + gi.require_foreign("cairo") +except ImportError as e: + raise ImportError("Gtk-based backends require cairo") from e + +_log = logging.getLogger(__name__) +_application = None # Placeholder + + +def _shutdown_application(app): + # The application might prematurely shut down if Ctrl-C'd out of IPython, + # so close all windows. + for win in app.get_windows(): + win.close() + # The PyGObject wrapper incorrectly thinks that None is not allowed, or we + # would call this: + # Gio.Application.set_default(None) + # Instead, we set this property and ignore default applications with it: + app._created_by_matplotlib = True + global _application + _application = None + + +def _create_application(): + global _application + + if _application is None: + app = Gio.Application.get_default() + if app is None or getattr(app, '_created_by_matplotlib', False): + # display_is_valid returns False only if on Linux and neither X11 + # nor Wayland display can be opened. + if not mpl._c_internal_utils.display_is_valid(): + raise RuntimeError('Invalid DISPLAY variable') + _application = Gtk.Application.new('org.matplotlib.Matplotlib3', + Gio.ApplicationFlags.NON_UNIQUE) + # The activate signal must be connected, but we don't care for + # handling it, since we don't do any remote processing. + _application.connect('activate', lambda *args, **kwargs: None) + _application.connect('shutdown', _shutdown_application) + _application.register() + cbook._setup_new_guiapp() + else: + _application = app + + return _application + + +def mpl_to_gtk_cursor_name(mpl_cursor): + return _api.check_getitem({ + Cursors.MOVE: "move", + Cursors.HAND: "pointer", + Cursors.POINTER: "default", + Cursors.SELECT_REGION: "crosshair", + Cursors.WAIT: "wait", + Cursors.RESIZE_HORIZONTAL: "ew-resize", + Cursors.RESIZE_VERTICAL: "ns-resize", + }, cursor=mpl_cursor) + + +class TimerGTK(TimerBase): + """Subclass of `.TimerBase` using GTK timer events.""" + + def __init__(self, *args, **kwargs): + self._timer = None + super().__init__(*args, **kwargs) + + def _timer_start(self): + # Need to stop it, otherwise we potentially leak a timer id that will + # never be stopped. + self._timer_stop() + self._timer = GLib.timeout_add(self._interval, self._on_timer) + + def _timer_stop(self): + if self._timer is not None: + GLib.source_remove(self._timer) + self._timer = None + + def _timer_set_interval(self): + # Only stop and restart it if the timer has already been started. + if self._timer is not None: + self._timer_stop() + self._timer_start() + + def _on_timer(self): + super()._on_timer() + + # Gtk timeout_add() requires that the callback returns True if it + # is to be called again. + if self.callbacks and not self._single: + return True + else: + self._timer = None + return False + + +class _FigureManagerGTK(FigureManagerBase): + """ + Attributes + ---------- + canvas : `FigureCanvas` + The FigureCanvas instance + num : int or str + The Figure number + toolbar : Gtk.Toolbar or Gtk.Box + The toolbar + vbox : Gtk.VBox + The Gtk.VBox containing the canvas and toolbar + window : Gtk.Window + The Gtk.Window + """ + + def __init__(self, canvas, num): + self._gtk_ver = gtk_ver = Gtk.get_major_version() + + app = _create_application() + self.window = Gtk.Window() + app.add_window(self.window) + super().__init__(canvas, num) + + if gtk_ver == 3: + self.window.set_wmclass("matplotlib", "Matplotlib") + icon_ext = "png" if sys.platform == "win32" else "svg" + self.window.set_icon_from_file( + str(cbook._get_data_path(f"images/matplotlib.{icon_ext}"))) + + self.vbox = Gtk.Box() + self.vbox.set_property("orientation", Gtk.Orientation.VERTICAL) + + if gtk_ver == 3: + self.window.add(self.vbox) + self.vbox.show() + self.canvas.show() + self.vbox.pack_start(self.canvas, True, True, 0) + elif gtk_ver == 4: + self.window.set_child(self.vbox) + self.vbox.prepend(self.canvas) + + # calculate size for window + w, h = self.canvas.get_width_height() + + if self.toolbar is not None: + if gtk_ver == 3: + self.toolbar.show() + self.vbox.pack_end(self.toolbar, False, False, 0) + elif gtk_ver == 4: + sw = Gtk.ScrolledWindow(vscrollbar_policy=Gtk.PolicyType.NEVER) + sw.set_child(self.toolbar) + self.vbox.append(sw) + min_size, nat_size = self.toolbar.get_preferred_size() + h += nat_size.height + + self.window.set_default_size(w, h) + + self._destroying = False + self.window.connect("destroy", lambda *args: Gcf.destroy(self)) + self.window.connect({3: "delete_event", 4: "close-request"}[gtk_ver], + lambda *args: Gcf.destroy(self)) + if mpl.is_interactive(): + self.window.show() + self.canvas.draw_idle() + + self.canvas.grab_focus() + + def destroy(self, *args): + if self._destroying: + # Otherwise, this can be called twice when the user presses 'q', + # which calls Gcf.destroy(self), then this destroy(), then triggers + # Gcf.destroy(self) once again via + # `connect("destroy", lambda *args: Gcf.destroy(self))`. + return + self._destroying = True + self.window.destroy() + self.canvas.destroy() + + def show(self): + # show the figure window + self.window.show() + self.canvas.draw() + if mpl.rcParams["figure.raise_window"]: + meth_name = {3: "get_window", 4: "get_surface"}[self._gtk_ver] + if getattr(self.window, meth_name)(): + self.window.present() + else: + # If this is called by a callback early during init, + # self.window (a GtkWindow) may not have an associated + # low-level GdkWindow (on GTK3) or GdkSurface (on GTK4) yet, + # and present() would crash. + _api.warn_external("Cannot raise window yet to be setup") + + def full_screen_toggle(self): + is_fullscreen = { + 3: lambda w: (w.get_window().get_state() + & Gdk.WindowState.FULLSCREEN), + 4: lambda w: w.is_fullscreen(), + }[self._gtk_ver] + if is_fullscreen(self.window): + self.window.unfullscreen() + else: + self.window.fullscreen() + + def get_window_title(self): + return self.window.get_title() + + def set_window_title(self, title): + self.window.set_title(title) + + def resize(self, width, height): + width = int(width / self.canvas.device_pixel_ratio) + height = int(height / self.canvas.device_pixel_ratio) + if self.toolbar: + min_size, nat_size = self.toolbar.get_preferred_size() + height += nat_size.height + canvas_size = self.canvas.get_allocation() + if self._gtk_ver >= 4 or canvas_size.width == canvas_size.height == 1: + # A canvas size of (1, 1) cannot exist in most cases, because + # window decorations would prevent such a small window. This call + # must be before the window has been mapped and widgets have been + # sized, so just change the window's starting size. + self.window.set_default_size(width, height) + else: + self.window.resize(width, height) + + +class _NavigationToolbar2GTK(NavigationToolbar2): + # Must be implemented in GTK3/GTK4 backends: + # * __init__ + # * save_figure + + def set_message(self, s): + escaped = GLib.markup_escape_text(s) + self.message.set_markup(f'{escaped}') + + def draw_rubberband(self, event, x0, y0, x1, y1): + height = self.canvas.figure.bbox.height + y1 = height - y1 + y0 = height - y0 + rect = [int(val) for val in (x0, y0, x1 - x0, y1 - y0)] + self.canvas._draw_rubberband(rect) + + def remove_rubberband(self): + self.canvas._draw_rubberband(None) + + def _update_buttons_checked(self): + for name, active in [("Pan", "PAN"), ("Zoom", "ZOOM")]: + button = self._gtk_ids.get(name) + if button: + with button.handler_block(button._signal_handler): + button.set_active(self.mode.name == active) + + def pan(self, *args): + super().pan(*args) + self._update_buttons_checked() + + def zoom(self, *args): + super().zoom(*args) + self._update_buttons_checked() + + def set_history_buttons(self): + can_backward = self._nav_stack._pos > 0 + can_forward = self._nav_stack._pos < len(self._nav_stack._elements) - 1 + if 'Back' in self._gtk_ids: + self._gtk_ids['Back'].set_sensitive(can_backward) + if 'Forward' in self._gtk_ids: + self._gtk_ids['Forward'].set_sensitive(can_forward) + + +class RubberbandGTK(backend_tools.RubberbandBase): + def draw_rubberband(self, x0, y0, x1, y1): + _NavigationToolbar2GTK.draw_rubberband( + self._make_classic_style_pseudo_toolbar(), None, x0, y0, x1, y1) + + def remove_rubberband(self): + _NavigationToolbar2GTK.remove_rubberband( + self._make_classic_style_pseudo_toolbar()) + + +class ConfigureSubplotsGTK(backend_tools.ConfigureSubplotsBase): + def trigger(self, *args): + _NavigationToolbar2GTK.configure_subplots(self, None) + + +class _BackendGTK(_Backend): + backend_version = "%s.%s.%s" % ( + Gtk.get_major_version(), + Gtk.get_minor_version(), + Gtk.get_micro_version(), + ) + + @staticmethod + def mainloop(): + global _application + if _application is None: + return + + try: + _application.run() # Quits when all added windows close. + except KeyboardInterrupt: + # Ensure all windows can process their close event from + # _shutdown_application. + context = GLib.MainContext.default() + while context.pending(): + context.iteration(True) + raise + finally: + # Running after quit is undefined, so create a new one next time. + _application = None diff --git a/lib/matplotlib/backends/_backend_pdf_ps.py b/lib/matplotlib/backends/_backend_pdf_ps.py index 6c4f129b927f..4ab23915e9e2 100644 --- a/lib/matplotlib/backends/_backend_pdf_ps.py +++ b/lib/matplotlib/backends/_backend_pdf_ps.py @@ -2,12 +2,14 @@ Common functionality between the PDF and PS backends. """ +from io import BytesIO import functools +from fontTools import subset + import matplotlib as mpl -from matplotlib import _api from .. import font_manager, ft2font -from ..afm import AFM +from .._afm import AFM from ..backend_bases import RendererBase @@ -17,6 +19,39 @@ def _cached_get_afm_from_fname(fname): return AFM(fh) +def get_glyphs_subset(fontfile, characters): + """ + Subset a TTF font + + Reads the named fontfile and restricts the font to the characters. + Returns a serialization of the subset font as file-like object. + + Parameters + ---------- + symbol : str + Path to the font file + characters : str + Continuous set of characters to include in subset + """ + + options = subset.Options(glyph_names=True, recommended_glyphs=True) + + # prevent subsetting FontForge Timestamp and other tables + options.drop_tables += ['FFTM', 'PfEd', 'BDF'] + + # if fontfile is a ttc, specify font number + if fontfile.endswith(".ttc"): + options.font_number = 0 + + with subset.load_font(fontfile, options) as font: + subsetter = subset.Subsetter(options=options) + subsetter.populate(text=characters) + subsetter.subset(font) + fh = BytesIO() + font.save(fh, reorderTables=False) + return fh + + class CharacterTracker: """ Helper for font subsetting by the pdf and ps backends. @@ -28,29 +63,15 @@ class CharacterTracker: def __init__(self): self.used = {} - @_api.deprecated("3.3") - @property - def used_characters(self): - d = {} - for fname, chars in self.used.items(): - realpath, stat_key = mpl.cbook.get_realpath_and_stat(fname) - d[stat_key] = (realpath, chars) - return d - def track(self, font, s): """Record that string *s* is being typeset using font *font*.""" - if isinstance(font, str): - # Unused, can be removed after removal of track_characters. - fname = font - else: - fname = font.fname - self.used.setdefault(fname, set()).update(map(ord, s)) + char_to_font = font._get_fontmap(s) + for _c, _f in char_to_font.items(): + self.used.setdefault(_f.fname, set()).add(ord(_c)) - # Not public, can be removed when pdf/ps merge_used_characters is removed. - def merge(self, other): - """Update self with a font path to character codepoints.""" - for fname, charset in other.items(): - self.used.setdefault(fname, set()).update(charset) + def track_glyph(self, font, glyph): + """Record that codepoint *glyph* is being typeset using font *font*.""" + self.used.setdefault(font.fname, set()).add(glyph) class RendererPDFPSBase(RendererBase): @@ -89,12 +110,7 @@ def get_text_width_height_descent(self, s, prop, ismath): s, fontsize, renderer=self) return w, h, d elif ismath: - # Circular import. - from matplotlib.backends.backend_ps import RendererPS - parse = self._text2path.mathtext_parser.parse( - s, 72, prop, - _force_standard_ps_fonts=(isinstance(self, RendererPS) - and mpl.rcParams["ps.useafm"])) + parse = self._text2path.mathtext_parser.parse(s, 72, prop) return parse.width, parse.height, parse.depth elif mpl.rcParams[self._use_afm_rc_name]: font = self._get_font_afm(prop) @@ -121,8 +137,8 @@ def _get_font_afm(self, prop): return _cached_get_afm_from_fname(fname) def _get_font_ttf(self, prop): - fname = font_manager.findfont(prop) - font = font_manager.get_font(fname) + fnames = font_manager.fontManager._find_fonts_by_props(prop) + font = font_manager.get_font(fnames) font.clear() font.set_size(prop.get_size_in_points(), 72) return font diff --git a/lib/matplotlib/backends/_backend_tk.py b/lib/matplotlib/backends/_backend_tk.py index c13546ddca04..2aec1440f6c6 100644 --- a/lib/matplotlib/backends/_backend_tk.py +++ b/lib/matplotlib/backends/_backend_tk.py @@ -5,34 +5,34 @@ import os.path import sys import tkinter as tk -from tkinter.simpledialog import SimpleDialog import tkinter.filedialog +import tkinter.font import tkinter.messagebox +from tkinter.simpledialog import SimpleDialog import numpy as np +from PIL import Image, ImageTk import matplotlib as mpl from matplotlib import _api, backend_tools, cbook, _c_internal_utils from matplotlib.backend_bases import ( _Backend, FigureCanvasBase, FigureManagerBase, NavigationToolbar2, - StatusbarBase, TimerBase, ToolContainerBase, cursors, _Mode) + TimerBase, ToolContainerBase, cursors, _Mode, + CloseEvent, KeyEvent, LocationEvent, MouseEvent, ResizeEvent) from matplotlib._pylab_helpers import Gcf -from matplotlib.figure import Figure -from matplotlib.widgets import SubplotTool from . import _tkagg _log = logging.getLogger(__name__) - -backend_version = tk.TkVersion - cursord = { cursors.MOVE: "fleur", cursors.HAND: "hand2", cursors.POINTER: "arrow", cursors.SELECT_REGION: "tcross", cursors.WAIT: "watch", - } + cursors.RESIZE_HORIZONTAL: "sb_h_double_arrow", + cursors.RESIZE_VERTICAL: "sb_v_double_arrow", +} @contextmanager @@ -49,6 +49,9 @@ def _restore_foreground_window_at_end(): # Initialize to a non-empty string that is not a Tcl command _blit_tcl_name = "mpl_blit_" + uuid.uuid4().hex +TK_PHOTO_COMPOSITE_OVERLAY = 0 # apply transparency rules pixel-wise +TK_PHOTO_COMPOSITE_SET = 1 # set image buffer directly + def _blit(argsid): """ @@ -56,15 +59,10 @@ def _blit(argsid): *argsid* is a unique string identifier to fetch the correct arguments from the ``_blit_args`` dict, since arguments cannot be passed directly. - - photoimage blanking must occur in the same event and thread as blitting - to avoid flickering. """ - photoimage, dataptr, offsets, bboxptr, blank = _blit_args.pop(argsid) - if blank: - photoimage.blank() - _tkagg.blit( - photoimage.tk.interpaddr(), str(photoimage), dataptr, offsets, bboxptr) + photoimage, dataptr, offsets, bboxptr, comp_rule = _blit_args.pop(argsid) + _tkagg.blit(photoimage.tk.interpaddr(), str(photoimage), dataptr, + comp_rule, offsets, bboxptr) def blit(photoimage, aggimage, offsets, bbox=None): @@ -77,7 +75,9 @@ def blit(photoimage, aggimage, offsets, bbox=None): for big-endian ARGB32 (i.e. ARGB8888) data. If *bbox* is passed, it defines the region that gets blitted. That region - will NOT be blanked before blitting. + will be composed with the previous data according to the alpha channel. + Blitting will be clipped to pixels inside the canvas, including silently + doing nothing if the *bbox* region is entirely outside the canvas. Tcl events must be dispatched to trigger a blit from a non-Tcl thread. """ @@ -90,11 +90,13 @@ def blit(photoimage, aggimage, offsets, bbox=None): x2 = min(math.ceil(x2), width) y1 = max(math.floor(y1), 0) y2 = min(math.ceil(y2), height) + if (x1 > x2) or (y1 > y2): + return bboxptr = (x1, x2, y1, y2) - blank = False + comp_rule = TK_PHOTO_COMPOSITE_OVERLAY else: bboxptr = (0, width, 0, height) - blank = True + comp_rule = TK_PHOTO_COMPOSITE_SET # NOTE: _tkagg.blit is thread unsafe and will crash the process if called # from a thread (GH#13293). Instead of blanking and blitting here, @@ -103,7 +105,7 @@ def blit(photoimage, aggimage, offsets, bbox=None): # tkapp.call coerces all arguments to strings, so to avoid string parsing # within _blit, pack up the arguments into a global data structure. - args = photoimage, dataptr, offsets, bboxptr, blank + args = photoimage, dataptr, offsets, bboxptr, comp_rule # Need a unique key to avoid thread races. # Again, make the key a string to avoid string parsing in _blit. argsid = str(id(args)) @@ -158,23 +160,22 @@ def _on_timer(self): class FigureCanvasTk(FigureCanvasBase): required_interactive_framework = "tk" + manager_class = _api.classproperty(lambda cls: FigureManagerTk) - @_api.delete_parameter( - "3.4", "resize_callback", - alternative="get_tk_widget().bind('', ..., True)") - def __init__(self, figure=None, master=None, resize_callback=None): + def __init__(self, figure=None, master=None): super().__init__(figure) - self._idle = True - self._idle_callback = None - w, h = self.figure.bbox.size.astype(int) + self._idle_draw_id = None + self._event_loop_id = None + w, h = self.get_width_height(physical=True) self._tkcanvas = tk.Canvas( master=master, background="white", width=w, height=h, borderwidth=0, highlightthickness=0) self._tkphoto = tk.PhotoImage( master=self._tkcanvas, width=w, height=h) self._tkcanvas.create_image(w//2, h//2, image=self._tkphoto) - self._resize_callback = resize_callback self._tkcanvas.bind("", self.resize) + if sys.platform == 'win32': + self._tkcanvas.bind("", self._update_device_pixel_ratio) self._tkcanvas.bind("", self.key_press) self._tkcanvas.bind("", self.motion_notify_event) self._tkcanvas.bind("", self.enter_notify_event) @@ -195,7 +196,7 @@ def __init__(self, figure=None, master=None, resize_callback=None): # Mouse wheel for windows goes to the window with the focus. # Since the canvas won't usually have the focus, bind the # event to the window containing the canvas instead. - # See http://wiki.tcl.tk/3893 (mousewheel) for details + # See https://wiki.tcl-lang.org/3893 (mousewheel) for details root = self._tkcanvas.winfo_toplevel() root.bind("", self.scroll_event_windows, "+") @@ -203,16 +204,28 @@ def __init__(self, figure=None, master=None, resize_callback=None): # to the window and filter. def filter_destroy(event): if event.widget is self._tkcanvas: - self.close_event() + CloseEvent("close_event", self)._process() root.bind("", filter_destroy, "+") - self._master = master self._tkcanvas.focus_set() + self._rubberband_rect_black = None + self._rubberband_rect_white = None + + def _update_device_pixel_ratio(self, event=None): + # Tk gives scaling with respect to 72 DPI, but Windows screens are + # scaled vs 96 dpi, and pixel ratio settings are given in whole + # percentages, so round to 2 digits. + ratio = round(self._tkcanvas.tk.call('tk', 'scaling') / (96 / 72), 2) + if self._set_device_pixel_ratio(ratio): + # The easiest way to resize the canvas is to resize the canvas + # widget itself, since we implement all the logic for resizing the + # canvas backing store on that event. + w, h = self.get_width_height(physical=True) + self._tkcanvas.configure(width=w, height=h) + def resize(self, event): width, height = event.width, event.height - if self._resize_callback is not None: - self._resize_callback(event) # compute desired figure size in inches dpival = self.figure.dpi @@ -225,22 +238,21 @@ def resize(self, event): master=self._tkcanvas, width=int(width), height=int(height)) self._tkcanvas.create_image( int(width / 2), int(height / 2), image=self._tkphoto) - self.resize_event() + ResizeEvent("resize_event", self)._process() + self.draw_idle() def draw_idle(self): # docstring inherited - if not self._idle: + if self._idle_draw_id: return - self._idle = False - def idle_draw(*args): try: self.draw() finally: - self._idle = True + self._idle_draw_id = None - self._idle_callback = self._tkcanvas.after_idle(idle_draw) + self._idle_draw_id = self._tkcanvas.after_idle(idle_draw) def get_tk_widget(self): """ @@ -251,70 +263,69 @@ def get_tk_widget(self): """ return self._tkcanvas + def _event_mpl_coords(self, event): + # calling canvasx/canvasy allows taking scrollbars into account (i.e. + # the top of the widget may have been scrolled out of view). + return (self._tkcanvas.canvasx(event.x), + # flipy so y=0 is bottom of canvas + self.figure.bbox.height - self._tkcanvas.canvasy(event.y)) + def motion_notify_event(self, event): - x = event.x - # flipy so y=0 is bottom of canvas - y = self.figure.bbox.height - event.y - super().motion_notify_event(x, y, guiEvent=event) + MouseEvent("motion_notify_event", self, + *self._event_mpl_coords(event), + guiEvent=event)._process() def enter_notify_event(self, event): - x = event.x - # flipy so y=0 is bottom of canvas - y = self.figure.bbox.height - event.y - super().enter_notify_event(guiEvent=event, xy=(x, y)) + LocationEvent("figure_enter_event", self, + *self._event_mpl_coords(event), + guiEvent=event)._process() - def button_press_event(self, event, dblclick=False): - x = event.x - # flipy so y=0 is bottom of canvas - y = self.figure.bbox.height - event.y - num = getattr(event, 'num', None) + def leave_notify_event(self, event): + LocationEvent("figure_leave_event", self, + *self._event_mpl_coords(event), + guiEvent=event)._process() - if sys.platform == 'darwin': - # 2 and 3 were reversed on the OSX platform I tested under tkagg. - if num == 2: - num = 3 - elif num == 3: - num = 2 + def button_press_event(self, event, dblclick=False): + # set focus to the canvas so that it can receive keyboard events + self._tkcanvas.focus_set() - super().button_press_event(x, y, num, - dblclick=dblclick, guiEvent=event) + num = getattr(event, 'num', None) + if sys.platform == 'darwin': # 2 and 3 are reversed. + num = {2: 3, 3: 2}.get(num, num) + MouseEvent("button_press_event", self, + *self._event_mpl_coords(event), num, dblclick=dblclick, + guiEvent=event)._process() def button_dblclick_event(self, event): self.button_press_event(event, dblclick=True) def button_release_event(self, event): - x = event.x - # flipy so y=0 is bottom of canvas - y = self.figure.bbox.height - event.y - num = getattr(event, 'num', None) - - if sys.platform == 'darwin': - # 2 and 3 were reversed on the OSX platform I tested under tkagg. - if num == 2: - num = 3 - elif num == 3: - num = 2 - - super().button_release_event(x, y, num, guiEvent=event) + if sys.platform == 'darwin': # 2 and 3 are reversed. + num = {2: 3, 3: 2}.get(num, num) + MouseEvent("button_release_event", self, + *self._event_mpl_coords(event), num, + guiEvent=event)._process() def scroll_event(self, event): - x = event.x - y = self.figure.bbox.height - event.y num = getattr(event, 'num', None) step = 1 if num == 4 else -1 if num == 5 else 0 - super().scroll_event(x, y, step, guiEvent=event) + MouseEvent("scroll_event", self, + *self._event_mpl_coords(event), step=step, + guiEvent=event)._process() def scroll_event_windows(self, event): """MouseWheel event processor""" # need to find the window that contains the mouse w = event.widget.winfo_containing(event.x_root, event.y_root) - if w == self._tkcanvas: - x = event.x_root - w.winfo_rootx() - y = event.y_root - w.winfo_rooty() - y = self.figure.bbox.height - y - step = event.delta/120. - FigureCanvasBase.scroll_event(self, x, y, step, guiEvent=event) + if w != self._tkcanvas: + return + x = self._tkcanvas.canvasx(event.x_root - w.winfo_rootx()) + y = (self.figure.bbox.height + - self._tkcanvas.canvasy(event.y_root - w.winfo_rooty())) + step = event.delta / 120 + MouseEvent("scroll_event", self, + x, y, step=step, guiEvent=event)._process() def _get_key(self, event): unikey = event.char @@ -356,12 +367,14 @@ def _get_key(self, event): return key def key_press(self, event): - key = self._get_key(event) - FigureCanvasBase.key_press_event(self, key, guiEvent=event) + KeyEvent("key_press_event", self, + self._get_key(event), *self._event_mpl_coords(event), + guiEvent=event)._process() def key_release(self, event): - key = self._get_key(event) - FigureCanvasBase.key_release_event(self, key, guiEvent=event) + KeyEvent("key_release_event", self, + self._get_key(event), *self._event_mpl_coords(event), + guiEvent=event)._process() def new_timer(self, *args, **kwargs): # docstring inherited @@ -369,17 +382,32 @@ def new_timer(self, *args, **kwargs): def flush_events(self): # docstring inherited - self._master.update() + self._tkcanvas.update() def start_event_loop(self, timeout=0): # docstring inherited if timeout > 0: - self._master.after(int(1000*timeout), self.stop_event_loop) - self._master.mainloop() + milliseconds = int(1000 * timeout) + if milliseconds > 0: + self._event_loop_id = self._tkcanvas.after( + milliseconds, self.stop_event_loop) + else: + self._event_loop_id = self._tkcanvas.after_idle( + self.stop_event_loop) + self._tkcanvas.mainloop() def stop_event_loop(self): # docstring inherited - self._master.quit() + if self._event_loop_id: + self._tkcanvas.after_cancel(self._event_loop_id) + self._event_loop_id = None + self._tkcanvas.quit() + + def set_cursor(self, cursor): + try: + self._tkcanvas.configure(cursor=cursord[cursor]) + except tkinter.TclError: + pass class FigureManagerTk(FigureManagerBase): @@ -404,24 +432,64 @@ def __init__(self, canvas, num, window): self.window.withdraw() # packing toolbar first, because if space is getting low, last packed # widget is getting shrunk first (-> the canvas) - self.toolbar = self._get_toolbar() self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1) - if self.toolmanager: - backend_tools.add_tools_to_manager(self.toolmanager) - if self.toolbar: - backend_tools.add_tools_to_container(self.toolbar) + # If the window has per-monitor DPI awareness, then setup a Tk variable + # to store the DPI, which will be updated by the C code, and the trace + # will handle it on the Python side. + window_frame = int(window.wm_frame(), 16) + self._window_dpi = tk.IntVar(master=window, value=96, + name=f'window_dpi{window_frame}') + self._window_dpi_cbname = '' + if _tkagg.enable_dpi_awareness(window_frame, window.tk.interpaddr()): + self._window_dpi_cbname = self._window_dpi.trace_add( + 'write', self._update_window_dpi) self._shown = False - def _get_toolbar(self): - if mpl.rcParams['toolbar'] == 'toolbar2': - toolbar = NavigationToolbar2Tk(self.canvas, self.window) - elif mpl.rcParams['toolbar'] == 'toolmanager': - toolbar = ToolbarTk(self.toolmanager, self.window) - else: - toolbar = None - return toolbar + @classmethod + def create_with_canvas(cls, canvas_class, figure, num): + # docstring inherited + with _restore_foreground_window_at_end(): + if cbook._get_running_interactive_framework() is None: + cbook._setup_new_guiapp() + _c_internal_utils.Win32_SetProcessDpiAwareness_max() + window = tk.Tk(className="matplotlib") + window.withdraw() + + # Put a Matplotlib icon on the window rather than the default tk + # icon. See https://www.tcl.tk/man/tcl/TkCmd/wm.html#M50 + # + # `ImageTk` can be replaced with `tk` whenever the minimum + # supported Tk version is increased to 8.6, as Tk 8.6+ natively + # supports PNG images. + icon_fname = str(cbook._get_data_path( + 'images/matplotlib.png')) + icon_img = ImageTk.PhotoImage(file=icon_fname, master=window) + + icon_fname_large = str(cbook._get_data_path( + 'images/matplotlib_large.png')) + icon_img_large = ImageTk.PhotoImage( + file=icon_fname_large, master=window) + try: + window.iconphoto(False, icon_img_large, icon_img) + except Exception as exc: + # log the failure (due e.g. to Tk version), but carry on + _log.info('Could not load matplotlib icon: %s', exc) + + canvas = canvas_class(figure, master=window) + manager = cls(canvas, num, window) + if mpl.is_interactive(): + manager.show() + canvas.draw_idle() + return manager + + def _update_window_dpi(self, *args): + newdpi = self._window_dpi.get() + self.window.call('tk', 'scaling', newdpi / 72) + if self.toolbar and hasattr(self.toolbar, '_rescale'): + self.toolbar._rescale() + self.canvas._update_device_pixel_ratio() def resize(self, width, height): max_size = 1_400_000 # the measured max on xorg 1.20.8 was 1_409_023 @@ -444,6 +512,7 @@ def destroy(*args): Gcf.destroy(self) self.window.protocol("WM_DELETE_WINDOW", destroy) self.window.deiconify() + self.canvas._tkcanvas.focus_set() else: self.canvas.draw_idle() if mpl.rcParams['figure.raise_window']: @@ -452,21 +521,30 @@ def destroy(*args): self._shown = True def destroy(self, *args): - if self.canvas._idle_callback: - self.canvas._tkcanvas.after_cancel(self.canvas._idle_callback) + if self.canvas._idle_draw_id: + self.canvas._tkcanvas.after_cancel(self.canvas._idle_draw_id) + if self.canvas._event_loop_id: + self.canvas._tkcanvas.after_cancel(self.canvas._event_loop_id) + if self._window_dpi_cbname: + self._window_dpi.trace_remove('write', self._window_dpi_cbname) # NOTE: events need to be flushed before issuing destroy (GH #9956), - # however, self.window.update() can break user code. This is the - # safest way to achieve a complete draining of the event queue, - # but it may require users to update() on their own to execute the - # completion in obscure corner cases. + # however, self.window.update() can break user code. An async callback + # is the safest way to achieve a complete draining of the event queue, + # but it leaks if no tk event loop is running. Therefore we explicitly + # check for an event loop and choose our best guess. def delayed_destroy(): self.window.destroy() if self._owns_mainloop and not Gcf.get_num_fig_managers(): self.window.quit() - self.window.after_idle(delayed_destroy) + if cbook._get_running_interactive_framework() == "tk": + # "after idle after 0" avoids Tcl error/race (GH #19940) + self.window.after_idle(self.window.after, 0, delayed_destroy) + else: + self.window.update() + delayed_destroy() def get_window_title(self): return self.window.wm_title() @@ -480,24 +558,26 @@ def full_screen_toggle(self): class NavigationToolbar2Tk(NavigationToolbar2, tk.Frame): - """ - Attributes - ---------- - canvas : `FigureCanvas` - The figure canvas on which to operate. - win : tk.Window - The tk.Window which owns this toolbar. - pack_toolbar : bool, default: True - If True, add the toolbar to the parent's pack manager's packing list - during initialization with ``side='bottom'`` and ``fill='x'``. - If you want to use the toolbar with a different layout manager, use - ``pack_toolbar=False``. - """ - def __init__(self, canvas, window, *, pack_toolbar=True): - # Avoid using self.window (prefer self.canvas.get_tk_widget().master), - # so that Tool implementations can reuse the methods. - self.window = window + window = _api.deprecated("3.6", alternative="self.master")( + property(lambda self: self.master)) + + def __init__(self, canvas, window=None, *, pack_toolbar=True): + """ + Parameters + ---------- + canvas : `FigureCanvas` + The figure canvas on which to operate. + window : tk.Window + The tk.Window which owns this toolbar. + pack_toolbar : bool, default: True + If True, add the toolbar to the parent's pack manager's packing + list during initialization with ``side="bottom"`` and ``fill="x"``. + If you want to use the toolbar with a different layout manager, use + ``pack_toolbar=False``. + """ + if window is None: + window = canvas.get_tk_widget().master tk.Frame.__init__(self, master=window, borderwidth=2, width=int(canvas.figure.bbox.width), height=50) @@ -516,22 +596,53 @@ def __init__(self, canvas, window, *, pack_toolbar=True): if tooltip_text is not None: ToolTip.createToolTip(button, tooltip_text) + self._label_font = tkinter.font.Font(root=window, size=10) + # This filler item ensures the toolbar is always at least two text # lines high. Otherwise the canvas gets redrawn as the mouse hovers # over images because those use two-line messages which resize the # toolbar. - label = tk.Label(master=self, + label = tk.Label(master=self, font=self._label_font, text='\N{NO-BREAK SPACE}\n\N{NO-BREAK SPACE}') label.pack(side=tk.RIGHT) self.message = tk.StringVar(master=self) - self._message_label = tk.Label(master=self, textvariable=self.message) + self._message_label = tk.Label(master=self, font=self._label_font, + textvariable=self.message, + justify=tk.RIGHT) self._message_label.pack(side=tk.RIGHT) NavigationToolbar2.__init__(self, canvas) if pack_toolbar: self.pack(side=tk.BOTTOM, fill=tk.X) + def _rescale(self): + """ + Scale all children of the toolbar to current DPI setting. + + Before this is called, the Tk scaling setting will have been updated to + match the new DPI. Tk widgets do not update for changes to scaling, but + all measurements made after the change will match the new scaling. Thus + this function re-applies all the same sizes in points, which Tk will + scale correctly to pixels. + """ + for widget in self.winfo_children(): + if isinstance(widget, (tk.Button, tk.Checkbutton)): + if hasattr(widget, '_image_file'): + # Explicit class because ToolbarTk calls _rescale. + NavigationToolbar2Tk._set_image_for_button(self, widget) + else: + # Text-only button is handled by the font setting instead. + pass + elif isinstance(widget, tk.Frame): + widget.configure(height='22p', pady='1p') + widget.pack_configure(padx='4p') + elif isinstance(widget, tk.Label): + pass # Text is handled by the font setting instead. + else: + _log.warning('Unknown child class %s', widget.winfo_class) + self._label_font.configure(size=10) + def _update_buttons_checked(self): # sync button checkstates to match active mode for text, mode in [('Zoom', _Mode.ZOOM), ('Pan', _Mode.PAN)]: @@ -553,35 +664,115 @@ def set_message(self, s): self.message.set(s) def draw_rubberband(self, event, x0, y0, x1, y1): + # Block copied from remove_rubberband for backend_tools convenience. + if self.canvas._rubberband_rect_white: + self.canvas._tkcanvas.delete(self.canvas._rubberband_rect_white) + if self.canvas._rubberband_rect_black: + self.canvas._tkcanvas.delete(self.canvas._rubberband_rect_black) height = self.canvas.figure.bbox.height y0 = height - y0 y1 = height - y1 - if hasattr(self, "lastrect"): - self.canvas._tkcanvas.delete(self.lastrect) - self.lastrect = self.canvas._tkcanvas.create_rectangle(x0, y0, x1, y1) + self.canvas._rubberband_rect_black = ( + self.canvas._tkcanvas.create_rectangle( + x0, y0, x1, y1)) + self.canvas._rubberband_rect_white = ( + self.canvas._tkcanvas.create_rectangle( + x0, y0, x1, y1, outline='white', dash=(3, 3))) + + def remove_rubberband(self): + if self.canvas._rubberband_rect_white: + self.canvas._tkcanvas.delete(self.canvas._rubberband_rect_white) + self.canvas._rubberband_rect_white = None + if self.canvas._rubberband_rect_black: + self.canvas._tkcanvas.delete(self.canvas._rubberband_rect_black) + self.canvas._rubberband_rect_black = None - def release_zoom(self, event): - super().release_zoom(event) - if hasattr(self, "lastrect"): - self.canvas._tkcanvas.delete(self.lastrect) - del self.lastrect + lastrect = _api.deprecated("3.6")( + property(lambda self: self.canvas._rubberband_rect_black)) - def set_cursor(self, cursor): - window = self.canvas.get_tk_widget().master - try: - window.configure(cursor=cursord[cursor]) - except tkinter.TclError: - pass + def _set_image_for_button(self, button): + """ + Set the image for a button based on its pixel size. - def _Button(self, text, image_file, toggle, command): - if tk.TkVersion >= 8.6: - PhotoImage = tk.PhotoImage + The pixel size is determined by the DPI scaling of the window. + """ + if button._image_file is None: + return + + # Allow _image_file to be relative to Matplotlib's "images" data + # directory. + path_regular = cbook._get_data_path('images', button._image_file) + path_large = path_regular.with_name( + path_regular.name.replace('.png', '_large.png')) + size = button.winfo_pixels('18p') + + # Nested functions because ToolbarTk calls _Button. + def _get_color(color_name): + # `winfo_rgb` returns an (r, g, b) tuple in the range 0-65535 + return button.winfo_rgb(button.cget(color_name)) + + def _is_dark(color): + if isinstance(color, str): + color = _get_color(color) + return max(color) < 65535 / 2 + + def _recolor_icon(image, color): + image_data = np.asarray(image).copy() + black_mask = (image_data[..., :3] == 0).all(axis=-1) + image_data[black_mask, :3] = color + return Image.fromarray(image_data, mode="RGBA") + + # Use the high-resolution (48x48 px) icon if it exists and is needed + with Image.open(path_large if (size > 24 and path_large.exists()) + else path_regular) as im: + image = ImageTk.PhotoImage(im.resize((size, size)), master=self) + button._ntimage = image + + # create a version of the icon with the button's text color + foreground = (255 / 65535) * np.array( + button.winfo_rgb(button.cget("foreground"))) + im_alt = _recolor_icon(im, foreground) + image_alt = ImageTk.PhotoImage( + im_alt.resize((size, size)), master=self) + button._ntimage_alt = image_alt + + if _is_dark("background"): + # For Checkbuttons, we need to set `image` and `selectimage` at + # the same time. Otherwise, when updating the `image` option + # (such as when changing DPI), if the old `selectimage` has + # just been overwritten, Tk will throw an error. + image_kwargs = {"image": image_alt} else: - from PIL.ImageTk import PhotoImage - image = (PhotoImage(master=self, file=image_file) - if image_file is not None else None) + image_kwargs = {"image": image} + # Checkbuttons may switch the background to `selectcolor` in the + # checked state, so check separately which image it needs to use in + # that state to still ensure enough contrast with the background. + if ( + isinstance(button, tk.Checkbutton) + and button.cget("selectcolor") != "" + ): + if self._windowingsystem != "x11": + selectcolor = "selectcolor" + else: + # On X11, selectcolor isn't used directly for indicator-less + # buttons. See `::tk::CheckEnter` in the Tk button.tcl source + # code for details. + r1, g1, b1 = _get_color("selectcolor") + r2, g2, b2 = _get_color("activebackground") + selectcolor = ((r1+r2)/2, (g1+g2)/2, (b1+b2)/2) + if _is_dark(selectcolor): + image_kwargs["selectimage"] = image_alt + else: + image_kwargs["selectimage"] = image + + button.configure(**image_kwargs, height='18p', width='18p') + + def _Button(self, text, image_file, toggle, command): if not toggle: - b = tk.Button(master=self, text=text, image=image, command=command) + b = tk.Button( + master=self, text=text, command=command, + relief="flat", overrelief="groove", borderwidth=1, + ) else: # There is a bug in tkinter included in some python 3.6 versions # that without this variable, produces a "visual" toggling of @@ -590,18 +781,24 @@ def _Button(self, text, image_file, toggle, command): # https://bugs.python.org/issue25684 var = tk.IntVar(master=self) b = tk.Checkbutton( - master=self, text=text, image=image, command=command, - indicatoron=False, variable=var) + master=self, text=text, command=command, indicatoron=False, + variable=var, offrelief="flat", overrelief="groove", + borderwidth=1 + ) b.var = var - b._ntimage = image + b._image_file = image_file + if image_file is not None: + # Explicit class because ToolbarTk calls _Button. + NavigationToolbar2Tk._set_image_for_button(self, b) + else: + b.configure(font=self._label_font) b.pack(side=tk.LEFT) return b def _Spacer(self): - # Buttons are 30px high. Make this 26px tall +2px padding to center it. - s = tk.Frame( - master=self, height=26, relief=tk.RIDGE, pady=2, bg="DarkGray") - s.pack(side=tk.LEFT, padx=5) + # Buttons are also 18pt high. + s = tk.Frame(master=self, height='18p', relief=tk.RIDGE, bg='DarkGray') + s.pack(side=tk.LEFT, padx='3p') return s def save_figure(self, *args): @@ -619,7 +816,7 @@ def save_figure(self, *args): # asksaveasfilename dialog when you choose various save types # from the dropdown. Passing in the empty string seems to # work - JDH! - #defaultextension = self.canvas.get_default_filetype() + # defaultextension = self.canvas.get_default_filetype() defaultextension = '' initialdir = os.path.expanduser(mpl.rcParams['savefig.directory']) initialfile = self.canvas.get_default_filename() @@ -683,7 +880,7 @@ def showtip(self, text): if self.tipwindow or not self.text: return x, y, _, _ = self.widget.bbox("insert") - x = x + self.widget.winfo_rootx() + 27 + x = x + self.widget.winfo_rootx() + self.widget.winfo_width() y = y + self.widget.winfo_rooty() self.tipwindow = tw = tk.Toplevel(self.widget) tw.wm_overrideredirect(1) @@ -706,22 +903,21 @@ def hidetip(self): tw.destroy() +@backend_tools._register_tool_class(FigureCanvasTk) class RubberbandTk(backend_tools.RubberbandBase): def draw_rubberband(self, x0, y0, x1, y1): - height = self.figure.canvas.figure.bbox.height - y0 = height - y0 - y1 = height - y1 - if hasattr(self, "lastrect"): - self.figure.canvas._tkcanvas.delete(self.lastrect) - self.lastrect = self.figure.canvas._tkcanvas.create_rectangle( - x0, y0, x1, y1) + NavigationToolbar2Tk.draw_rubberband( + self._make_classic_style_pseudo_toolbar(), None, x0, y0, x1, y1) def remove_rubberband(self): - if hasattr(self, "lastrect"): - self.figure.canvas._tkcanvas.delete(self.lastrect) - del self.lastrect + NavigationToolbar2Tk.remove_rubberband( + self._make_classic_style_pseudo_toolbar()) + lastrect = _api.deprecated("3.6")( + property(lambda self: self.figure.canvas._rubberband_rect_black)) + +@_api.deprecated("3.5", alternative="ToolSetCursor") class SetCursorTk(backend_tools.SetCursorBase): def set_cursor(self, cursor): NavigationToolbar2Tk.set_cursor( @@ -729,25 +925,38 @@ def set_cursor(self, cursor): class ToolbarTk(ToolContainerBase, tk.Frame): - def __init__(self, toolmanager, window): + def __init__(self, toolmanager, window=None): ToolContainerBase.__init__(self, toolmanager) + if window is None: + window = self.toolmanager.canvas.get_tk_widget().master xmin, xmax = self.toolmanager.canvas.figure.bbox.intervalx height, width = 50, xmax - xmin tk.Frame.__init__(self, master=window, width=int(width), height=int(height), borderwidth=2) + self._label_font = tkinter.font.Font(size=10) self._message = tk.StringVar(master=self) - self._message_label = tk.Label(master=self, textvariable=self._message) + self._message_label = tk.Label(master=self, font=self._label_font, + textvariable=self._message) self._message_label.pack(side=tk.RIGHT) self._toolitems = {} self.pack(side=tk.TOP, fill=tk.X) self._groups = {} + def _rescale(self): + return NavigationToolbar2Tk._rescale(self) + def add_toolitem( self, name, group, position, image_file, description, toggle): frame = self._get_groupframe(group) - button = NavigationToolbar2Tk._Button(self, name, image_file, toggle, + buttons = frame.pack_slaves() + if position >= len(buttons) or position < 0: + before = None + else: + before = buttons[position] + button = NavigationToolbar2Tk._Button(frame, name, image_file, toggle, lambda: self._button_click(name)) + button.pack_configure(before=before) if description is not None: ToolTip.createToolTip(button, description) self._toolitems.setdefault(name, []) @@ -759,6 +968,7 @@ def _get_groupframe(self, group): self._add_separator() frame = tk.Frame(master=self, borderwidth=0) frame.pack(side=tk.LEFT, fill=tk.Y) + frame._label_font = self._label_font self._groups[group] = frame return self._groups[group] @@ -786,59 +996,20 @@ def set_message(self, s): self._message.set(s) -@_api.deprecated("3.3") -class StatusbarTk(StatusbarBase, tk.Frame): - def __init__(self, window, *args, **kwargs): - StatusbarBase.__init__(self, *args, **kwargs) - xmin, xmax = self.toolmanager.canvas.figure.bbox.intervalx - height, width = 50, xmax - xmin - tk.Frame.__init__(self, master=window, - width=int(width), height=int(height), - borderwidth=2) - self._message = tk.StringVar(master=self) - self._message_label = tk.Label(master=self, textvariable=self._message) - self._message_label.pack(side=tk.RIGHT) - self.pack(side=tk.TOP, fill=tk.X) - - def set_message(self, s): - self._message.set(s) - - +@backend_tools._register_tool_class(FigureCanvasTk) class SaveFigureTk(backend_tools.SaveFigureBase): def trigger(self, *args): NavigationToolbar2Tk.save_figure( self._make_classic_style_pseudo_toolbar()) +@backend_tools._register_tool_class(FigureCanvasTk) class ConfigureSubplotsTk(backend_tools.ConfigureSubplotsBase): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.window = None - def trigger(self, *args): - self.init_window() - self.window.lift() - - def init_window(self): - if self.window: - return - - toolfig = Figure(figsize=(6, 3)) - self.window = tk.Tk() - - canvas = type(self.canvas)(toolfig, master=self.window) - toolfig.subplots_adjust(top=0.9) - SubplotTool(self.figure, toolfig) - canvas.draw() - canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1) - self.window.protocol("WM_DELETE_WINDOW", self.destroy) - - def destroy(self, *args, **kwargs): - if self.window is not None: - self.window.destroy() - self.window = None + NavigationToolbar2Tk.configure_subplots(self) +@backend_tools._register_tool_class(FigureCanvasTk) class HelpTk(backend_tools.ToolHelpBase): def trigger(self, *args): dialog = SimpleDialog( @@ -846,50 +1017,16 @@ def trigger(self, *args): dialog.done = lambda num: dialog.frame.master.withdraw() -backend_tools.ToolSaveFigure = SaveFigureTk -backend_tools.ToolConfigureSubplots = ConfigureSubplotsTk -backend_tools.ToolSetCursor = SetCursorTk -backend_tools.ToolRubberband = RubberbandTk -backend_tools.ToolHelp = HelpTk -backend_tools.ToolCopyToClipboard = backend_tools.ToolCopyToClipboardBase Toolbar = ToolbarTk +FigureManagerTk._toolbar2_class = NavigationToolbar2Tk +FigureManagerTk._toolmanager_toolbar_class = ToolbarTk @_Backend.export class _BackendTk(_Backend): + backend_version = tk.TkVersion FigureManager = FigureManagerTk - @classmethod - def new_figure_manager_given_figure(cls, num, figure): - """ - Create a new figure manager instance for the given figure. - """ - with _restore_foreground_window_at_end(): - if cbook._get_running_interactive_framework() is None: - cbook._setup_new_guiapp() - window = tk.Tk(className="matplotlib") - window.withdraw() - - # Put a Matplotlib icon on the window rather than the default tk - # icon. Tkinter doesn't allow colour icons on linux systems, but - # tk>=8.5 has a iconphoto command which we call directly. See - # http://mail.python.org/pipermail/tkinter-discuss/2006-November/000954.html - icon_fname = str(cbook._get_data_path( - 'images/matplotlib_128.ppm')) - icon_img = tk.PhotoImage(file=icon_fname, master=window) - try: - window.iconphoto(False, icon_img) - except Exception as exc: - # log the failure (due e.g. to Tk version), but carry on - _log.info('Could not load matplotlib icon: %s', exc) - - canvas = cls.FigureCanvas(figure, master=window) - manager = cls.FigureManager(canvas, num, window) - if mpl.is_interactive(): - manager.show() - canvas.draw_idle() - return manager - @staticmethod def mainloop(): managers = Gcf.get_all_fig_managers() diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index a9b0383f4bdd..8fda2606b472 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,25 +17,21 @@ Still TODO: * integrate screen dpi w/ ppi and text + +.. _Anti-Grain Geometry: http://agg.sourceforge.net/antigrain.com """ -try: - import threading -except ImportError: - import dummy_threading as threading from contextlib import nullcontext from math import radians, cos, sin +import threading import numpy as np -from PIL import Image import matplotlib as mpl from matplotlib import _api, cbook -from matplotlib import colors as mcolors from matplotlib.backend_bases import ( - _Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase, - RendererBase) -from matplotlib.font_manager import findfont, get_font + _Backend, FigureCanvasBase, FigureManagerBase, RendererBase) +from matplotlib.font_manager import fontManager as _fontManager, get_font from matplotlib.ft2font import (LOAD_FORCE_AUTOHINT, LOAD_NO_HINTING, LOAD_DEFAULT, LOAD_NO_AUTOHINT) from matplotlib.mathtext import MathTextParser @@ -44,9 +40,6 @@ from matplotlib.backends._backend_agg import RendererAgg as _RendererAgg -backend_version = 'v2.2' - - def get_hinting_flag(): mapping = { 'default': LOAD_DEFAULT, @@ -109,27 +102,10 @@ def _update_methods(self): self.draw_gouraud_triangles = self._renderer.draw_gouraud_triangles self.draw_image = self._renderer.draw_image self.draw_markers = self._renderer.draw_markers - # This is its own method for the duration of the deprecation of - # offset_position = "data". - # self.draw_path_collection = self._renderer.draw_path_collection + self.draw_path_collection = self._renderer.draw_path_collection self.draw_quad_mesh = self._renderer.draw_quad_mesh self.copy_from_bbox = self._renderer.copy_from_bbox - @_api.deprecated("3.4") - def get_content_extents(self): - orig_img = np.asarray(self.buffer_rgba()) - slice_y, slice_x = cbook._get_nonzero_slices(orig_img[..., 3]) - return (slice_x.start, slice_y.start, - slice_x.stop - slice_x.start, slice_y.stop - slice_y.start) - - @_api.deprecated("3.4") - def tostring_rgba_minimized(self): - extents = self.get_content_extents() - bbox = [[extents[0], self.height - (extents[1] + extents[3])], - [extents[0] + extents[2], self.height - extents[1]]] - region = self.copy_from_bbox(bbox) - return np.array(region), extents - def draw_path(self, gc, path, transform, rgbFace=None): # docstring inherited nmax = mpl.rcParams['agg.path.chunksize'] # here at least for testing @@ -150,35 +126,69 @@ def draw_path(self, gc, path, transform, rgbFace=None): c = c[ii0:ii1] c[0] = Path.MOVETO # move to end of last chunk p = Path(v, c) + p.simplify_threshold = path.simplify_threshold try: self._renderer.draw_path(gc, p, transform, rgbFace) - except OverflowError as err: - raise OverflowError( - "Exceeded cell block limit (set 'agg.path.chunksize' " - "rcparam)") from err + except OverflowError: + msg = ( + "Exceeded cell block limit in Agg.\n\n" + "Please reduce the value of " + f"rcParams['agg.path.chunksize'] (currently {nmax}) " + "or increase the path simplification threshold" + "(rcParams['path.simplify_threshold'] = " + f"{mpl.rcParams['path.simplify_threshold']:.2f} by " + "default and path.simplify_threshold = " + f"{path.simplify_threshold:.2f} on the input)." + ) + raise OverflowError(msg) from None else: try: self._renderer.draw_path(gc, path, transform, rgbFace) - except OverflowError as err: - raise OverflowError("Exceeded cell block limit (set " - "'agg.path.chunksize' rcparam)") from err - - def draw_path_collection(self, gc, master_transform, paths, all_transforms, - offsets, offsetTrans, facecolors, edgecolors, - linewidths, linestyles, antialiaseds, urls, - offset_position): - if offset_position == "data": - _api.warn_deprecated( - "3.3", message="Support for offset_position='data' is " - "deprecated since %(since)s and will be removed %(removal)s.") - return self._renderer.draw_path_collection( - gc, master_transform, paths, all_transforms, offsets, offsetTrans, - facecolors, edgecolors, linewidths, linestyles, antialiaseds, urls, - offset_position) + except OverflowError: + cant_chunk = '' + if rgbFace is not None: + cant_chunk += "- can not split filled path\n" + if gc.get_hatch() is not None: + cant_chunk += "- can not split hatched path\n" + if not path.should_simplify: + cant_chunk += "- path.should_simplify is False\n" + if len(cant_chunk): + msg = ( + "Exceeded cell block limit in Agg, however for the " + "following reasons:\n\n" + f"{cant_chunk}\n" + "we can not automatically split up this path to draw." + "\n\nPlease manually simplify your path." + ) + + else: + inc_threshold = ( + "or increase the path simplification threshold" + "(rcParams['path.simplify_threshold'] = " + f"{mpl.rcParams['path.simplify_threshold']} " + "by default and path.simplify_threshold " + f"= {path.simplify_threshold} " + "on the input)." + ) + if nmax > 100: + msg = ( + "Exceeded cell block limit in Agg. Please reduce " + "the value of rcParams['agg.path.chunksize'] " + f"(currently {nmax}) {inc_threshold}" + ) + else: + msg = ( + "Exceeded cell block limit in Agg. Please set " + "the value of rcParams['agg.path.chunksize'], " + f"(currently {nmax}) to be greater than 100 " + + inc_threshold + ) + + raise OverflowError(msg) from None def draw_mathtext(self, gc, x, y, s, prop, angle): """Draw mathtext using :mod:`matplotlib.mathtext`.""" - ox, oy, width, height, descent, font_image, used_characters = \ + ox, oy, width, height, descent, font_image = \ self.mathtext_parser.parse(s, self.dpi, prop) xd = descent * sin(radians(angle)) @@ -189,18 +199,12 @@ def draw_mathtext(self, gc, x, y, s, prop, angle): def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): # docstring inherited - if ismath: return self.draw_mathtext(gc, x, y, s, prop, angle) - - flags = get_hinting_flag() - font = self._get_agg_font(prop) - - if font is None: - return None + font = self._prepare_font(prop) # We pass '0' for angle here, since it will be rotated (in raster # space) in the following call to draw_text_image). - font.set_text(s, 0, flags=flags) + font.set_text(s, 0, flags=get_hinting_flag()) font.draw_glyphs_to_bitmap( antialiased=mpl.rcParams['text.antialiased']) d = font.get_descent() / 64.0 @@ -217,12 +221,8 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): def get_text_width_height_descent(self, s, prop, ismath): # docstring inherited - if ismath in ["TeX", "TeX!"]: - if ismath == "TeX!": - _api.warn_deprecated( - "3.3", message="Support for ismath='TeX!' is deprecated " - "since %(since)s and will be removed %(removal)s; use " - "ismath='TeX' instead.") + _api.check_in_list(["TeX", True, False], ismath=ismath) + if ismath == "TeX": # todo: handle props texmanager = self.get_texmanager() fontsize = prop.get_size_in_points() @@ -231,13 +231,12 @@ def get_text_width_height_descent(self, s, prop, ismath): return w, h, d if ismath: - ox, oy, width, height, descent, fonts, used_characters = \ + ox, oy, width, height, descent, font_image = \ self.mathtext_parser.parse(s, self.dpi, prop) return width, height, descent - flags = get_hinting_flag() - font = self._get_agg_font(prop) - font.set_text(s, 0.0, flags=flags) + font = self._prepare_font(prop) + font.set_text(s, 0.0, flags=get_hinting_flag()) w, h = font.get_width_height() # width and height of unrotated string d = font.get_descent() w /= 64.0 # convert from subpixels @@ -266,17 +265,14 @@ def get_canvas_width_height(self): # docstring inherited return self.width, self.height - def _get_agg_font(self, prop): + def _prepare_font(self, font_prop): """ - Get the font for text instance t, caching for efficiency + Get the `.FT2Font` for *font_prop*, clear its buffer, and set its size. """ - fname = findfont(prop) - font = get_font(fname) - + font = get_font(_fontManager._find_fonts_by_props(font_prop)) font.clear() - size = prop.get_size_in_points() + size = font_prop.get_size_in_points() font.set_size(size, self.dpi) - return font def points_to_pixels(self, points): @@ -388,6 +384,8 @@ def post_processing(image, dpi): class FigureCanvasAgg(FigureCanvasBase): # docstring inherited + _lastKey = None # Overwritten per-instance on the first draw. + def copy_from_bbox(self, bbox): renderer = self.get_renderer() return renderer.copy_from_bbox(bbox) @@ -398,7 +396,8 @@ def restore_region(self, region, bbox=None, xy=None): def draw(self): # docstring inherited - self.renderer = self.get_renderer(cleared=True) + self.renderer = self.get_renderer() + self.renderer.clear() # Acquire a lock on the shared font cache. with RendererAgg.lock, \ (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar @@ -408,11 +407,11 @@ def draw(self): # don't forget to call the superclass. super().draw() + @_api.delete_parameter("3.6", "cleared", alternative="renderer.clear()") def get_renderer(self, cleared=False): w, h = self.figure.bbox.size key = w, h, self.figure.dpi - reuse_renderer = (hasattr(self, "renderer") - and getattr(self, "_lastKey", None) == key) + reuse_renderer = (self._lastKey == key) if not reuse_renderer: self.renderer = RendererAgg(w, h, self.figure.dpi) self._lastKey = key @@ -447,7 +446,7 @@ def buffer_rgba(self): """ return self.renderer.buffer_rgba() - @_check_savefig_extra_args + @_api.delete_parameter("3.5", "args") def print_raw(self, filename_or_obj, *args): FigureCanvasAgg.draw(self) renderer = self.get_renderer() @@ -456,7 +455,17 @@ def print_raw(self, filename_or_obj, *args): print_rgba = print_raw - @_check_savefig_extra_args + 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) + + @_api.delete_parameter("3.5", "args") def print_png(self, filename_or_obj, *args, metadata=None, pil_kwargs=None): """ @@ -505,10 +514,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) @@ -520,85 +526,40 @@ def print_to_buffer(self): # print_figure(), and the latter ensures that `self.figure.dpi` already # matches the dpi kwarg (if any). - @_check_savefig_extra_args( - extra_kwargs=["quality", "optimize", "progressive"]) - @_api.delete_parameter("3.3", "quality", - alternative="pil_kwargs={'quality': ...}") - @_api.delete_parameter("3.3", "optimize", - alternative="pil_kwargs={'optimize': ...}") - @_api.delete_parameter("3.3", "progressive", - alternative="pil_kwargs={'progressive': ...}") - def print_jpg(self, filename_or_obj, *args, pil_kwargs=None, **kwargs): + @_api.delete_parameter("3.5", "args") + def print_jpg(self, filename_or_obj, *args, pil_kwargs=None): + # savefig() has already applied savefig.facecolor; we now set it to + # white to make imsave() blend semi-transparent figures against an + # assumed white background. + with mpl.rc_context({"savefig.facecolor": "white"}): + self._print_pil(filename_or_obj, "jpeg", pil_kwargs) + + print_jpeg = print_jpg + + def print_tif(self, filename_or_obj, *, pil_kwargs=None): + self._print_pil(filename_or_obj, "tiff", pil_kwargs) + + print_tiff = print_tif + + 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 JPEG file. + Write the figure to a {} file. Parameters ---------- filename_or_obj : str or path-like or file-like The file to write to. - - Other Parameters - ---------------- - quality : int, default: :rc:`savefig.jpeg_quality` - The image quality, on a scale from 1 (worst) to 95 (best). - Values above 95 should be avoided; 100 disables portions of - the JPEG compression algorithm, and results in large files - with hardly any gain in image quality. This parameter is - deprecated. - optimize : bool, default: False - Whether the encoder should make an extra pass over the image - in order to select optimal encoder settings. This parameter is - deprecated. - progressive : bool, default: False - Whether the image should be stored as a progressive JPEG file. - This parameter is deprecated. pil_kwargs : dict, optional Additional keyword arguments that are passed to - `PIL.Image.Image.save` when saving the figure. These take - precedence over *quality*, *optimize* and *progressive*. - """ - # 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) - finally: - self.figure.set_facecolor((r, g, b, a)) - if pil_kwargs is None: - pil_kwargs = {} - for k in ["quality", "optimize", "progressive"]: - if k in kwargs: - pil_kwargs.setdefault(k, kwargs.pop(k)) - if "quality" not in pil_kwargs: - quality = pil_kwargs["quality"] = \ - dict.__getitem__(mpl.rcParams, "savefig.jpeg_quality") - if quality not in [0, 75, 95]: # default qualities. - _api.warn_deprecated( - "3.3", name="savefig.jpeg_quality", obj_type="rcParam", - addendum="Set the quality using " - "`pil_kwargs={'quality': ...}`; the future default " - "quality will be 75, matching the default of Pillow and " - "libjpeg.") - 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)) - - print_tiff = print_tif + `PIL.Image.Image.save` when saving the figure. + """.format, ["JPEG", "TIFF", "WebP"]) @_Backend.export class _BackendAgg(_Backend): + backend_version = 'v2.2' FigureCanvas = FigureCanvasAgg FigureManager = FigureManagerBase diff --git a/lib/matplotlib/backends/backend_cairo.py b/lib/matplotlib/backends/backend_cairo.py index b05a5fc0967a..3d6cec641884 100644 --- a/lib/matplotlib/backends/backend_cairo.py +++ b/lib/matplotlib/backends/backend_cairo.py @@ -1,11 +1,12 @@ """ -A Cairo backend for matplotlib +A Cairo backend for Matplotlib ============================== :Author: Steve Chaplin and others This backend depends on cairocffi or pycairo. """ +import functools import gzip import math @@ -13,46 +14,26 @@ try: import cairo - if cairo.version_info < (1, 11, 0): - # Introduced create_for_data for Py3. + if cairo.version_info < (1, 14, 0): # Introduced set_device_scale. raise ImportError except ImportError: try: import cairocffi as cairo except ImportError as err: raise ImportError( - "cairo backend requires that pycairo>=1.11.0 or cairocffi " + "cairo backend requires that pycairo>=1.14.0 or cairocffi " "is installed") from err +import matplotlib as mpl from .. import _api, cbook, font_manager from matplotlib.backend_bases import ( - _Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase, - GraphicsContextBase, RendererBase) + _Backend, FigureCanvasBase, FigureManagerBase, GraphicsContextBase, + RendererBase) from matplotlib.font_manager import ttfFontProperty -from matplotlib.mathtext import MathTextParser from matplotlib.path import Path from matplotlib.transforms import Affine2D -backend_version = cairo.version - - -if cairo.__name__ == "cairocffi": - # Convert a pycairo context to a cairocffi one. - def _to_context(ctx): - if not isinstance(ctx, cairo.Context): - ctx = cairo.Context._from_pointer( - cairo.ffi.cast( - 'cairo_t **', - id(ctx) + object.__basicsize__)[0], - incref=True) - return ctx -else: - # Pass-through a pycairo context. - def _to_context(ctx): - return ctx - - def _append_path(ctx, path, transform, clip=None): for points, code in path.iter_segments( transform, remove_nans=True, clip=clip): @@ -121,25 +102,38 @@ def attr(field): class RendererCairo(RendererBase): - fontweights = _api.deprecated("3.3")(property(lambda self: {*_f_weights})) - fontangles = _api.deprecated("3.3")(property(lambda self: {*_f_angles})) - mathtext_parser = _api.deprecated("3.4")( - property(lambda self: MathTextParser('Cairo'))) - def __init__(self, dpi): self.dpi = dpi self.gc = GraphicsContextCairo(renderer=self) + self.width = None + self.height = None self.text_ctx = cairo.Context( cairo.ImageSurface(cairo.FORMAT_ARGB32, 1, 1)) super().__init__() + def set_context(self, ctx): + surface = ctx.get_target() + if hasattr(surface, "get_width") and hasattr(surface, "get_height"): + size = surface.get_width(), surface.get_height() + elif hasattr(surface, "get_extents"): # GTK4 RecordingSurface. + ext = surface.get_extents() + size = ext.width, ext.height + else: # vector surfaces. + ctx.save() + ctx.reset_clip() + rect, *rest = ctx.copy_clip_rectangle_list() + if rest: + raise TypeError("Cannot infer surface size") + size = rect.width, rect.height + ctx.restore() + self.gc.ctx = ctx + self.width, self.height = size + + @_api.deprecated("3.6", alternative="set_context") def set_ctx_from_surface(self, surface): self.gc.ctx = cairo.Context(surface) - # Although it may appear natural to automatically call - # `self.set_width_height(surface.get_width(), surface.get_height())` - # here (instead of having the caller do so separately), this would fail - # for PDF/PS/SVG surfaces, which have no way to report their extents. + @_api.deprecated("3.6") def set_width_height(self, width, height): self.width = width self.height = height @@ -243,9 +237,14 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): ctx.new_path() ctx.move_to(x, y) - ctx.select_font_face(*_cairo_font_args_from_font_prop(prop)) ctx.save() - ctx.set_font_size(prop.get_size_in_points() * self.dpi / 72) + ctx.select_font_face(*_cairo_font_args_from_font_prop(prop)) + ctx.set_font_size(self.points_to_pixels(prop.get_size_in_points())) + opts = cairo.FontOptions() + opts.set_antialias( + cairo.ANTIALIAS_DEFAULT if mpl.rcParams["text.antialiased"] + else cairo.ANTIALIAS_NONE) + ctx.set_font_options(opts) if angle: ctx.rotate(np.deg2rad(-angle)) ctx.show_text(s) @@ -266,7 +265,7 @@ def _draw_mathtext(self, gc, x, y, s, prop, angle): ctx.move_to(ox, -oy) ctx.select_font_face( *_cairo_font_args_from_font_prop(ttfFontProperty(font))) - ctx.set_font_size(fontsize * self.dpi / 72) + ctx.set_font_size(self.points_to_pixels(fontsize)) ctx.show_text(chr(idx)) for ox, oy, w, h in rects: @@ -298,9 +297,7 @@ def get_text_width_height_descent(self, s, prop, ismath): # save/restore prevents the problem ctx.save() ctx.select_font_face(*_cairo_font_args_from_font_prop(prop)) - # Cairo (says it) uses 1/96 inch user space units, ref: cairo_gstate.c - # but if /96.0 is used the font is too small - ctx.set_font_size(prop.get_size_in_points() * self.dpi / 72) + ctx.set_font_size(self.points_to_pixels(prop.get_size_in_points())) y_bearing, w, h = ctx.text_extents(s)[1:4] ctx.restore() @@ -348,9 +345,9 @@ def set_alpha(self, alpha): else: self.ctx.set_source_rgba(rgb[0], rgb[1], rgb[2], rgb[3]) - # def set_antialiased(self, b): - # cairo has many antialiasing modes, we need to pick one for True and - # one for False. + def set_antialiased(self, b): + self.ctx.set_antialias( + cairo.ANTIALIAS_DEFAULT if b else cairo.ANTIALIAS_NONE) def set_capstyle(self, cs): self.ctx.set_line_cap(_api.check_getitem(self._capd, capstyle=cs)) @@ -411,6 +408,18 @@ def __init__(self, slices, data): class FigureCanvasCairo(FigureCanvasBase): + @property + def _renderer(self): + # In theory, _renderer should be set in __init__, but GUI canvas + # subclasses (FigureCanvasFooCairo) don't always interact well with + # multiple inheritance (FigureCanvasFoo inits but doesn't super-init + # FigureCanvasCairo), so initialize it in the getter instead. + if not hasattr(self, "_cached_renderer"): + self._cached_renderer = RendererCairo(self.figure.dpi) + return self._cached_renderer + + def get_renderer(self): + return self._renderer def copy_from_bbox(self, bbox): surface = self._renderer.gc.ctx.get_target() @@ -445,11 +454,9 @@ def restore_region(self, region): surface.mark_dirty_rectangle( slx.start, sly.start, slx.stop - slx.start, sly.stop - sly.start) - @_check_savefig_extra_args def print_png(self, fobj): self._get_printed_image_surface().write_to_png(fobj) - @_check_savefig_extra_args def print_rgba(self, fobj): width, height = self.get_width_height() buf = self._get_printed_image_surface().get_data() @@ -459,28 +466,14 @@ def print_rgba(self, fobj): print_raw = print_rgba def _get_printed_image_surface(self): + self._renderer.dpi = self.figure.dpi width, height = self.get_width_height() - renderer = RendererCairo(self.figure.dpi) - renderer.set_width_height(width, height) surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) - renderer.set_ctx_from_surface(surface) - self.figure.draw(renderer) + self._renderer.set_context(cairo.Context(surface)) + self.figure.draw(self._renderer) return surface - def print_pdf(self, fobj, *args, **kwargs): - return self._save(fobj, 'pdf', *args, **kwargs) - - def print_ps(self, fobj, *args, **kwargs): - return self._save(fobj, 'ps', *args, **kwargs) - - def print_svg(self, fobj, *args, **kwargs): - return self._save(fobj, 'svg', *args, **kwargs) - - def print_svgz(self, fobj, *args, **kwargs): - return self._save(fobj, 'svgz', *args, **kwargs) - - @_check_savefig_extra_args - def _save(self, fo, fmt, *, orientation='portrait'): + def _save(self, fmt, fobj, *, orientation='portrait'): # save PDF/PS/SVG dpi = 72 @@ -496,45 +489,62 @@ def _save(self, fo, fmt, *, orientation='portrait'): if not hasattr(cairo, 'PSSurface'): raise RuntimeError('cairo has not been compiled with PS ' 'support enabled') - surface = cairo.PSSurface(fo, width_in_points, height_in_points) + surface = cairo.PSSurface(fobj, width_in_points, height_in_points) elif fmt == 'pdf': if not hasattr(cairo, 'PDFSurface'): raise RuntimeError('cairo has not been compiled with PDF ' 'support enabled') - surface = cairo.PDFSurface(fo, width_in_points, height_in_points) + surface = cairo.PDFSurface(fobj, width_in_points, height_in_points) elif fmt in ('svg', 'svgz'): if not hasattr(cairo, 'SVGSurface'): raise RuntimeError('cairo has not been compiled with SVG ' 'support enabled') if fmt == 'svgz': - if isinstance(fo, str): - fo = gzip.GzipFile(fo, 'wb') + if isinstance(fobj, str): + fobj = gzip.GzipFile(fobj, 'wb') else: - fo = gzip.GzipFile(None, 'wb', fileobj=fo) - surface = cairo.SVGSurface(fo, width_in_points, height_in_points) + fobj = gzip.GzipFile(None, 'wb', fileobj=fobj) + surface = cairo.SVGSurface(fobj, width_in_points, height_in_points) else: raise ValueError("Unknown format: {!r}".format(fmt)) - # surface.set_dpi() can be used - renderer = RendererCairo(self.figure.dpi) - renderer.set_width_height(width_in_points, height_in_points) - renderer.set_ctx_from_surface(surface) - ctx = renderer.gc.ctx + self._renderer.dpi = self.figure.dpi + self._renderer.set_context(cairo.Context(surface)) + ctx = self._renderer.gc.ctx if orientation == 'landscape': ctx.rotate(np.pi / 2) ctx.translate(0, -height_in_points) # Perhaps add an '%%Orientation: Landscape' comment? - self.figure.draw(renderer) + self.figure.draw(self._renderer) ctx.show_page() surface.finish() if fmt == 'svgz': - fo.close() + fobj.close() + + print_pdf = functools.partialmethod(_save, "pdf") + print_ps = functools.partialmethod(_save, "ps") + print_svg = functools.partialmethod(_save, "svg") + print_svgz = functools.partialmethod(_save, "svgz") + + +@_api.deprecated("3.6") +class _RendererGTKCairo(RendererCairo): + def set_context(self, ctx): + if (cairo.__name__ == "cairocffi" + and not isinstance(ctx, cairo.Context)): + ctx = cairo.Context._from_pointer( + cairo.ffi.cast( + 'cairo_t **', + id(ctx) + object.__basicsize__)[0], + incref=True) + self.gc.ctx = ctx @_Backend.export class _BackendCairo(_Backend): + backend_version = cairo.version FigureCanvas = FigureCanvasCairo FigureManager = FigureManagerBase diff --git a/lib/matplotlib/backends/backend_gtk3.py b/lib/matplotlib/backends/backend_gtk3.py index 1dbf5d93a929..cb1062cf157c 100644 --- a/lib/matplotlib/backends/backend_gtk3.py +++ b/lib/matplotlib/backends/backend_gtk3.py @@ -6,12 +6,10 @@ import matplotlib as mpl from matplotlib import _api, backend_tools, cbook -from matplotlib._pylab_helpers import Gcf from matplotlib.backend_bases import ( - _Backend, FigureCanvasBase, FigureManagerBase, NavigationToolbar2, - StatusbarBase, TimerBase, ToolContainerBase, cursors) -from matplotlib.figure import Figure -from matplotlib.widgets import SubplotTool + FigureCanvasBase, ToolContainerBase, + CloseEvent, KeyEvent, LocationEvent, MouseEvent, ResizeEvent) +from matplotlib.backend_tools import Cursors try: import gi @@ -28,67 +26,53 @@ raise ImportError from e from gi.repository import Gio, GLib, GObject, Gtk, Gdk +from . import _backend_gtk +from ._backend_gtk import ( + _BackendGTK, _FigureManagerGTK, _NavigationToolbar2GTK, + TimerGTK as TimerGTK3, +) _log = logging.getLogger(__name__) -backend_version = "%s.%s.%s" % ( - Gtk.get_major_version(), Gtk.get_micro_version(), Gtk.get_minor_version()) -try: - _display = Gdk.Display.get_default() - cursord = { - cursors.MOVE: Gdk.Cursor.new_from_name(_display, "move"), - cursors.HAND: Gdk.Cursor.new_from_name(_display, "pointer"), - cursors.POINTER: Gdk.Cursor.new_from_name(_display, "default"), - cursors.SELECT_REGION: Gdk.Cursor.new_from_name(_display, "crosshair"), - cursors.WAIT: Gdk.Cursor.new_from_name(_display, "wait"), - } -except TypeError as exc: - # Happens when running headless. Convert to ImportError to cooperate with - # backend switching. - raise ImportError(exc) from exc - - -class TimerGTK3(TimerBase): - """Subclass of `.TimerBase` using GTK3 timer events.""" - - def __init__(self, *args, **kwargs): - self._timer = None - super().__init__(*args, **kwargs) - - def _timer_start(self): - # Need to stop it, otherwise we potentially leak a timer id that will - # never be stopped. - self._timer_stop() - self._timer = GLib.timeout_add(self._interval, self._on_timer) - - def _timer_stop(self): - if self._timer is not None: - GLib.source_remove(self._timer) - self._timer = None - - def _timer_set_interval(self): - # Only stop and restart it if the timer has already been started - if self._timer is not None: - self._timer_stop() - self._timer_start() - - def _on_timer(self): - super()._on_timer() - - # Gtk timeout_add() requires that the callback returns True if it - # is to be called again. - if self.callbacks and not self._single: - return True - else: - self._timer = None - return False - - -class FigureCanvasGTK3(Gtk.DrawingArea, FigureCanvasBase): +@_api.caching_module_getattr # module-level deprecations +class __getattr__: + @_api.deprecated("3.5", obj_type="") + @property + def cursord(self): + try: + new_cursor = functools.partial( + Gdk.Cursor.new_from_name, Gdk.Display.get_default()) + return { + Cursors.MOVE: new_cursor("move"), + Cursors.HAND: new_cursor("pointer"), + Cursors.POINTER: new_cursor("default"), + Cursors.SELECT_REGION: new_cursor("crosshair"), + Cursors.WAIT: new_cursor("wait"), + } + except TypeError: + return {} + + icon_filename = _api.deprecated("3.6", obj_type="")(property( + lambda self: + "matplotlib.png" if sys.platform == "win32" else "matplotlib.svg")) + window_icon = _api.deprecated("3.6", obj_type="")(property( + lambda self: + str(cbook._get_data_path("images", __getattr__("icon_filename"))))) + + +@functools.lru_cache() +def _mpl_to_gtk_cursor(mpl_cursor): + return Gdk.Cursor.new_from_name( + Gdk.Display.get_default(), + _backend_gtk.mpl_to_gtk_cursor_name(mpl_cursor)) + + +class FigureCanvasGTK3(FigureCanvasBase, Gtk.DrawingArea): required_interactive_framework = "gtk3" _timer_cls = TimerGTK3 + manager_class = _api.classproperty(lambda cls: FigureManagerGTK3) # Setting this as a static constant prevents # this resulting expression from leaking event_mask = (Gdk.EventMask.BUTTON_PRESS_MASK @@ -99,28 +83,27 @@ class FigureCanvasGTK3(Gtk.DrawingArea, FigureCanvasBase): | Gdk.EventMask.ENTER_NOTIFY_MASK | Gdk.EventMask.LEAVE_NOTIFY_MASK | Gdk.EventMask.POINTER_MOTION_MASK - | Gdk.EventMask.POINTER_MOTION_HINT_MASK | Gdk.EventMask.SCROLL_MASK) def __init__(self, figure=None): - FigureCanvasBase.__init__(self, figure) - GObject.GObject.__init__(self) + super().__init__(figure=figure) self._idle_draw_id = 0 - self._lastCursor = None self._rubberband_rect = None self.connect('scroll_event', self.scroll_event) self.connect('button_press_event', self.button_press_event) self.connect('button_release_event', self.button_release_event) self.connect('configure_event', self.configure_event) + self.connect('screen-changed', self._update_device_pixel_ratio) + self.connect('notify::scale-factor', self._update_device_pixel_ratio) self.connect('draw', self.on_draw_event) self.connect('draw', self._post_draw) self.connect('key_press_event', self.key_press_event) self.connect('key_release_event', self.key_release_event) self.connect('motion_notify_event', self.motion_notify_event) - self.connect('leave_notify_event', self.leave_notify_event) self.connect('enter_notify_event', self.enter_notify_event) + self.connect('leave_notify_event', self.leave_notify_event) self.connect('size_allocate', self.size_allocate) self.set_events(self.__class__.event_mask) @@ -133,83 +116,88 @@ def __init__(self, figure=None): style_ctx.add_provider(css, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) style_ctx.add_class("matplotlib-canvas") - renderer_init = _api.deprecate_method_override( - __class__._renderer_init, self, allow_empty=True, since="3.3", - addendum="Please initialize the renderer, if needed, in the " - "subclass' __init__; a fully empty _renderer_init implementation " - "may be kept for compatibility with earlier versions of " - "Matplotlib.") - if renderer_init: - renderer_init() - - @_api.deprecated("3.3", alternative="__init__") - def _renderer_init(self): - pass - def destroy(self): - #Gtk.DrawingArea.destroy(self) - self.close_event() + CloseEvent("close_event", self)._process() + + def set_cursor(self, cursor): + # docstring inherited + window = self.get_property("window") + if window is not None: + window.set_cursor(_mpl_to_gtk_cursor(cursor)) + context = GLib.MainContext.default() + context.iteration(True) + + def _mpl_coords(self, event=None): + """ + Convert the position of a GTK event, or of the current cursor position + if *event* is None, to Matplotlib coordinates. + + GTK use logical pixels, but the figure is scaled to physical pixels for + rendering. Transform to physical pixels so that all of the down-stream + transforms work as expected. + + Also, the origin is different and needs to be corrected. + """ + if event is None: + window = self.get_window() + t, x, y, state = window.get_device_position( + window.get_display().get_device_manager().get_client_pointer()) + else: + x, y = event.x, event.y + x = x * self.device_pixel_ratio + # flip y so y=0 is bottom of canvas + y = self.figure.bbox.height - y * self.device_pixel_ratio + return x, y def scroll_event(self, widget, event): - x = event.x - # flipy so y=0 is bottom of canvas - y = self.get_allocation().height - event.y step = 1 if event.direction == Gdk.ScrollDirection.UP else -1 - FigureCanvasBase.scroll_event(self, x, y, step, guiEvent=event) + MouseEvent("scroll_event", self, *self._mpl_coords(event), step=step, + guiEvent=event)._process() return False # finish event propagation? def button_press_event(self, widget, event): - x = event.x - # flipy so y=0 is bottom of canvas - y = self.get_allocation().height - event.y - FigureCanvasBase.button_press_event( - self, x, y, event.button, guiEvent=event) + MouseEvent("button_press_event", self, + *self._mpl_coords(event), event.button, + guiEvent=event)._process() return False # finish event propagation? def button_release_event(self, widget, event): - x = event.x - # flipy so y=0 is bottom of canvas - y = self.get_allocation().height - event.y - FigureCanvasBase.button_release_event( - self, x, y, event.button, guiEvent=event) + MouseEvent("button_release_event", self, + *self._mpl_coords(event), event.button, + guiEvent=event)._process() return False # finish event propagation? def key_press_event(self, widget, event): - key = self._get_key(event) - FigureCanvasBase.key_press_event(self, key, guiEvent=event) + KeyEvent("key_press_event", self, + self._get_key(event), *self._mpl_coords(), + guiEvent=event)._process() return True # stop event propagation def key_release_event(self, widget, event): - key = self._get_key(event) - FigureCanvasBase.key_release_event(self, key, guiEvent=event) + KeyEvent("key_release_event", self, + self._get_key(event), *self._mpl_coords(), + guiEvent=event)._process() return True # stop event propagation def motion_notify_event(self, widget, event): - if event.is_hint: - t, x, y, state = event.window.get_device_position(event.device) - else: - x, y = event.x, event.y - - # flipy so y=0 is bottom of canvas - y = self.get_allocation().height - y - FigureCanvasBase.motion_notify_event(self, x, y, guiEvent=event) + MouseEvent("motion_notify_event", self, *self._mpl_coords(event), + guiEvent=event)._process() return False # finish event propagation? - def leave_notify_event(self, widget, event): - FigureCanvasBase.leave_notify_event(self, event) - def enter_notify_event(self, widget, event): - x = event.x - # flipy so y=0 is bottom of canvas - y = self.get_allocation().height - event.y - FigureCanvasBase.enter_notify_event(self, guiEvent=event, xy=(x, y)) + LocationEvent("figure_enter_event", self, *self._mpl_coords(event), + guiEvent=event)._process() + + def leave_notify_event(self, widget, event): + LocationEvent("figure_leave_event", self, *self._mpl_coords(event), + guiEvent=event)._process() def size_allocate(self, widget, allocation): dpival = self.figure.dpi - winch = allocation.width / dpival - hinch = allocation.height / dpival + winch = allocation.width * self.device_pixel_ratio / dpival + hinch = allocation.height * self.device_pixel_ratio / dpival self.figure.set_size_inches(winch, hinch, forward=False) - FigureCanvasBase.resize_event(self) + ResizeEvent("resize_event", self)._process() self.draw_idle() def _get_key(self, event): @@ -226,13 +214,24 @@ def _get_key(self, event): for key_mask, prefix in modifiers: if event.state & key_mask: if not (prefix == 'shift' and unikey.isprintable()): - key = '{0}+{1}'.format(prefix, key) + key = f'{prefix}+{key}' return key + def _update_device_pixel_ratio(self, *args, **kwargs): + # We need to be careful in cases with mixed resolution displays if + # device_pixel_ratio changes. + if self._set_device_pixel_ratio(self.get_scale_factor()): + # The easiest way to resize the canvas is to emit a resize event + # since we implement all the logic for resizing the canvas for that + # event. + self.queue_resize() + self.queue_draw() + def configure_event(self, widget, event): if widget.get_property("window") is None: return - w, h = event.width, event.height + w = event.width * self.device_pixel_ratio + h = event.height * self.device_pixel_ratio if w < 3 or h < 3: return # empty fig # resize the figure (in inches) @@ -249,7 +248,8 @@ def _post_draw(self, widget, ctx): if self._rubberband_rect is None: return - x0, y0, w, h = self._rubberband_rect + x0, y0, w, h = (dim / self.device_pixel_ratio + for dim in self._rubberband_rect) x1 = x0 + w y1 = y0 + h @@ -297,157 +297,15 @@ def idle_draw(*args): def flush_events(self): # docstring inherited - Gdk.threads_enter() - while Gtk.events_pending(): - Gtk.main_iteration() - Gdk.flush() - Gdk.threads_leave() - - -class FigureManagerGTK3(FigureManagerBase): - """ - Attributes - ---------- - canvas : `FigureCanvas` - The FigureCanvas instance - num : int or str - The Figure number - toolbar : Gtk.Toolbar - The Gtk.Toolbar - vbox : Gtk.VBox - The Gtk.VBox containing the canvas and toolbar - window : Gtk.Window - The Gtk.Window - - """ - def __init__(self, canvas, num): - self.window = Gtk.Window() - super().__init__(canvas, num) - - self.window.set_wmclass("matplotlib", "Matplotlib") - try: - self.window.set_icon_from_file(window_icon) - except Exception: - # Some versions of gtk throw a glib.GError but not all, so I am not - # sure how to catch it. I am unhappy doing a blanket catch here, - # but am not sure what a better way is - JDH - _log.info('Could not load matplotlib icon: %s', sys.exc_info()[1]) - - self.vbox = Gtk.Box() - self.vbox.set_property("orientation", Gtk.Orientation.VERTICAL) - self.window.add(self.vbox) - self.vbox.show() - - self.canvas.show() - - self.vbox.pack_start(self.canvas, True, True, 0) - # calculate size for window - w = int(self.canvas.figure.bbox.width) - h = int(self.canvas.figure.bbox.height) - - self.toolbar = self._get_toolbar() - - if self.toolmanager: - backend_tools.add_tools_to_manager(self.toolmanager) - if self.toolbar: - backend_tools.add_tools_to_container(self.toolbar) - - if self.toolbar is not None: - self.toolbar.show() - self.vbox.pack_end(self.toolbar, False, False, 0) - min_size, nat_size = self.toolbar.get_preferred_size() - h += nat_size.height - - self.window.set_default_size(w, h) - - self._destroying = False - self.window.connect("destroy", lambda *args: Gcf.destroy(self)) - self.window.connect("delete_event", lambda *args: Gcf.destroy(self)) - if mpl.is_interactive(): - self.window.show() - self.canvas.draw_idle() - - self.canvas.grab_focus() - - def destroy(self, *args): - if self._destroying: - # Otherwise, this can be called twice when the user presses 'q', - # which calls Gcf.destroy(self), then this destroy(), then triggers - # Gcf.destroy(self) once again via - # `connect("destroy", lambda *args: Gcf.destroy(self))`. - return - self._destroying = True - self.vbox.destroy() - self.window.destroy() - self.canvas.destroy() - if self.toolbar: - self.toolbar.destroy() - - if (Gcf.get_num_fig_managers() == 0 and not mpl.is_interactive() and - Gtk.main_level() >= 1): - Gtk.main_quit() - - def show(self): - # show the figure window - self.window.show() - self.canvas.draw() - if mpl.rcParams['figure.raise_window']: - if self.window.get_window(): - self.window.present() - else: - # If this is called by a callback early during init, - # self.window (a GtkWindow) may not have an associated - # low-level GdkWindow (self.window.get_window()) yet, and - # present() would crash. - _api.warn_external("Cannot raise window yet to be setup") - - def full_screen_toggle(self): - self._full_screen_flag = not self._full_screen_flag - if self._full_screen_flag: - self.window.fullscreen() - else: - self.window.unfullscreen() - _full_screen_flag = False - - def _get_toolbar(self): - # must be inited after the window, drawingArea and figure - # attrs are set - if mpl.rcParams['toolbar'] == 'toolbar2': - toolbar = NavigationToolbar2GTK3(self.canvas, self.window) - elif mpl.rcParams['toolbar'] == 'toolmanager': - toolbar = ToolbarGTK3(self.toolmanager) - else: - toolbar = None - return toolbar - - def get_window_title(self): - return self.window.get_title() - - def set_window_title(self, title): - self.window.set_title(title) - - def resize(self, width, height): - """Set the canvas size in pixels.""" - if self.toolbar: - toolbar_size = self.toolbar.size_request() - height += toolbar_size.height - canvas_size = self.canvas.get_allocation() - if canvas_size.width == canvas_size.height == 1: - # A canvas size of (1, 1) cannot exist in most cases, because - # window decorations would prevent such a small window. This call - # must be before the window has been mapped and widgets have been - # sized, so just change the window's starting size. - self.window.set_default_size(width, height) - else: - self.window.resize(width, height) - + context = GLib.MainContext.default() + while context.pending(): + context.iteration(True) -class NavigationToolbar2GTK3(NavigationToolbar2, Gtk.Toolbar): - ctx = _api.deprecated("3.3")(property( - lambda self: self.canvas.get_property("window").cairo_create())) - def __init__(self, canvas, window): - self.win = window +class NavigationToolbar2GTK3(_NavigationToolbar2GTK, Gtk.Toolbar): + @_api.delete_parameter("3.6", "window") + def __init__(self, canvas, window=None): + self._win = window GObject.GObject.__init__(self) self.set_style(Gtk.ToolbarStyle.ICONS) @@ -462,21 +320,16 @@ def __init__(self, canvas, window): str(cbook._get_data_path('images', f'{image_file}-symbolic.svg'))), Gtk.IconSize.LARGE_TOOLBAR) - self._gtk_ids[text] = tbutton = ( + self._gtk_ids[text] = button = ( Gtk.ToggleToolButton() if callback in ['zoom', 'pan'] else Gtk.ToolButton()) - tbutton.set_label(text) - tbutton.set_icon_widget(image) - self.insert(tbutton, -1) + button.set_label(text) + button.set_icon_widget(image) # Save the handler id, so that we can block it as needed. - tbutton._signal_handler = tbutton.connect( + button._signal_handler = button.connect( 'clicked', getattr(self, callback)) - tbutton.set_tooltip_text(tooltip_text) - - toolitem = Gtk.SeparatorToolItem() - self.insert(toolitem, -1) - toolitem.set_draw(False) - toolitem.set_expand(True) + button.set_tooltip_text(tooltip_text) + self.insert(button, -1) # This filler item ensures the toolbar is always at least two text # lines high. Otherwise the canvas gets redrawn as the mouse hovers @@ -487,51 +340,20 @@ def __init__(self, canvas, window): label = Gtk.Label() label.set_markup( '\N{NO-BREAK SPACE}\n\N{NO-BREAK SPACE}') + toolitem.set_expand(True) # Push real message to the right. toolitem.add(label) toolitem = Gtk.ToolItem() self.insert(toolitem, -1) self.message = Gtk.Label() + self.message.set_justify(Gtk.Justification.RIGHT) toolitem.add(self.message) self.show_all() - NavigationToolbar2.__init__(self, canvas) - - def set_message(self, s): - escaped = GLib.markup_escape_text(s) - self.message.set_markup(f'{escaped}') + _NavigationToolbar2GTK.__init__(self, canvas) - def set_cursor(self, cursor): - window = self.canvas.get_property("window") - if window is not None: - window.set_cursor(cursord[cursor]) - Gtk.main_iteration() - - def draw_rubberband(self, event, x0, y0, x1, y1): - height = self.canvas.figure.bbox.height - y1 = height - y1 - y0 = height - y0 - rect = [int(val) for val in (x0, y0, x1 - x0, y1 - y0)] - self.canvas._draw_rubberband(rect) - - def remove_rubberband(self): - self.canvas._draw_rubberband(None) - - def _update_buttons_checked(self): - for name, active in [("Pan", "PAN"), ("Zoom", "ZOOM")]: - button = self._gtk_ids.get(name) - if button: - with button.handler_block(button._signal_handler): - button.set_active(self.mode.name == active) - - def pan(self, *args): - super().pan(*args) - self._update_buttons_checked() - - def zoom(self, *args): - super().zoom(*args) - self._update_buttons_checked() + win = _api.deprecated("3.6")(property(lambda self: self._win)) def save_figure(self, *args): dialog = Gtk.FileChooserDialog( @@ -546,7 +368,7 @@ def save_figure(self, *args): ff = Gtk.FileFilter() ff.set_name(name) for fmt in fmts: - ff.add_pattern("*." + fmt) + ff.add_pattern(f'*.{fmt}') dialog.add_filter(ff) if self.canvas.get_default_filetype() in fmts: dialog.set_filter(ff) @@ -556,7 +378,7 @@ def on_notify_filter(*args): name = dialog.get_filter().get_name() fmt = self.canvas.get_supported_filetypes_grouped()[name][0] dialog.set_current_name( - str(Path(dialog.get_current_name()).with_suffix("." + fmt))) + str(Path(dialog.get_current_name()).with_suffix(f'.{fmt}'))) dialog.set_current_folder(mpl.rcParams["savefig.directory"]) dialog.set_current_name(self.canvas.get_default_filename()) @@ -575,15 +397,11 @@ def on_notify_filter(*args): try: self.canvas.figure.savefig(fname, format=fmt) except Exception as e: - error_msg_gtk(str(e), parent=self) - - def set_history_buttons(self): - can_backward = self._nav_stack._pos > 0 - can_forward = self._nav_stack._pos < len(self._nav_stack._elements) - 1 - if 'Back' in self._gtk_ids: - self._gtk_ids['Back'].set_sensitive(can_backward) - if 'Forward' in self._gtk_ids: - self._gtk_ids['Forward'].set_sensitive(can_forward) + dialog = Gtk.MessageDialog( + parent=self.canvas.get_toplevel(), message_format=str(e), + type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK) + dialog.run() + dialog.destroy() class ToolbarGTK3(ToolContainerBase, Gtk.Box): @@ -594,6 +412,7 @@ def __init__(self, toolmanager): Gtk.Box.__init__(self) self.set_property('orientation', Gtk.Orientation.HORIZONTAL) self._message = Gtk.Label() + self._message.set_justify(Gtk.Justification.RIGHT) self.pack_end(self._message, False, False, 0) self.show_all() self._groups = {} @@ -602,26 +421,26 @@ def __init__(self, toolmanager): def add_toolitem(self, name, group, position, image_file, description, toggle): if toggle: - tbutton = Gtk.ToggleToolButton() + button = Gtk.ToggleToolButton() else: - tbutton = Gtk.ToolButton() - tbutton.set_label(name) + button = Gtk.ToolButton() + button.set_label(name) if image_file is not None: image = Gtk.Image.new_from_gicon( Gio.Icon.new_for_string(image_file), Gtk.IconSize.LARGE_TOOLBAR) - tbutton.set_icon_widget(image) + button.set_icon_widget(image) if position is None: position = -1 - self._add_button(tbutton, group, position) - signal = tbutton.connect('clicked', self._call_tool, name) - tbutton.set_tooltip_text(description) - tbutton.show_all() + self._add_button(button, group, position) + signal = button.connect('clicked', self._call_tool, name) + button.set_tooltip_text(description) + button.show_all() self._toolitems.setdefault(name, []) - self._toolitems[name].append((tbutton, signal)) + self._toolitems[name].append((button, signal)) def _add_button(self, button, group, position): if group not in self._groups: @@ -647,7 +466,7 @@ def toggle_toolitem(self, name, toggled): def remove_toolitem(self, name): if name not in self._toolitems: - self.toolmanager.message_event('%s Not in toolbar' % name, self) + self.toolmanager.message_event(f'{name} not in toolbar', self) return for group in self._groups: @@ -666,52 +485,21 @@ def set_message(self, s): self._message.set_label(s) -@_api.deprecated("3.3") -class StatusbarGTK3(StatusbarBase, Gtk.Statusbar): - def __init__(self, *args, **kwargs): - StatusbarBase.__init__(self, *args, **kwargs) - Gtk.Statusbar.__init__(self) - self._context = self.get_context_id('message') - - def set_message(self, s): - self.pop(self._context) - self.push(self._context, s) - - -class RubberbandGTK3(backend_tools.RubberbandBase): - def draw_rubberband(self, x0, y0, x1, y1): - NavigationToolbar2GTK3.draw_rubberband( - self._make_classic_style_pseudo_toolbar(), None, x0, y0, x1, y1) - - def remove_rubberband(self): - NavigationToolbar2GTK3.remove_rubberband( - self._make_classic_style_pseudo_toolbar()) - - +@backend_tools._register_tool_class(FigureCanvasGTK3) class SaveFigureGTK3(backend_tools.SaveFigureBase): def trigger(self, *args, **kwargs): - - class PseudoToolbar: - canvas = self.figure.canvas - - return NavigationToolbar2GTK3.save_figure(PseudoToolbar()) + NavigationToolbar2GTK3.save_figure( + self._make_classic_style_pseudo_toolbar()) +@_api.deprecated("3.5", alternative="ToolSetCursor") class SetCursorGTK3(backend_tools.SetCursorBase): def set_cursor(self, cursor): NavigationToolbar2GTK3.set_cursor( self._make_classic_style_pseudo_toolbar(), cursor) -class ConfigureSubplotsGTK3(backend_tools.ConfigureSubplotsBase, Gtk.Window): - def _get_canvas(self, fig): - return self.canvas.__class__(fig) - - def trigger(self, *args): - NavigationToolbar2GTK3.configure_subplots( - self._make_classic_style_pseudo_toolbar(), None) - - +@backend_tools._register_tool_class(FigureCanvasGTK3) class HelpGTK3(backend_tools.ToolHelpBase): def _normalize_shortcut(self, key): """ @@ -797,6 +585,7 @@ def trigger(self, *args): self._show_shortcuts_dialog() +@backend_tools._register_tool_class(FigureCanvasGTK3) class ToolCopyToClipboardGTK3(backend_tools.ToolCopyToClipboardBase): def trigger(self, *args, **kwargs): clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) @@ -806,14 +595,7 @@ def trigger(self, *args, **kwargs): clipboard.set_image(pb) -# Define the file to use as the GTk icon -if sys.platform == 'win32': - icon_filename = 'matplotlib.png' -else: - icon_filename = 'matplotlib.svg' -window_icon = str(cbook._get_data_path('images', icon_filename)) - - +@_api.deprecated("3.6") def error_msg_gtk(msg, parent=None): if parent is not None: # find the toplevel Gtk.Window parent = parent.get_toplevel() @@ -828,23 +610,19 @@ def error_msg_gtk(msg, parent=None): dialog.destroy() -backend_tools.ToolSaveFigure = SaveFigureGTK3 -backend_tools.ToolConfigureSubplots = ConfigureSubplotsGTK3 -backend_tools.ToolSetCursor = SetCursorGTK3 -backend_tools.ToolRubberband = RubberbandGTK3 -backend_tools.ToolHelp = HelpGTK3 -backend_tools.ToolCopyToClipboard = ToolCopyToClipboardGTK3 - Toolbar = ToolbarGTK3 +backend_tools._register_tool_class( + FigureCanvasGTK3, _backend_gtk.ConfigureSubplotsGTK) +backend_tools._register_tool_class( + FigureCanvasGTK3, _backend_gtk.RubberbandGTK) + +class FigureManagerGTK3(_FigureManagerGTK): + _toolbar2_class = NavigationToolbar2GTK3 + _toolmanager_toolbar_class = ToolbarGTK3 -@_Backend.export -class _BackendGTK3(_Backend): + +@_BackendGTK.export +class _BackendGTK3(_BackendGTK): FigureCanvas = FigureCanvasGTK3 FigureManager = FigureManagerGTK3 - - @staticmethod - def mainloop(): - if Gtk.main_level() == 0: - cbook._setup_new_guiapp() - Gtk.main() diff --git a/lib/matplotlib/backends/backend_gtk3agg.py b/lib/matplotlib/backends/backend_gtk3agg.py index ecd15327aa02..10aa15ac2a6b 100644 --- a/lib/matplotlib/backends/backend_gtk3agg.py +++ b/lib/matplotlib/backends/backend_gtk3agg.py @@ -1,26 +1,23 @@ import numpy as np -from .. import cbook -try: - from . import backend_cairo -except ImportError as e: - raise ImportError('backend Gtk3Agg requires cairo') from e +from .. import _api, cbook, transforms from . import backend_agg, backend_gtk3 -from .backend_cairo import cairo from .backend_gtk3 import Gtk, _BackendGTK3 -from matplotlib import transforms +import cairo # Presence of cairo is already checked by _backend_gtk. -class FigureCanvasGTK3Agg(backend_gtk3.FigureCanvasGTK3, - backend_agg.FigureCanvasAgg): + +class FigureCanvasGTK3Agg(backend_agg.FigureCanvasAgg, + backend_gtk3.FigureCanvasGTK3): def __init__(self, figure): - backend_gtk3.FigureCanvasGTK3.__init__(self, figure) + super().__init__(figure=figure) self._bbox_queue = [] def on_draw_event(self, widget, ctx): - """GtkDrawable draw event, like expose_event in GTK 2.X.""" + scale = self.device_pixel_ratio allocation = self.get_allocation() - w, h = allocation.width, allocation.height + w = allocation.width * scale + h = allocation.height * scale if not len(self._bbox_queue): Gtk.render_background( @@ -31,8 +28,6 @@ def on_draw_event(self, widget, ctx): else: bbox_queue = self._bbox_queue - ctx = backend_cairo._to_context(ctx) - for bbox in bbox_queue: x = int(bbox.x0) y = h - int(bbox.y1) @@ -43,7 +38,8 @@ def on_draw_event(self, widget, ctx): np.asarray(self.copy_from_bbox(bbox))) image = cairo.ImageSurface.create_for_data( buf.ravel().data, cairo.FORMAT_ARGB32, width, height) - ctx.set_source_surface(image, x, y) + image.set_device_scale(scale, scale) + ctx.set_source_surface(image, x / scale, y / scale) ctx.paint() if len(self._bbox_queue): @@ -57,25 +53,18 @@ def blit(self, bbox=None): if bbox is None: bbox = self.figure.bbox + scale = self.device_pixel_ratio allocation = self.get_allocation() - x = int(bbox.x0) - y = allocation.height - int(bbox.y1) - width = int(bbox.x1) - int(bbox.x0) - height = int(bbox.y1) - int(bbox.y0) + 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() - - def print_png(self, filename, *args, **kwargs): - # Do this so we can save the resolution of figure in the PNG file - agg = self.switch_backends(backend_agg.FigureCanvasAgg) - return agg.print_png(filename, *args, **kwargs) - +@_api.deprecated("3.6", alternative="backend_gtk3.FigureManagerGTK3") class FigureManagerGTK3Agg(backend_gtk3.FigureManagerGTK3): pass @@ -83,4 +72,3 @@ class FigureManagerGTK3Agg(backend_gtk3.FigureManagerGTK3): @_BackendGTK3.export class _BackendGTK3Cairo(_BackendGTK3): FigureCanvas = FigureCanvasGTK3Agg - FigureManager = FigureManagerGTK3Agg diff --git a/lib/matplotlib/backends/backend_gtk3cairo.py b/lib/matplotlib/backends/backend_gtk3cairo.py index af290902d8a6..49215510c178 100644 --- a/lib/matplotlib/backends/backend_gtk3cairo.py +++ b/lib/matplotlib/backends/backend_gtk3cairo.py @@ -1,33 +1,24 @@ from contextlib import nullcontext -from . import backend_cairo, backend_gtk3 -from .backend_gtk3 import Gtk, _BackendGTK3 +from .backend_cairo import ( # noqa + FigureCanvasCairo, _RendererGTKCairo as RendererGTK3Cairo) +from .backend_gtk3 import Gtk, FigureCanvasGTK3, _BackendGTK3 -class RendererGTK3Cairo(backend_cairo.RendererCairo): - def set_context(self, ctx): - self.gc.ctx = backend_cairo._to_context(ctx) - - -class FigureCanvasGTK3Cairo(backend_gtk3.FigureCanvasGTK3, - backend_cairo.FigureCanvasCairo): - - def __init__(self, figure): - super().__init__(figure) - self._renderer = RendererGTK3Cairo(self.figure.dpi) - +class FigureCanvasGTK3Cairo(FigureCanvasCairo, FigureCanvasGTK3): def on_draw_event(self, widget, ctx): - """GtkDrawable draw event.""" with (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar else nullcontext()): self._renderer.set_context(ctx) + scale = self.device_pixel_ratio + # Scale physical drawing to logical size. + ctx.scale(1 / scale, 1 / scale) allocation = self.get_allocation() Gtk.render_background( self.get_style_context(), ctx, allocation.x, allocation.y, allocation.width, allocation.height) - self._renderer.set_width_height( - allocation.width, allocation.height) + self._renderer.dpi = self.figure.dpi self.figure.draw(self._renderer) diff --git a/lib/matplotlib/backends/backend_gtk4.py b/lib/matplotlib/backends/backend_gtk4.py new file mode 100644 index 000000000000..923787150a8d --- /dev/null +++ b/lib/matplotlib/backends/backend_gtk4.py @@ -0,0 +1,578 @@ +import functools +import io +import os + +import matplotlib as mpl +from matplotlib import _api, backend_tools, cbook +from matplotlib.backend_bases import ( + FigureCanvasBase, ToolContainerBase, + KeyEvent, LocationEvent, MouseEvent, ResizeEvent) + +try: + import gi +except ImportError as err: + raise ImportError("The GTK4 backends require PyGObject") from err + +try: + # :raises ValueError: If module/version is already loaded, already + # required, or unavailable. + gi.require_version("Gtk", "4.0") +except ValueError as e: + # in this case we want to re-raise as ImportError so the + # auto-backend selection logic correctly skips. + raise ImportError from e + +from gi.repository import Gio, GLib, Gtk, Gdk, GdkPixbuf +from . import _backend_gtk +from ._backend_gtk import ( + _BackendGTK, _FigureManagerGTK, _NavigationToolbar2GTK, + TimerGTK as TimerGTK4, +) + + +class FigureCanvasGTK4(FigureCanvasBase, Gtk.DrawingArea): + required_interactive_framework = "gtk4" + supports_blit = False + _timer_cls = TimerGTK4 + manager_class = _api.classproperty(lambda cls: FigureManagerGTK4) + _context_is_scaled = False + + def __init__(self, figure=None): + super().__init__(figure=figure) + + self.set_hexpand(True) + self.set_vexpand(True) + + self._idle_draw_id = 0 + self._rubberband_rect = None + + self.set_draw_func(self._draw_func) + self.connect('resize', self.resize_event) + self.connect('notify::scale-factor', self._update_device_pixel_ratio) + + click = Gtk.GestureClick() + click.set_button(0) # All buttons. + click.connect('pressed', self.button_press_event) + click.connect('released', self.button_release_event) + self.add_controller(click) + + key = Gtk.EventControllerKey() + key.connect('key-pressed', self.key_press_event) + key.connect('key-released', self.key_release_event) + self.add_controller(key) + + motion = Gtk.EventControllerMotion() + motion.connect('motion', self.motion_notify_event) + motion.connect('enter', self.enter_notify_event) + motion.connect('leave', self.leave_notify_event) + self.add_controller(motion) + + scroll = Gtk.EventControllerScroll.new( + Gtk.EventControllerScrollFlags.VERTICAL) + scroll.connect('scroll', self.scroll_event) + self.add_controller(scroll) + + self.set_focusable(True) + + css = Gtk.CssProvider() + css.load_from_data(b".matplotlib-canvas { background-color: white; }") + style_ctx = self.get_style_context() + style_ctx.add_provider(css, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) + style_ctx.add_class("matplotlib-canvas") + + def destroy(self): + self.close_event() + + def set_cursor(self, cursor): + # docstring inherited + self.set_cursor_from_name(_backend_gtk.mpl_to_gtk_cursor_name(cursor)) + + def _mpl_coords(self, xy=None): + """ + Convert the *xy* position of a GTK event, or of the current cursor + position if *xy* is None, to Matplotlib coordinates. + + GTK use logical pixels, but the figure is scaled to physical pixels for + rendering. Transform to physical pixels so that all of the down-stream + transforms work as expected. + + Also, the origin is different and needs to be corrected. + """ + if xy is None: + surface = self.get_native().get_surface() + is_over, x, y, mask = surface.get_device_position( + self.get_display().get_default_seat().get_pointer()) + else: + x, y = xy + x = x * self.device_pixel_ratio + # flip y so y=0 is bottom of canvas + y = self.figure.bbox.height - y * self.device_pixel_ratio + return x, y + + def scroll_event(self, controller, dx, dy): + MouseEvent("scroll_event", self, + *self._mpl_coords(), step=dy)._process() + return True + + def button_press_event(self, controller, n_press, x, y): + MouseEvent("button_press_event", self, + *self._mpl_coords((x, y)), controller.get_current_button() + )._process() + self.grab_focus() + + def button_release_event(self, controller, n_press, x, y): + MouseEvent("button_release_event", self, + *self._mpl_coords((x, y)), controller.get_current_button() + )._process() + + def key_press_event(self, controller, keyval, keycode, state): + KeyEvent("key_press_event", self, + self._get_key(keyval, keycode, state), *self._mpl_coords() + )._process() + return True + + def key_release_event(self, controller, keyval, keycode, state): + KeyEvent("key_release_event", self, + self._get_key(keyval, keycode, state), *self._mpl_coords() + )._process() + return True + + def motion_notify_event(self, controller, x, y): + MouseEvent("motion_notify_event", self, + *self._mpl_coords((x, y)))._process() + + def leave_notify_event(self, controller): + LocationEvent("figure_leave_event", self, + *self._mpl_coords())._process() + + def enter_notify_event(self, controller, x, y): + LocationEvent("figure_enter_event", self, + *self._mpl_coords((x, y)))._process() + + def resize_event(self, area, width, height): + self._update_device_pixel_ratio() + dpi = self.figure.dpi + winch = width * self.device_pixel_ratio / dpi + hinch = height * self.device_pixel_ratio / dpi + self.figure.set_size_inches(winch, hinch, forward=False) + ResizeEvent("resize_event", self)._process() + self.draw_idle() + + def _get_key(self, keyval, keycode, state): + unikey = chr(Gdk.keyval_to_unicode(keyval)) + key = cbook._unikey_or_keysym_to_mplkey( + unikey, + Gdk.keyval_name(keyval)) + modifiers = [ + (Gdk.ModifierType.CONTROL_MASK, 'ctrl'), + (Gdk.ModifierType.ALT_MASK, 'alt'), + (Gdk.ModifierType.SHIFT_MASK, 'shift'), + (Gdk.ModifierType.SUPER_MASK, 'super'), + ] + for key_mask, prefix in modifiers: + if state & key_mask: + if not (prefix == 'shift' and unikey.isprintable()): + key = f'{prefix}+{key}' + return key + + def _update_device_pixel_ratio(self, *args, **kwargs): + # We need to be careful in cases with mixed resolution displays if + # device_pixel_ratio changes. + if self._set_device_pixel_ratio(self.get_scale_factor()): + self.draw() + + def _draw_rubberband(self, rect): + self._rubberband_rect = rect + # TODO: Only update the rubberband area. + self.queue_draw() + + def _draw_func(self, drawing_area, ctx, width, height): + self.on_draw_event(self, ctx) + self._post_draw(self, ctx) + + def _post_draw(self, widget, ctx): + if self._rubberband_rect is None: + return + + lw = 1 + dash = 3 + if not self._context_is_scaled: + x0, y0, w, h = (dim / self.device_pixel_ratio + for dim in self._rubberband_rect) + else: + x0, y0, w, h = self._rubberband_rect + lw *= self.device_pixel_ratio + dash *= self.device_pixel_ratio + x1 = x0 + w + y1 = y0 + h + + # Draw the lines from x0, y0 towards x1, y1 so that the + # dashes don't "jump" when moving the zoom box. + ctx.move_to(x0, y0) + ctx.line_to(x0, y1) + ctx.move_to(x0, y0) + ctx.line_to(x1, y0) + ctx.move_to(x0, y1) + ctx.line_to(x1, y1) + ctx.move_to(x1, y0) + ctx.line_to(x1, y1) + + ctx.set_antialias(1) + ctx.set_line_width(lw) + ctx.set_dash((dash, dash), 0) + ctx.set_source_rgb(0, 0, 0) + ctx.stroke_preserve() + + ctx.set_dash((dash, dash), dash) + ctx.set_source_rgb(1, 1, 1) + ctx.stroke() + + def on_draw_event(self, widget, ctx): + # to be overwritten by GTK4Agg or GTK4Cairo + pass + + def draw(self): + # docstring inherited + if self.is_drawable(): + self.queue_draw() + + def draw_idle(self): + # docstring inherited + if self._idle_draw_id != 0: + return + def idle_draw(*args): + try: + self.draw() + finally: + self._idle_draw_id = 0 + return False + self._idle_draw_id = GLib.idle_add(idle_draw) + + def flush_events(self): + # docstring inherited + context = GLib.MainContext.default() + while context.pending(): + context.iteration(True) + + +class NavigationToolbar2GTK4(_NavigationToolbar2GTK, Gtk.Box): + @_api.delete_parameter("3.6", "window") + def __init__(self, canvas, window=None): + self._win = window + Gtk.Box.__init__(self) + + self.add_css_class('toolbar') + + self._gtk_ids = {} + for text, tooltip_text, image_file, callback in self.toolitems: + if text is None: + self.append(Gtk.Separator()) + continue + image = Gtk.Image.new_from_gicon( + Gio.Icon.new_for_string( + str(cbook._get_data_path('images', + f'{image_file}-symbolic.svg')))) + self._gtk_ids[text] = button = ( + Gtk.ToggleButton() if callback in ['zoom', 'pan'] else + Gtk.Button()) + button.set_child(image) + button.add_css_class('flat') + button.add_css_class('image-button') + # Save the handler id, so that we can block it as needed. + button._signal_handler = button.connect( + 'clicked', getattr(self, callback)) + button.set_tooltip_text(tooltip_text) + self.append(button) + + # This filler item ensures the toolbar is always at least two text + # lines high. Otherwise the canvas gets redrawn as the mouse hovers + # over images because those use two-line messages which resize the + # toolbar. + label = Gtk.Label() + label.set_markup( + '\N{NO-BREAK SPACE}\n\N{NO-BREAK SPACE}') + label.set_hexpand(True) # Push real message to the right. + self.append(label) + + self.message = Gtk.Label() + self.message.set_justify(Gtk.Justification.RIGHT) + self.append(self.message) + + _NavigationToolbar2GTK.__init__(self, canvas) + + win = _api.deprecated("3.6")(property(lambda self: self._win)) + + def save_figure(self, *args): + dialog = Gtk.FileChooserNative( + title='Save the figure', + transient_for=self.canvas.get_root(), + action=Gtk.FileChooserAction.SAVE, + modal=True) + self._save_dialog = dialog # Must keep a reference. + + ff = Gtk.FileFilter() + ff.set_name('All files') + ff.add_pattern('*') + dialog.add_filter(ff) + dialog.set_filter(ff) + + formats = [] + default_format = None + for i, (name, fmts) in enumerate( + self.canvas.get_supported_filetypes_grouped().items()): + ff = Gtk.FileFilter() + ff.set_name(name) + for fmt in fmts: + ff.add_pattern(f'*.{fmt}') + dialog.add_filter(ff) + formats.append(name) + if self.canvas.get_default_filetype() in fmts: + default_format = i + # Setting the choice doesn't always work, so make sure the default + # format is first. + formats = [formats[default_format], *formats[:default_format], + *formats[default_format+1:]] + dialog.add_choice('format', 'File format', formats, formats) + dialog.set_choice('format', formats[default_format]) + + dialog.set_current_folder(Gio.File.new_for_path( + os.path.expanduser(mpl.rcParams['savefig.directory']))) + dialog.set_current_name(self.canvas.get_default_filename()) + + @functools.partial(dialog.connect, 'response') + def on_response(dialog, response): + file = dialog.get_file() + fmt = dialog.get_choice('format') + fmt = self.canvas.get_supported_filetypes_grouped()[fmt][0] + dialog.destroy() + self._save_dialog = None + if response != Gtk.ResponseType.ACCEPT: + return + # Save dir for next time, unless empty str (which means use cwd). + if mpl.rcParams['savefig.directory']: + parent = file.get_parent() + mpl.rcParams['savefig.directory'] = parent.get_path() + try: + self.canvas.figure.savefig(file.get_path(), format=fmt) + except Exception as e: + msg = Gtk.MessageDialog( + transient_for=self.canvas.get_root(), + message_type=Gtk.MessageType.ERROR, + buttons=Gtk.ButtonsType.OK, modal=True, + text=str(e)) + msg.show() + + dialog.show() + + +class ToolbarGTK4(ToolContainerBase, Gtk.Box): + _icon_extension = '-symbolic.svg' + + def __init__(self, toolmanager): + ToolContainerBase.__init__(self, toolmanager) + Gtk.Box.__init__(self) + self.set_property('orientation', Gtk.Orientation.HORIZONTAL) + + # Tool items are created later, but must appear before the message. + self._tool_box = Gtk.Box() + self.append(self._tool_box) + self._groups = {} + self._toolitems = {} + + # This filler item ensures the toolbar is always at least two text + # lines high. Otherwise the canvas gets redrawn as the mouse hovers + # over images because those use two-line messages which resize the + # toolbar. + label = Gtk.Label() + label.set_markup( + '\N{NO-BREAK SPACE}\n\N{NO-BREAK SPACE}') + label.set_hexpand(True) # Push real message to the right. + self.append(label) + + self._message = Gtk.Label() + self._message.set_justify(Gtk.Justification.RIGHT) + self.append(self._message) + + def add_toolitem(self, name, group, position, image_file, description, + toggle): + if toggle: + button = Gtk.ToggleButton() + else: + button = Gtk.Button() + button.set_label(name) + button.add_css_class('flat') + + if image_file is not None: + image = Gtk.Image.new_from_gicon( + Gio.Icon.new_for_string(image_file)) + button.set_child(image) + button.add_css_class('image-button') + + if position is None: + position = -1 + + self._add_button(button, group, position) + signal = button.connect('clicked', self._call_tool, name) + button.set_tooltip_text(description) + self._toolitems.setdefault(name, []) + self._toolitems[name].append((button, signal)) + + def _find_child_at_position(self, group, position): + children = [None] + child = self._groups[group].get_first_child() + while child is not None: + children.append(child) + child = child.get_next_sibling() + return children[position] + + def _add_button(self, button, group, position): + if group not in self._groups: + if self._groups: + self._add_separator() + group_box = Gtk.Box() + self._tool_box.append(group_box) + self._groups[group] = group_box + self._groups[group].insert_child_after( + button, self._find_child_at_position(group, position)) + + def _call_tool(self, btn, name): + self.trigger_tool(name) + + def toggle_toolitem(self, name, toggled): + if name not in self._toolitems: + return + for toolitem, signal in self._toolitems[name]: + toolitem.handler_block(signal) + toolitem.set_active(toggled) + toolitem.handler_unblock(signal) + + def remove_toolitem(self, name): + if name not in self._toolitems: + self.toolmanager.message_event(f'{name} not in toolbar', self) + return + + for group in self._groups: + for toolitem, _signal in self._toolitems[name]: + if toolitem in self._groups[group]: + self._groups[group].remove(toolitem) + del self._toolitems[name] + + def _add_separator(self): + sep = Gtk.Separator() + sep.set_property("orientation", Gtk.Orientation.VERTICAL) + self._tool_box.append(sep) + + def set_message(self, s): + self._message.set_label(s) + + +@backend_tools._register_tool_class(FigureCanvasGTK4) +class SaveFigureGTK4(backend_tools.SaveFigureBase): + def trigger(self, *args, **kwargs): + NavigationToolbar2GTK4.save_figure( + self._make_classic_style_pseudo_toolbar()) + + +@backend_tools._register_tool_class(FigureCanvasGTK4) +class HelpGTK4(backend_tools.ToolHelpBase): + def _normalize_shortcut(self, key): + """ + Convert Matplotlib key presses to GTK+ accelerator identifiers. + + Related to `FigureCanvasGTK4._get_key`. + """ + special = { + 'backspace': 'BackSpace', + 'pagedown': 'Page_Down', + 'pageup': 'Page_Up', + 'scroll_lock': 'Scroll_Lock', + } + + parts = key.split('+') + mods = ['<' + mod + '>' for mod in parts[:-1]] + key = parts[-1] + + if key in special: + key = special[key] + elif len(key) > 1: + key = key.capitalize() + elif key.isupper(): + mods += [''] + + return ''.join(mods) + key + + def _is_valid_shortcut(self, key): + """ + Check for a valid shortcut to be displayed. + + - GTK will never send 'cmd+' (see `FigureCanvasGTK4._get_key`). + - The shortcut window only shows keyboard shortcuts, not mouse buttons. + """ + return 'cmd+' not in key and not key.startswith('MouseButton.') + + def trigger(self, *args): + section = Gtk.ShortcutsSection() + + for name, tool in sorted(self.toolmanager.tools.items()): + if not tool.description: + continue + + # Putting everything in a separate group allows GTK to + # automatically split them into separate columns/pages, which is + # useful because we have lots of shortcuts, some with many keys + # that are very wide. + group = Gtk.ShortcutsGroup() + section.append(group) + # A hack to remove the title since we have no group naming. + child = group.get_first_child() + while child is not None: + child.set_visible(False) + child = child.get_next_sibling() + + shortcut = Gtk.ShortcutsShortcut( + accelerator=' '.join( + self._normalize_shortcut(key) + for key in self.toolmanager.get_tool_keymap(name) + if self._is_valid_shortcut(key)), + title=tool.name, + subtitle=tool.description) + group.append(shortcut) + + window = Gtk.ShortcutsWindow( + title='Help', + modal=True, + transient_for=self._figure.canvas.get_root()) + window.set_child(section) + + window.show() + + +@backend_tools._register_tool_class(FigureCanvasGTK4) +class ToolCopyToClipboardGTK4(backend_tools.ToolCopyToClipboardBase): + def trigger(self, *args, **kwargs): + with io.BytesIO() as f: + self.canvas.print_rgba(f) + w, h = self.canvas.get_width_height() + pb = GdkPixbuf.Pixbuf.new_from_data(f.getbuffer(), + GdkPixbuf.Colorspace.RGB, True, + 8, w, h, w*4) + clipboard = self.canvas.get_clipboard() + clipboard.set(pb) + + +backend_tools._register_tool_class( + FigureCanvasGTK4, _backend_gtk.ConfigureSubplotsGTK) +backend_tools._register_tool_class( + FigureCanvasGTK4, _backend_gtk.RubberbandGTK) +Toolbar = ToolbarGTK4 + + +class FigureManagerGTK4(_FigureManagerGTK): + _toolbar2_class = NavigationToolbar2GTK4 + _toolmanager_toolbar_class = ToolbarGTK4 + + +@_BackendGTK.export +class _BackendGTK4(_BackendGTK): + FigureCanvas = FigureCanvasGTK4 + FigureManager = FigureManagerGTK4 diff --git a/lib/matplotlib/backends/backend_gtk4agg.py b/lib/matplotlib/backends/backend_gtk4agg.py new file mode 100644 index 000000000000..d7ee04616e72 --- /dev/null +++ b/lib/matplotlib/backends/backend_gtk4agg.py @@ -0,0 +1,41 @@ +import numpy as np + +from .. import _api, cbook +from . import backend_agg, backend_gtk4 +from .backend_gtk4 import Gtk, _BackendGTK4 + +import cairo # Presence of cairo is already checked by _backend_gtk. + + +class FigureCanvasGTK4Agg(backend_agg.FigureCanvasAgg, + backend_gtk4.FigureCanvasGTK4): + + def on_draw_event(self, widget, ctx): + scale = self.device_pixel_ratio + allocation = self.get_allocation() + + Gtk.render_background( + self.get_style_context(), ctx, + allocation.x, allocation.y, + allocation.width, allocation.height) + + buf = cbook._unmultiplied_rgba8888_to_premultiplied_argb32( + np.asarray(self.get_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 + + +@_api.deprecated("3.6", alternative="backend_gtk4.FigureManagerGTK4") +class FigureManagerGTK4Agg(backend_gtk4.FigureManagerGTK4): + pass + + +@_BackendGTK4.export +class _BackendGTK4Agg(_BackendGTK4): + FigureCanvas = FigureCanvasGTK4Agg diff --git a/lib/matplotlib/backends/backend_gtk4cairo.py b/lib/matplotlib/backends/backend_gtk4cairo.py new file mode 100644 index 000000000000..83cbd081c26d --- /dev/null +++ b/lib/matplotlib/backends/backend_gtk4cairo.py @@ -0,0 +1,29 @@ +from contextlib import nullcontext + +from .backend_cairo import ( # noqa + FigureCanvasCairo, _RendererGTKCairo as RendererGTK4Cairo) +from .backend_gtk4 import Gtk, FigureCanvasGTK4, _BackendGTK4 + + +class FigureCanvasGTK4Cairo(FigureCanvasCairo, FigureCanvasGTK4): + _context_is_scaled = True + + def on_draw_event(self, widget, ctx): + with (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar + else nullcontext()): + self._renderer.set_context(ctx) + scale = self.device_pixel_ratio + # Scale physical drawing to logical size. + ctx.scale(1 / scale, 1 / scale) + allocation = self.get_allocation() + Gtk.render_background( + self.get_style_context(), ctx, + allocation.x, allocation.y, + allocation.width, allocation.height) + self._renderer.dpi = self.figure.dpi + self.figure.draw(self._renderer) + + +@_BackendGTK4.export +class _BackendGTK4Cairo(_BackendGTK4): + FigureCanvas = FigureCanvasGTK4Cairo diff --git a/lib/matplotlib/backends/backend_macosx.py b/lib/matplotlib/backends/backend_macosx.py index c84087f3b209..10dff3ef33f1 100644 --- a/lib/matplotlib/backends/backend_macosx.py +++ b/lib/matplotlib/backends/backend_macosx.py @@ -1,11 +1,13 @@ +import os + import matplotlib as mpl -from matplotlib import cbook +from matplotlib import _api, cbook from matplotlib._pylab_helpers import Gcf -from matplotlib.backends import _macosx -from matplotlib.backends.backend_agg import FigureCanvasAgg +from . import _macosx +from .backend_agg import FigureCanvasAgg from matplotlib.backend_bases import ( _Backend, FigureCanvasBase, FigureManagerBase, NavigationToolbar2, - TimerBase) + ResizeEvent, TimerBase) from matplotlib.figure import Figure from matplotlib.widgets import SubplotTool @@ -15,87 +17,98 @@ class TimerMac(_macosx.Timer, TimerBase): # completely implemented at the C-level (in _macosx.Timer) -class FigureCanvasMac(_macosx.FigureCanvas, FigureCanvasAgg): +class FigureCanvasMac(FigureCanvasAgg, _macosx.FigureCanvas, FigureCanvasBase): # docstring inherited - # Events such as button presses, mouse movements, and key presses - # are handled in the C code and the base class methods - # button_press_event, button_release_event, motion_notify_event, - # key_press_event, and key_release_event are called from there. + # Ideally this class would be `class FCMacAgg(FCAgg, FCMac)` + # (FC=FigureCanvas) where FCMac would be an ObjC-implemented mac-specific + # class also inheriting from FCBase (this is the approach with other GUI + # toolkits). However, writing an extension type inheriting from a Python + # base class is slightly tricky (the extension type must be a heap type), + # and we can just as well lift the FCBase base up one level, keeping it *at + # the end* to have the right method resolution order. + + # Events such as button presses, mouse movements, and key presses are + # handled in C and events (MouseEvent, etc.) are triggered from there. required_interactive_framework = "macosx" _timer_cls = TimerMac + manager_class = _api.classproperty(lambda cls: FigureManagerMac) def __init__(self, figure): - FigureCanvasBase.__init__(self, figure) - width, height = self.get_width_height() - _macosx.FigureCanvas.__init__(self, width, height) - self._dpi_ratio = 1.0 - - def _set_device_scale(self, value): - if self._dpi_ratio != value: - # Need the new value in place before setting figure.dpi, which - # will trigger a resize - self._dpi_ratio, old_value = value, self._dpi_ratio - self.figure.dpi = self.figure.dpi / old_value * self._dpi_ratio - - def _draw(self): - renderer = self.get_renderer(cleared=self.figure.stale) - if self.figure.stale: - self.figure.draw(renderer) - return renderer + super().__init__(figure=figure) + self._draw_pending = False + self._is_drawing = False def draw(self): - # docstring inherited - self.draw_idle() - self.flush_events() + """Render the figure and update the macosx canvas.""" + # The renderer draw is done here; delaying causes problems with code + # that uses the result of the draw() to update plot elements. + if self._is_drawing: + return + with cbook._setattr_cm(self, _is_drawing=True): + super().draw() + self.update() - # draw_idle is provided by _macosx.FigureCanvas + def draw_idle(self): + # docstring inherited + if not (getattr(self, '_draw_pending', False) or + getattr(self, '_is_drawing', False)): + self._draw_pending = True + # Add a singleshot timer to the eventloop that will call back + # into the Python method _draw_idle to take care of the draw + self._single_shot_timer(self._draw_idle) + + def _single_shot_timer(self, callback): + """Add a single shot timer with the given callback""" + # We need to explicitly stop (called from delete) the timer after + # firing, otherwise segfaults will occur when trying to deallocate + # the singleshot timers. + def callback_func(callback, timer): + callback() + del timer + timer = self.new_timer(interval=0) + timer.add_callback(callback_func, callback, timer) + timer.start() + + def _draw_idle(self): + """ + Draw method for singleshot timer + + This draw method can be added to a singleshot timer, which can + accumulate draws while the eventloop is spinning. This method will + then only draw the first time and short-circuit the others. + """ + with self._idle_draw_cntx(): + if not self._draw_pending: + # Short-circuit because our draw request has already been + # taken care of + return + self._draw_pending = False + self.draw() def blit(self, bbox=None): - self.draw_idle() + # docstring inherited + super().blit(bbox) + self.update() def resize(self, width, height): - dpi = self.figure.dpi - width /= dpi - height /= dpi - self.figure.set_size_inches(width * self._dpi_ratio, - height * self._dpi_ratio, - forward=False) - FigureCanvasBase.resize_event(self) + # Size from macOS is logical pixels, dpi is physical. + scale = self.figure.dpi / self.device_pixel_ratio + width /= scale + height /= scale + self.figure.set_size_inches(width, height, forward=False) + ResizeEvent("resize_event", self)._process() self.draw_idle() -class FigureManagerMac(_macosx.FigureManager, FigureManagerBase): - """ - Wrap everything up into a window for the pylab interface - """ - def __init__(self, canvas, num): - _macosx.FigureManager.__init__(self, canvas) - FigureManagerBase.__init__(self, canvas, num) - if mpl.rcParams['toolbar'] == 'toolbar2': - self.toolbar = NavigationToolbar2Mac(canvas) - else: - self.toolbar = None - if self.toolbar is not None: - self.toolbar.update() - - if mpl.is_interactive(): - self.show() - self.canvas.draw_idle() - - def close(self): - Gcf.destroy(self) - - class NavigationToolbar2Mac(_macosx.NavigationToolbar2, NavigationToolbar2): def __init__(self, canvas): - self.canvas = canvas # Needed by the _macosx __init__. data_path = cbook._get_data_path('images') _, tooltips, image_names, _ = zip(*NavigationToolbar2.toolitems) _macosx.NavigationToolbar2.__init__( - self, + self, canvas, tuple(str(data_path / image_name) + ".pdf" for image_name in image_names if image_name is not None), tuple(tooltip for tooltip in tooltips if tooltip is not None)) @@ -104,20 +117,22 @@ def __init__(self, canvas): def draw_rubberband(self, event, x0, y0, x1, y1): self.canvas.set_rubberband(int(x0), int(y0), int(x1), int(y1)) - def release_zoom(self, event): - super().release_zoom(event) + def remove_rubberband(self): self.canvas.remove_rubberband() - def set_cursor(self, cursor): - _macosx.set_cursor(cursor) - def save_figure(self, *args): + directory = os.path.expanduser(mpl.rcParams['savefig.directory']) filename = _macosx.choose_save_file('Save the figure', + directory, self.canvas.get_default_filename()) if filename is None: # Cancel return + # Save dir for next time, unless empty str (which means use cwd). + if mpl.rcParams['savefig.directory']: + mpl.rcParams['savefig.directory'] = os.path.dirname(filename) self.canvas.figure.savefig(filename) + @_api.deprecated("3.6", alternative='configure_subplots()') def prepare_configure_subplots(self): toolfig = Figure(figsize=(6, 3)) canvas = FigureCanvasMac(toolfig) @@ -126,8 +141,36 @@ def prepare_configure_subplots(self): _tool = SubplotTool(self.canvas.figure, toolfig) return canvas - def set_message(self, message): - _macosx.NavigationToolbar2.set_message(self, message.encode('utf-8')) + +class FigureManagerMac(_macosx.FigureManager, FigureManagerBase): + _toolbar2_class = NavigationToolbar2Mac + + def __init__(self, canvas, num): + self._shown = False + _macosx.FigureManager.__init__(self, canvas) + icon_path = str(cbook._get_data_path('images/matplotlib.pdf')) + _macosx.FigureManager.set_icon(icon_path) + FigureManagerBase.__init__(self, canvas, num) + if self.toolbar is not None: + self.toolbar.update() + if mpl.is_interactive(): + self.show() + self.canvas.draw_idle() + + def _close_button_pressed(self): + Gcf.destroy(self) + self.canvas.flush_events() + + @_api.deprecated("3.6") + def close(self): + return self._close_button_pressed() + + def show(self): + if not self._shown: + self._show() + self._shown = True + if mpl.rcParams["figure.raise_window"]: + self._raise() @_Backend.export diff --git a/lib/matplotlib/backends/backend_mixed.py b/lib/matplotlib/backends/backend_mixed.py index 54ca8f81ba02..5fadb96a0f73 100644 --- a/lib/matplotlib/backends/backend_mixed.py +++ b/lib/matplotlib/backends/backend_mixed.py @@ -1,8 +1,8 @@ import numpy as np from matplotlib import cbook -from matplotlib.backends.backend_agg import RendererAgg -from matplotlib.tight_bbox import process_figure_for_rasterizing +from .backend_agg import RendererAgg +from matplotlib._tight_bbox import process_figure_for_rasterizing class MixedModeRenderer: @@ -53,7 +53,7 @@ def __init__(self, figure, width, height, dpi, vector_renderer, # the figure dpi before and after the rasterization. Although # this looks ugly, I couldn't find a better solution. -JJL self.figure = figure - self._figdpi = figure.get_dpi() + self._figdpi = figure.dpi self._bbox_inches_restore = bbox_inches_restore @@ -74,7 +74,7 @@ def start_rasterizing(self): `stop_rasterizing` is called) will be drawn with the raster backend. """ # change the dpi of the figure temporarily. - self.figure.set_dpi(self.dpi) + self.figure.dpi = self.dpi if self._bbox_inches_restore: # when tight bbox is used r = process_figure_for_rasterizing(self.figure, self._bbox_inches_restore) @@ -110,7 +110,7 @@ def stop_rasterizing(self): self._raster_renderer = None # restore the figure dpi. - self.figure.set_dpi(self._figdpi) + self.figure.dpi = self._figdpi if self._bbox_inches_restore: # when tight bbox is used r = process_figure_for_rasterizing(self.figure, diff --git a/lib/matplotlib/backends/backend_nbagg.py b/lib/matplotlib/backends/backend_nbagg.py index 0fb8b03ea266..712f45735940 100644 --- a/lib/matplotlib/backends/backend_nbagg.py +++ b/lib/matplotlib/backends/backend_nbagg.py @@ -1,4 +1,4 @@ -"""Interactive figures in the IPython notebook""" +"""Interactive figures in the IPython notebook.""" # Note: There is a notebook in # lib/matplotlib/backends/web_backend/nbagg_uat.ipynb to help verify # that changes made maintain expected behaviour. @@ -9,20 +9,16 @@ import pathlib import uuid +from ipykernel.comm import Comm from IPython.display import display, Javascript, HTML -try: - # Jupyter/IPython 4.x or later - from ipykernel.comm import Comm -except ImportError: - # Jupyter/IPython 3.x or earlier - from IPython.kernel.comm import Comm from matplotlib import is_interactive from matplotlib._pylab_helpers import Gcf from matplotlib.backend_bases import _Backend, NavigationToolbar2 -from matplotlib.backends.backend_webagg_core import ( - FigureCanvasWebAggCore, FigureManagerWebAgg, NavigationToolbar2WebAgg, - TimerTornado) +from .backend_webagg_core import ( + FigureCanvasWebAggCore, FigureManagerWebAgg, NavigationToolbar2WebAgg) +from .backend_webagg_core import ( # noqa: F401 # pylint: disable=W0611 + TimerTornado, TimerAsyncio) def connection_info(): @@ -43,18 +39,13 @@ def connection_info(): return '\n'.join(result) -# Note: Version 3.2 and 4.x icons -# http://fontawesome.io/3.2.1/icons/ -# http://fontawesome.io/ -# the `fa fa-xxx` part targets font-awesome 4, (IPython 3.x) -# the icon-xxx targets font awesome 3.21 (IPython 2.x) -_FONT_AWESOME_CLASSES = { - 'home': 'fa fa-home icon-home', - 'back': 'fa fa-arrow-left icon-arrow-left', - 'forward': 'fa fa-arrow-right icon-arrow-right', - 'zoom_to_rect': 'fa fa-square-o icon-check-empty', - 'move': 'fa fa-arrows icon-move', - 'download': 'fa fa-floppy-o icon-save', +_FONT_AWESOME_CLASSES = { # font-awesome 4 names + 'home': 'fa fa-home', + 'back': 'fa fa-arrow-left', + 'forward': 'fa fa-arrow-right', + 'zoom_to_rect': 'fa fa-square-o', + 'move': 'fa fa-arrows', + 'download': 'fa fa-floppy-o', None: None } @@ -71,12 +62,27 @@ class NavigationIPy(NavigationToolbar2WebAgg): class FigureManagerNbAgg(FigureManagerWebAgg): - ToolbarCls = NavigationIPy + _toolbar2_class = ToolbarCls = NavigationIPy def __init__(self, canvas, num): self._shown = False super().__init__(canvas, num) + @classmethod + def create_with_canvas(cls, canvas_class, figure, num): + canvas = canvas_class(figure) + manager = cls(canvas, num) + if is_interactive(): + manager.show() + canvas.draw_idle() + + def destroy(event): + canvas.mpl_disconnect(cid) + Gcf.destroy(manager) + + cid = canvas.mpl_connect('close_event', destroy) + return manager + def display_js(self): # XXX How to do this just once? It has to deal with multiple # browser instances using the same kernel (require.js - but the @@ -90,6 +96,15 @@ def show(self): else: self.canvas.draw_idle() self._shown = True + # plt.figure adds an event which makes the figure in focus the active + # one. Disable this behaviour, as it results in figures being put as + # the active figure after they have been shown, even in non-interactive + # mode. + if hasattr(self, '_cidgcf'): + self.canvas.mpl_disconnect(self._cidgcf) + if not is_interactive(): + from matplotlib._pylab_helpers import Gcf + Gcf.figs.pop(self.num, None) def reshow(self): """ @@ -142,7 +157,7 @@ def remove_comm(self, comm_id): class FigureCanvasNbAgg(FigureCanvasWebAggCore): - pass + manager_class = FigureManagerNbAgg class CommSocket: @@ -226,42 +241,3 @@ def on_message(self, message): class _BackendNbAgg(_Backend): FigureCanvas = FigureCanvasNbAgg FigureManager = FigureManagerNbAgg - - @staticmethod - def new_figure_manager_given_figure(num, figure): - canvas = FigureCanvasNbAgg(figure) - manager = FigureManagerNbAgg(canvas, num) - if is_interactive(): - manager.show() - figure.canvas.draw_idle() - - def destroy(event): - canvas.mpl_disconnect(cid) - Gcf.destroy(manager) - - cid = canvas.mpl_connect('close_event', destroy) - return manager - - @staticmethod - def show(block=None): - ## TODO: something to do when keyword block==False ? - from matplotlib._pylab_helpers import Gcf - - managers = Gcf.get_all_fig_managers() - if not managers: - return - - interactive = is_interactive() - - for manager in managers: - manager.show() - - # plt.figure adds an event which makes the figure in focus the - # active one. Disable this behaviour, as it results in - # figures being put as the active figure after they have been - # shown, even in non-interactive mode. - if hasattr(manager, '_cidgcf'): - manager.canvas.mpl_disconnect(manager._cidgcf) - - if not interactive: - Gcf.figs.pop(manager.num, None) diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index 26a12764da38..a3ea5dac6e43 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -1,10 +1,10 @@ """ -A PDF matplotlib backend -Author: Jouni K Seppänen +A PDF Matplotlib backend. + +Author: Jouni K Seppänen and others. """ import codecs -import collections from datetime import datetime from enum import Enum from functools import total_ordering @@ -13,8 +13,9 @@ import logging import math import os -import re +import string import struct +import sys import time import types import warnings @@ -24,20 +25,17 @@ from PIL import Image import matplotlib as mpl -from matplotlib import _api, _text_layout, cbook +from matplotlib import _api, _text_helpers, _type1font, cbook, dviread from matplotlib._pylab_helpers import Gcf from matplotlib.backend_bases import ( - _Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase, - GraphicsContextBase, RendererBase, _no_output_draw) + _Backend, FigureCanvasBase, FigureManagerBase, GraphicsContextBase, + RendererBase) from matplotlib.backends.backend_mixed import MixedModeRenderer from matplotlib.figure import Figure -from matplotlib.font_manager import findfont, get_font -from matplotlib.afm import AFM -import matplotlib.type1font as type1font -import matplotlib.dviread as dviread +from matplotlib.font_manager import get_font, fontManager as _fontManager +from matplotlib._afm import AFM from matplotlib.ft2font import (FIXED_WIDTH, ITALIC, LOAD_NO_SCALE, - LOAD_NO_HINTING, KERNING_UNFITTED) -from matplotlib.mathtext import MathTextParser + LOAD_NO_HINTING, KERNING_UNFITTED, FT2Font) from matplotlib.transforms import Affine2D, BboxBase from matplotlib.path import Path from matplotlib.dates import UTC @@ -88,13 +86,18 @@ # TODOs: # -# * encoding of fonts, including mathtext fonts and unicode support +# * encoding of fonts, including mathtext fonts and Unicode support # * TTF support has lots of small TODOs, e.g., how do you know if a font # is serif/sans-serif, or symbolic/non-symbolic? # * draw_quad_mesh +@_api.deprecated("3.6", alternative="a vendored copy of _fill") def fill(strings, linelen=75): + return _fill(strings, linelen=linelen) + + +def _fill(strings, linelen=75): """ Make one string from sequence of strings, with whitespace in between. @@ -115,25 +118,6 @@ def fill(strings, linelen=75): result.append(b' '.join(strings[lasti:])) return b'\n'.join(result) -# PDF strings are supposed to be able to include any eight-bit data, -# except that unbalanced parens and backslashes must be escaped by a -# backslash. However, sf bug #2708559 shows that the carriage return -# character may get read as a newline; these characters correspond to -# \gamma and \Omega in TeX's math font encoding. Escaping them fixes -# the bug. -_string_escape_regex = re.compile(br'([\\()\r\n])') - - -def _string_escape(match): - m = match.group(0) - if m in br'\()': - return b'\\' + m - elif m == b'\n': - return br'\n' - elif m == b'\r': - return br'\r' - assert False - def _create_pdf_info_dict(backend, metadata): """ @@ -246,6 +230,77 @@ def _datetime_to_pdf(d): return r +def _calculate_quad_point_coordinates(x, y, width, height, angle=0): + """ + Calculate the coordinates of rectangle when rotated by angle around x, y + """ + + angle = math.radians(-angle) + sin_angle = math.sin(angle) + cos_angle = math.cos(angle) + a = x + height * sin_angle + b = y + height * cos_angle + c = x + width * cos_angle + height * sin_angle + d = y - width * sin_angle + height * cos_angle + e = x + width * cos_angle + f = y - width * sin_angle + return ((x, y), (e, f), (c, d), (a, b)) + + +def _get_coordinates_of_block(x, y, width, height, angle=0): + """ + Get the coordinates of rotated rectangle and rectangle that covers the + rotated rectangle. + """ + + vertices = _calculate_quad_point_coordinates(x, y, width, + height, angle) + + # Find min and max values for rectangle + # adjust so that QuadPoints is inside Rect + # PDF docs says that QuadPoints should be ignored if any point lies + # outside Rect, but for Acrobat it is enough that QuadPoints is on the + # border of Rect. + + pad = 0.00001 if angle % 90 else 0 + min_x = min(v[0] for v in vertices) - pad + min_y = min(v[1] for v in vertices) - pad + max_x = max(v[0] for v in vertices) + pad + max_y = max(v[1] for v in vertices) + pad + return (tuple(itertools.chain.from_iterable(vertices)), + (min_x, min_y, max_x, max_y)) + + +def _get_link_annotation(gc, x, y, width, height, angle=0): + """ + Create a link annotation object for embedding URLs. + """ + quadpoints, rect = _get_coordinates_of_block(x, y, width, height, angle) + link_annotation = { + 'Type': Name('Annot'), + 'Subtype': Name('Link'), + 'Rect': rect, + 'Border': [0, 0, 0], + 'A': { + 'S': Name('URI'), + 'URI': gc.get_url(), + }, + } + if angle % 90: + # Add QuadPoints + link_annotation['QuadPoints'] = quadpoints + return link_annotation + + +# PDF strings are supposed to be able to include any eight-bit data, except +# that unbalanced parens and backslashes must be escaped by a backslash. +# However, sf bug #2708559 shows that the carriage return character may get +# read as a newline; these characters correspond to \gamma and \Omega in TeX's +# math font encoding. Escaping them fixes the bug. +_str_escapes = str.maketrans({ + '\\': '\\\\', '(': '\\(', ')': '\\)', '\n': '\\n', '\r': '\\r'}) + + def pdfRepr(obj): """Map Python objects to PDF syntax.""" @@ -271,38 +326,36 @@ def pdfRepr(obj): elif isinstance(obj, (int, np.integer)): return b"%d" % obj - # Unicode strings are encoded in UTF-16BE with byte-order mark. + # Non-ASCII Unicode strings are encoded in UTF-16BE with byte-order mark. elif isinstance(obj, str): - try: - # But maybe it's really ASCII? - s = obj.encode('ASCII') - return pdfRepr(s) - except UnicodeEncodeError: - s = codecs.BOM_UTF16_BE + obj.encode('UTF-16BE') - return pdfRepr(s) + return pdfRepr(obj.encode('ascii') if obj.isascii() + else codecs.BOM_UTF16_BE + obj.encode('UTF-16BE')) # Strings are written in parentheses, with backslashes and parens # escaped. Actually balanced parens are allowed, but it is # simpler to escape them all. TODO: cut long strings into lines; # I believe there is some maximum line length in PDF. + # Despite the extra decode/encode, translate is faster than regex. elif isinstance(obj, bytes): - return b'(' + _string_escape_regex.sub(_string_escape, obj) + b')' + return ( + b'(' + + obj.decode('latin-1').translate(_str_escapes).encode('latin-1') + + b')') # Dictionaries. The keys must be PDF names, so if we find strings # there, we make Name objects from them. The values may be # anything, so the caller must ensure that PDF names are # represented as Name objects. elif isinstance(obj, dict): - return fill([ + return _fill([ b"<<", - *[Name(key).pdfRepr() + b" " + pdfRepr(obj[key]) - for key in sorted(obj)], + *[Name(k).pdfRepr() + b" " + pdfRepr(v) for k, v in obj.items()], b">>", ]) # Lists. elif isinstance(obj, (list, tuple)): - return fill([b"[", *[pdfRepr(val) for val in obj], b"]"]) + return _fill([b"[", *[pdfRepr(val) for val in obj], b"]"]) # The null keyword. elif obj is None: @@ -314,13 +367,28 @@ def pdfRepr(obj): # A bounding box elif isinstance(obj, BboxBase): - return fill([pdfRepr(val) for val in obj.bounds]) + return _fill([pdfRepr(val) for val in obj.bounds]) else: raise TypeError("Don't know a PDF representation for {} objects" .format(type(obj))) +def _font_supports_glyph(fonttype, glyph): + """ + Returns True if the font is able to provide codepoint *glyph* in a PDF. + + For a Type 3 font, this method returns True only for single-byte + characters. For Type 42 fonts this method return True if the character is + from the Basic Multilingual Plane. + """ + if fonttype == 3: + return glyph <= 255 + if fonttype == 42: + return glyph <= 65535 + raise NotImplementedError() + + class Reference: """ PDF reference object. @@ -348,7 +416,8 @@ def write(self, contents, file): class Name: """PDF name object.""" __slots__ = ('name',) - _regex = re.compile(r'[^!-~]') + _hexify = {c: '#%02x' % c + for c in {*range(256)} - {*range(ord('!'), ord('~') + 1)}} def __init__(self, name): if isinstance(name, Name): @@ -356,13 +425,13 @@ def __init__(self, name): else: if isinstance(name, bytes): name = name.decode('ascii') - self.name = self._regex.sub(Name.hexify, name).encode('ascii') + self.name = name.translate(self._hexify).encode('ascii') def __repr__(self): return "" % self.name def __str__(self): - return '/' + str(self.name) + return '/' + self.name.decode('ascii') def __eq__(self, other): return isinstance(other, Name) and self.name == other.name @@ -374,6 +443,7 @@ def __hash__(self): return hash(self.name) @staticmethod + @_api.deprecated("3.6") def hexify(match): return '#%02x' % ord(match.group()) @@ -381,8 +451,8 @@ def pdfRepr(self): return b'/' + self.name +@_api.deprecated("3.6") class Operator: - """PDF operator object.""" __slots__ = ('op',) def __init__(self, op): @@ -404,46 +474,52 @@ def pdfRepr(self): return self._x -# PDF operators (not an exhaustive list) -class Op(Operator, Enum): +class Op(Enum): + """PDF operators (not an exhaustive list).""" + close_fill_stroke = b'b' fill_stroke = b'B' fill = b'f' - closepath = b'h', + closepath = b'h' close_stroke = b's' stroke = b'S' endpath = b'n' - begin_text = b'BT', + begin_text = b'BT' end_text = b'ET' curveto = b'c' rectangle = b're' lineto = b'l' - moveto = b'm', + moveto = b'm' concat_matrix = b'cm' use_xobject = b'Do' - setgray_stroke = b'G', + setgray_stroke = b'G' setgray_nonstroke = b'g' setrgb_stroke = b'RG' - setrgb_nonstroke = b'rg', + setrgb_nonstroke = b'rg' setcolorspace_stroke = b'CS' - setcolorspace_nonstroke = b'cs', + setcolorspace_nonstroke = b'cs' setcolor_stroke = b'SCN' setcolor_nonstroke = b'scn' - setdash = b'd', + setdash = b'd' setlinejoin = b'j' setlinecap = b'J' setgstate = b'gs' - gsave = b'q', + gsave = b'q' grestore = b'Q' textpos = b'Td' selectfont = b'Tf' - textmatrix = b'Tm', + textmatrix = b'Tm' show = b'Tj' showkern = b'TJ' setlinewidth = b'w' clip = b'W' shading = b'sh' + op = _api.deprecated('3.6')(property(lambda self: self.value)) + + def pdfRepr(self): + return self.value + @classmethod def paint_path(cls, fill, stroke): """ @@ -681,15 +757,14 @@ def __init__(self, filename, metadata=None): self._soft_mask_states = {} self._soft_mask_seq = (Name(f'SM{i}') for i in itertools.count(1)) self._soft_mask_groups = [] - # reproducible writeHatches needs an ordered dict: - self.hatchPatterns = collections.OrderedDict() + self.hatchPatterns = {} self._hatch_pattern_seq = (Name(f'H{i}') for i in itertools.count(1)) self.gouraudTriangles = [] - self._images = collections.OrderedDict() # reproducible writeImages + self._images = {} self._image_seq = (Name(f'I{i}') for i in itertools.count(1)) - self.markers = collections.OrderedDict() # reproducible writeMarkers + self.markers = {} self.multi_byte_charprocs = {} self.paths = [] @@ -716,11 +791,6 @@ def __init__(self, filename, metadata=None): 'ProcSet': procsets} self.writeObject(self.resourceObject, resources) - @_api.deprecated("3.3") - @property - def used_characters(self): - return self.file._character_tracker.used_characters - def newPage(self, width, height): self.endStream() @@ -732,9 +802,6 @@ def newPage(self, width, height): 'Resources': self.resourceObject, 'MediaBox': [0, 0, 72 * width, 72 * height], 'Contents': contentObject, - 'Group': {'Type': Name('Group'), - 'S': Name('Transparency'), - 'CS': Name('DeviceRGB')}, 'Annots': annotsObject, } pageObject = self.reserveObject('page') @@ -744,8 +811,10 @@ def newPage(self, width, height): self.beginStream(contentObject.id, self.reserveObject('length of content stream')) - # Initialize the pdf graphics state to match the default mpl - # graphics context: currently only the join style needs to be set + # Initialize the pdf graphics state to match the default Matplotlib + # graphics context (colorspace and joinstyle). + self.output(Name('DeviceRGB'), Op.setcolorspace_stroke) + self.output(Name('DeviceRGB'), Op.setcolorspace_nonstroke) self.output(GraphicsContextPdf.joinstyles['round'], Op.setlinejoin) # Clear the list of annotations for the next page @@ -760,6 +829,22 @@ def newTextnote(self, text, positionRect=[-100, -100, 0, 0]): } self.pageAnnotations.append(theNote) + def _get_subsetted_psname(self, ps_name, charmap): + def toStr(n, base): + if n < base: + return string.ascii_uppercase[n] + else: + return ( + toStr(n // base, base) + string.ascii_uppercase[n % base] + ) + + # encode to string using base 26 + hashed = hash(frozenset(charmap.keys())) % ((sys.maxsize + 1) * 2) + prefix = toStr(hashed, 26) + + # get first 6 characters from prefix + return prefix[:6] + "+" + ps_name + def finalize(self): """Write out the various deferred objects and the pdf end matter.""" @@ -811,7 +896,7 @@ def write(self, data): self.currentstream.write(data) def output(self, *data): - self.write(fill([pdfRepr(x) for x in data])) + self.write(_fill([pdfRepr(x) for x in data])) self.write(b'\n') def beginStream(self, id, len, extra=None, png=None): @@ -823,6 +908,11 @@ def endStream(self): self.currentstream.end() self.currentstream = None + def outputStream(self, ref, data, *, extra=None): + self.beginStream(ref.id, None, extra) + self.currentstream.write(data) + self.endStream() + def _write_annotations(self): for annotsObject, annotations in self._annotations: self.writeObject(annotsObject, annotations) @@ -835,20 +925,28 @@ def fontName(self, fontprop): """ if isinstance(fontprop, str): - filename = fontprop + filenames = [fontprop] elif mpl.rcParams['pdf.use14corefonts']: - filename = findfont( - fontprop, fontext='afm', directory=RendererPdf._afm_font_dir) + filenames = _fontManager._find_fonts_by_props( + fontprop, fontext='afm', directory=RendererPdf._afm_font_dir + ) else: - filename = findfont(fontprop) - - Fx = self.fontNames.get(filename) - if Fx is None: - Fx = next(self._internal_font_seq) - self.fontNames[filename] = Fx - _log.debug('Assigning font %s = %r', Fx, filename) - - return Fx + filenames = _fontManager._find_fonts_by_props(fontprop) + first_Fx = None + for fname in filenames: + Fx = self.fontNames.get(fname) + if not first_Fx: + first_Fx = Fx + if Fx is None: + Fx = next(self._internal_font_seq) + self.fontNames[fname] = Fx + _log.debug('Assigning font %s = %r', Fx, fname) + if not first_Fx: + first_Fx = Fx + + # find_fontsprop's first value always adheres to + # findfont's value, so technically no behaviour change + return first_Fx def dviFontName(self, dvifont): """ @@ -861,7 +959,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( @@ -952,7 +1050,7 @@ def _embedTeXFont(self, fontinfo): return fontdictObject # We have a font file to embed - read it in and apply any effects - t1font = type1font.Type1Font(fontinfo.fontfile) + t1font = _type1font.Type1Font(fontinfo.fontfile) if fontinfo.effects: t1font = t1font.transform(fontinfo.effects) fontdict['BaseFont'] = Name(t1font.prop['FontName']) @@ -1027,22 +1125,19 @@ def createType1Descriptor(self, t1font, fontfile): self.writeObject(fontdescObject, descriptor) - self.beginStream(fontfileObject.id, None, - {'Length1': len(t1font.parts[0]), - 'Length2': len(t1font.parts[1]), - 'Length3': 0}) - self.currentstream.write(t1font.parts[0]) - self.currentstream.write(t1font.parts[1]) - self.endStream() + self.outputStream(fontfileObject, b"".join(t1font.parts[:2]), + extra={'Length1': len(t1font.parts[0]), + 'Length2': len(t1font.parts[1]), + 'Length3': 0}) return fontdescObject - def _get_xobject_symbol_name(self, filename, symbol_name): + def _get_xobject_glyph_name(self, filename, glyph_name): Fx = self.fontName(filename) return "-".join([ Fx.name.decode(), os.path.splitext(os.path.basename(filename))[0], - symbol_name]) + glyph_name]) _identityToUnicodeCMap = b"""/CIDInit /ProcSet findresource begin 12 dict begin @@ -1117,7 +1212,6 @@ def get_char_width(charcode): width = font.load_char( s, flags=LOAD_NO_SCALE | LOAD_NO_HINTING).horiAdvance return cvt(width) - with warnings.catch_warnings(): # Ignore 'Required glyph missing from current font' warning # from ft2font: here we're just building the widths table, but @@ -1156,13 +1250,13 @@ def get_char_width(charcode): charprocs = {} for charname in sorted(rawcharprocs): stream = rawcharprocs[charname] - charprocDict = {'Length': len(stream)} + charprocDict = {} # The 2-byte characters are used as XObjects, so they # need extra info in their dictionary if charname in multi_byte_chars: - charprocDict['Type'] = Name('XObject') - charprocDict['Subtype'] = Name('Form') - charprocDict['BBox'] = bbox + charprocDict = {'Type': Name('XObject'), + 'Subtype': Name('Form'), + 'BBox': bbox} # Each glyph includes bounding box information, # but xpdf and ghostscript can't handle it in a # Form XObject (they segfault!!!), so we remove it @@ -1171,14 +1265,12 @@ def get_char_width(charcode): # value. stream = stream[stream.find(b"d1") + 2:] charprocObject = self.reserveObject('charProc') - self.beginStream(charprocObject.id, None, charprocDict) - self.currentstream.write(stream) - self.endStream() + self.outputStream(charprocObject, stream, extra=charprocDict) # Send the glyphs with ccode > 255 to the XObject dictionary, # and the others to the font itself if charname in multi_byte_chars: - name = self._get_xobject_symbol_name(filename, charname) + name = self._get_xobject_glyph_name(filename, charname) self.multi_byte_charprocs[name] = charprocObject else: charprocs[charname] = charprocObject @@ -1201,6 +1293,22 @@ def embedTTFType42(font, characters, descriptor): wObject = self.reserveObject('Type 0 widths') toUnicodeMapObject = self.reserveObject('ToUnicode map') + subset_str = "".join(chr(c) for c in characters) + _log.debug("SUBSET %s characters: %s", filename, subset_str) + fontdata = _backend_pdf_ps.get_glyphs_subset(filename, subset_str) + _log.debug( + "SUBSET %s %d -> %d", filename, + os.stat(filename).st_size, fontdata.getbuffer().nbytes + ) + + # We need this ref for XObjects + full_font = font + + # reload the font object from the subset + # (all the necessary data could probably be obtained directly + # using fontLib.ttLib) + font = FT2Font(fontdata) + cidFontDict = { 'Type': Name('Font'), 'Subtype': Name('CIDFontType2'), @@ -1225,21 +1333,9 @@ def embedTTFType42(font, characters, descriptor): # Make fontfile stream descriptor['FontFile2'] = fontfileObject - length1Object = self.reserveObject('decoded length of a font') - self.beginStream( - fontfileObject.id, - self.reserveObject('length of font stream'), - {'Length1': length1Object}) - with open(filename, 'rb') as fontfile: - length1 = 0 - while True: - data = fontfile.read(4096) - if not data: - break - length1 += len(data) - self.currentstream.write(data) - self.endStream() - self.writeObject(length1Object, length1) + self.outputStream( + fontfileObject, fontdata.getvalue(), + extra={'Length1': fontdata.getbuffer().nbytes}) # Make the 'W' (Widths) array, CidToGidMap and ToUnicode CMap # at the same time @@ -1275,6 +1371,11 @@ def embedTTFType42(font, characters, descriptor): unicode_bfrange = [] for start, end in unicode_groups: + # Ensure the CID map contains only chars from BMP + if start > 65535: + continue + end = min(65535, end) + unicode_bfrange.append( b"<%04x> <%04x> [%s]" % (start, end, @@ -1282,20 +1383,39 @@ def embedTTFType42(font, characters, descriptor): unicode_cmap = (self._identityToUnicodeCMap % (len(unicode_groups), b"\n".join(unicode_bfrange))) + # Add XObjects for unsupported chars + glyph_ids = [] + for ccode in characters: + if not _font_supports_glyph(fonttype, ccode): + gind = full_font.get_char_index(ccode) + glyph_ids.append(gind) + + bbox = [cvt(x, nearest=False) for x in full_font.bbox] + rawcharprocs = _get_pdf_charprocs(filename, glyph_ids) + for charname in sorted(rawcharprocs): + stream = rawcharprocs[charname] + charprocDict = {'Type': Name('XObject'), + 'Subtype': Name('Form'), + 'BBox': bbox} + # Each glyph includes bounding box information, + # but xpdf and ghostscript can't handle it in a + # Form XObject (they segfault!!!), so we remove it + # from the stream here. It's not needed anyway, + # since the Form XObject includes it in its BBox + # value. + stream = stream[stream.find(b"d1") + 2:] + charprocObject = self.reserveObject('charProc') + self.outputStream(charprocObject, stream, extra=charprocDict) + + name = self._get_xobject_glyph_name(filename, charname) + self.multi_byte_charprocs[name] = charprocObject + # CIDToGIDMap stream cid_to_gid_map = "".join(cid_to_gid_map).encode("utf-16be") - self.beginStream(cidToGidMapObject.id, - None, - {'Length': len(cid_to_gid_map)}) - self.currentstream.write(cid_to_gid_map) - self.endStream() + self.outputStream(cidToGidMapObject, cid_to_gid_map) # ToUnicode CMap - self.beginStream(toUnicodeMapObject.id, - None, - {'Length': unicode_cmap}) - self.currentstream.write(unicode_cmap) - self.endStream() + self.outputStream(toUnicodeMapObject, unicode_cmap) descriptor['MaxWidth'] = max_width @@ -1309,7 +1429,11 @@ def embedTTFType42(font, characters, descriptor): # Beginning of main embedTTF function... - ps_name = font.postscript_name.encode('ascii', 'replace') + ps_name = self._get_subsetted_psname( + font.postscript_name, + font.get_charmap() + ) + ps_name = ps_name.encode('ascii', 'replace') ps_name = Name(ps_name) pclt = font.get_sfnt_table('pclt') or {'capHeight': 0, 'xHeight': 0} post = font.get_sfnt_table('post') or {'italicAngle': (0, 0)} @@ -1469,7 +1593,7 @@ def writeHatches(self): # Change origin to match Agg at top-left. 'Matrix': [1, 0, 0, 1, 0, self.height * 72]}) - stroke_rgb, fill_rgb, path = hatch_style + stroke_rgb, fill_rgb, hatch = hatch_style self.output(stroke_rgb[0], stroke_rgb[1], stroke_rgb[2], Op.setrgb_stroke) if fill_rgb is not None: @@ -1481,7 +1605,7 @@ def writeHatches(self): self.output(mpl.rcParams['hatch.linewidth'], Op.setlinewidth) self.output(*self.pathOperations( - Path.hatch(path), + Path.hatch(hatch), Affine2D().scale(sidelen), simplify=False)) self.output(Op.fill_stroke) @@ -1646,16 +1770,20 @@ def _writeImg(self, data, id, smask=None): # Convert to indexed color if there are 256 colors or fewer # This can significantly reduce the file size num_colors = len(img_colors) - img = img.convert(mode='P', dither=Image.NONE, - palette=Image.ADAPTIVE, colors=num_colors) + # These constants were converted to IntEnums and deprecated in + # Pillow 9.2 + dither = getattr(Image, 'Dither', Image).NONE + pmode = getattr(Image, 'Palette', Image).ADAPTIVE + img = img.convert( + mode='P', dither=dither, palette=pmode, colors=num_colors + ) png_data, bit_depth, palette = self._writePng(img) if bit_depth is None or palette is None: raise RuntimeError("invalid PNG header") palette = palette[:num_colors * 3] # Trim padding - palette = pdfRepr(palette) - obj['ColorSpace'] = Verbatim(b'[/Indexed /DeviceRGB ' - + str(num_colors - 1).encode() - + b' ' + palette + b']') + obj['ColorSpace'] = Verbatim( + b'[/Indexed /DeviceRGB %d %s]' + % (num_colors - 1, pdfRepr(palette))) obj['BitsPerComponent'] = bit_depth color_channels = 1 else: @@ -1770,7 +1898,8 @@ def pathOperations(path, transform, clip=None, simplify=None, sketch=None): return [Verbatim(_path.convert_to_string( path, transform, clip, simplify, sketch, 6, - [Op.moveto.op, Op.lineto.op, b'', Op.curveto.op, Op.closepath.op], + [Op.moveto.value, Op.lineto.value, b'', Op.curveto.value, + Op.closepath.value], True))] def writePath(self, path, transform, clip=False, sketch=None): @@ -1844,11 +1973,6 @@ def __init__(self, file, image_dpi, height, width): self.gc = self.new_gc() self.image_dpi = image_dpi - @_api.deprecated("3.4") - @property - def mathtext_parser(self): - return MathTextParser("Pdf") - def finalize(self): self.file.output(*self.gc.finalize()) @@ -1879,15 +2003,6 @@ def check_gc(self, gc, fillcolor=None): gc._fillcolor = orig_fill gc._effective_alphas = orig_alphas - @_api.deprecated("3.3") - def track_characters(self, *args, **kwargs): - """Keep track of which characters are required from each font.""" - self.file._character_tracker.track(*args, **kwargs) - - @_api.deprecated("3.3") - def merge_used_characters(self, *args, **kwargs): - self.file._character_tracker.merge(*args, **kwargs) - def get_image_magnification(self): return self.image_dpi/72.0 @@ -1931,7 +2046,7 @@ def draw_path(self, gc, path, transform, rgbFace=None): self.file.output(self.gc.paint()) def draw_path_collection(self, gc, master_transform, paths, all_transforms, - offsets, offsetTrans, facecolors, edgecolors, + offsets, offset_trans, facecolors, edgecolors, linewidths, linestyles, antialiaseds, urls, offset_position): # We can only reuse the objects if the presence of fill and @@ -1973,7 +2088,7 @@ def draw_path_collection(self, gc, master_transform, paths, all_transforms, if (not can_do_optimization) or (not should_do_optimization): return RendererBase.draw_path_collection( self, gc, master_transform, paths, all_transforms, - offsets, offsetTrans, facecolors, edgecolors, + offsets, offset_trans, facecolors, edgecolors, linewidths, linestyles, antialiaseds, urls, offset_position) @@ -1989,8 +2104,8 @@ def draw_path_collection(self, gc, master_transform, paths, all_transforms, output(*self.gc.push()) lastx, lasty = 0, 0 for xo, yo, path_id, gc0, rgbFace in self._iter_collection( - gc, master_transform, all_transforms, path_codes, offsets, - offsetTrans, facecolors, edgecolors, linewidths, linestyles, + gc, path_codes, offsets, offset_trans, + facecolors, edgecolors, linewidths, linestyles, antialiaseds, urls, offset_position): self.check_gc(gc0, rgbFace) @@ -2098,17 +2213,8 @@ def draw_mathtext(self, gc, x, y, s, prop, angle): self._text2path.mathtext_parser.parse(s, 72, prop) if gc.get_url() is not None: - link_annotation = { - 'Type': Name('Annot'), - 'Subtype': Name('Link'), - 'Rect': (x, y, x + width, y + height), - 'Border': [0, 0, 0], - 'A': { - 'S': Name('URI'), - 'URI': gc.get_url(), - }, - } - self.file._annotations[-1][1].append(link_annotation) + self.file._annotations[-1][1].append(_get_link_annotation( + gc, x, y, width, height, angle)) fonttype = mpl.rcParams['pdf.fonttype'] @@ -2122,16 +2228,16 @@ def draw_mathtext(self, gc, x, y, s, prop, angle): self.check_gc(gc, gc._rgb) prev_font = None, None oldx, oldy = 0, 0 - type3_multibytes = [] + unsupported_chars = [] self.file.output(Op.begin_text) for font, fontsize, num, ox, oy in glyphs: - self.file._character_tracker.track(font, chr(num)) + self.file._character_tracker.track_glyph(font, num) fontname = font.fname - if fonttype == 3 and num > 255: - # For Type3 fonts, multibyte characters must be emitted - # separately (below). - type3_multibytes.append((font, fontsize, ox, oy, num)) + if not _font_supports_glyph(fonttype, num): + # Unsupported chars (i.e. multibyte in Type 3 or beyond BMP in + # Type 42) must be emitted separately (below). + unsupported_chars.append((font, fontsize, ox, oy, num)) else: self._setup_textpos(ox, oy, 0, oldx, oldy) oldx, oldy = ox, oy @@ -2143,7 +2249,7 @@ def draw_mathtext(self, gc, x, y, s, prop, angle): Op.show) self.file.output(Op.end_text) - for font, fontsize, ox, oy, num in type3_multibytes: + for font, fontsize, ox, oy, num in unsupported_chars: self._draw_xobject_glyph( font, fontsize, font.get_char_index(num), ox, oy) @@ -2155,8 +2261,7 @@ def draw_mathtext(self, gc, x, y, s, prop, angle): # Pop off the global transformation self.file.output(Op.grestore) - @_api.delete_parameter("3.3", "ismath") - def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!', mtext=None): + def draw_tex(self, gc, x, y, s, prop, angle, *, mtext=None): # docstring inherited texmanager = self.get_texmanager() fontsize = prop.get_size_in_points() @@ -2165,17 +2270,8 @@ def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!', mtext=None): page, = dvi if gc.get_url() is not None: - link_annotation = { - 'Type': Name('Annot'), - 'Subtype': Name('Link'), - 'Rect': (x, y, x + page.width, y + page.height), - 'Border': [0, 0, 0], - 'A': { - 'S': Name('URI'), - 'URI': gc.get_url(), - }, - } - self.file._annotations[-1][1].append(link_annotation) + self.file._annotations[-1][1].append(_get_link_annotation( + gc, x, y, page.width, page.height, angle)) # Gather font information and do some setup for combining # characters into strings. The variable seq will contain a @@ -2275,55 +2371,52 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): if gc.get_url() is not None: font.set_text(s) width, height = font.get_width_height() - link_annotation = { - 'Type': Name('Annot'), - 'Subtype': Name('Link'), - 'Rect': (x, y, x + width / 64, y + height / 64), - 'Border': [0, 0, 0], - 'A': { - 'S': Name('URI'), - 'URI': gc.get_url(), - }, - } - self.file._annotations[-1][1].append(link_annotation) + self.file._annotations[-1][1].append(_get_link_annotation( + gc, x, y, width / 64, height / 64, angle)) - # If fonttype != 3 emit the whole string at once without manual - # kerning. - if fonttype != 3: + # If fonttype is neither 3 nor 42, emit the whole string at once + # without manual kerning. + if fonttype not in [3, 42]: self.file.output(Op.begin_text, self.file.fontName(prop), fontsize, Op.selectfont) self._setup_textpos(x, y, angle) self.file.output(self.encode_string(s, fonttype), Op.show, Op.end_text) - # There is no way to access multibyte characters of Type 3 fonts, as - # they cannot have a CIDMap. Therefore, in this case we break the - # string into chunks, where each chunk contains either a string of - # consecutive 1-byte characters or a single multibyte character. - # A sequence of 1-byte characters is broken into multiple chunks to - # adjust the kerning between adjacent chunks. Each chunk is emitted - # with a separate command: 1-byte characters use the regular text show - # command (TJ) with appropriate kerning between chunks, whereas - # multibyte characters use the XObject command (Do). (If using Type - # 42 fonts, all of this complication is avoided, but of course, - # subsetting those fonts is complex/hard to implement.) + # A sequence of characters is broken into multiple chunks. The chunking + # serves two purposes: + # - For Type 3 fonts, there is no way to access multibyte characters, + # as they cannot have a CIDMap. Therefore, in this case we break + # the string into chunks, where each chunk contains either a string + # of consecutive 1-byte characters or a single multibyte character. + # - A sequence of 1-byte characters is split into chunks to allow for + # kerning adjustments between consecutive chunks. + # + # Each chunk is emitted with a separate command: 1-byte characters use + # the regular text show command (TJ) with appropriate kerning between + # chunks, whereas multibyte characters use the XObject command (Do). else: - # List of (start_x, [prev_kern, char, char, ...]), w/o zero kerns. + # List of (ft_object, start_x, [prev_kern, char, char, ...]), + # w/o zero kerns. singlebyte_chunks = [] - # List of (start_x, glyph_index). + # List of (ft_object, start_x, glyph_index). multibyte_glyphs = [] prev_was_multibyte = True - for item in _text_layout.layout( + prev_font = font + for item in _text_helpers.layout( s, font, kern_mode=KERNING_UNFITTED): - if ord(item.char) <= 255: - if prev_was_multibyte: - singlebyte_chunks.append((item.x, [])) + if _font_supports_glyph(fonttype, ord(item.char)): + if prev_was_multibyte or item.ft_object != prev_font: + singlebyte_chunks.append((item.ft_object, item.x, [])) + prev_font = item.ft_object if item.prev_kern: - singlebyte_chunks[-1][1].append(item.prev_kern) - singlebyte_chunks[-1][1].append(item.char) + singlebyte_chunks[-1][2].append(item.prev_kern) + singlebyte_chunks[-1][2].append(item.char) prev_was_multibyte = False else: - multibyte_glyphs.append((item.x, item.glyph_idx)) + multibyte_glyphs.append( + (item.ft_object, item.x, item.glyph_idx) + ) prev_was_multibyte = True # Do the rotation and global translation as a single matrix # concatenation up front @@ -2333,10 +2426,12 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): -math.sin(a), math.cos(a), x, y, Op.concat_matrix) # Emit all the 1-byte characters in a BT/ET group. - self.file.output(Op.begin_text, - self.file.fontName(prop), fontsize, Op.selectfont) + + self.file.output(Op.begin_text) prev_start_x = 0 - for start_x, kerns_or_chars in singlebyte_chunks: + for ft_object, start_x, kerns_or_chars in singlebyte_chunks: + ft_name = self.file.fontName(ft_object.fname) + self.file.output(ft_name, fontsize, Op.selectfont) self._setup_textpos(start_x, 0, 0, prev_start_x, 0, 0) self.file.output( # See pdf spec "Text space details" for the 1000/fontsize @@ -2348,14 +2443,16 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): prev_start_x = start_x self.file.output(Op.end_text) # Then emit all the multibyte characters, one at a time. - for start_x, glyph_idx in multibyte_glyphs: - self._draw_xobject_glyph(font, fontsize, glyph_idx, start_x, 0) + for ft_object, start_x, glyph_idx in multibyte_glyphs: + self._draw_xobject_glyph( + ft_object, fontsize, glyph_idx, start_x, 0 + ) self.file.output(Op.grestore) def _draw_xobject_glyph(self, font, fontsize, glyph_idx, x, y): """Draw a multibyte character from a Type 3 font as an XObject.""" - symbol_name = font.get_glyph_name(glyph_idx) - name = self.file._get_xobject_symbol_name(font.fname, symbol_name) + glyph_name = font.get_glyph_name(glyph_idx) + name = self.file._get_xobject_glyph_name(font.fname, glyph_name) self.file.output( Op.gsave, 0.001 * fontsize, 0, 0, 0.001 * fontsize, x, y, Op.concat_matrix, @@ -2657,11 +2754,8 @@ def savefig(self, figure=None, **kwargs): Parameters ---------- - figure : `.Figure` or int, optional - Specifies what figure is saved to file. If not specified, the - active figure is saved. If a `.Figure` instance is provided, this - figure is saved. If an int is specified, the figure instance to - save is looked up by number. + figure : `.Figure` or int, default: the active figure + The figure, or index of the figure, that is saved to the file. """ if not isinstance(figure, Figure): if figure is None: @@ -2702,15 +2796,11 @@ class FigureCanvasPdf(FigureCanvasBase): def get_default_filetype(self): return 'pdf' - @_check_savefig_extra_args - @_api.delete_parameter("3.4", "dpi") def print_pdf(self, filename, *, - dpi=None, # dpi to use for images bbox_inches_restore=None, metadata=None): - if dpi is None: # always use this branch after deprecation elapses. - dpi = self.figure.get_dpi() - self.figure.set_dpi(72) # there are 72 pdf points to an inch + dpi = self.figure.dpi + self.figure.dpi = 72 # there are 72 pdf points to an inch width, height = self.figure.get_size_inches() if isinstance(filename, PdfPages): file = filename._file @@ -2733,7 +2823,7 @@ def print_pdf(self, filename, *, file.close() def draw(self): - _no_output_draw(self.figure) + self.figure.draw_without_rendering() return super().draw() diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index 9d01f603e021..c3b900a97399 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -1,4 +1,3 @@ -import atexit import codecs import datetime import functools @@ -18,8 +17,7 @@ import matplotlib as mpl from matplotlib import _api, cbook, font_manager as fm from matplotlib.backend_bases import ( - _Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase, - GraphicsContextBase, RendererBase, _no_output_draw + _Backend, FigureCanvasBase, FigureManagerBase, RendererBase ) from matplotlib.backends.backend_mixed import MixedModeRenderer from matplotlib.backends.backend_pdf import ( @@ -36,58 +34,70 @@ # which is not recognized by TeX. -def get_fontspec(): - """Build fontspec preamble from rc.""" - latex_fontspec = [] - texcommand = mpl.rcParams["pgf.texsystem"] +@_api.caching_module_getattr +class __getattr__: + NO_ESCAPE = _api.deprecated("3.6", obj_type="")( + property(lambda self: _NO_ESCAPE)) + re_mathsep = _api.deprecated("3.6", obj_type="")( + property(lambda self: _split_math.__self__)) - if texcommand != "pdflatex": - latex_fontspec.append("\\usepackage{fontspec}") - if texcommand != "pdflatex" and mpl.rcParams["pgf.rcfonts"]: - families = ["serif", "sans\\-serif", "monospace"] - commands = ["setmainfont", "setsansfont", "setmonofont"] - for family, command in zip(families, commands): - # 1) Forward slashes also work on Windows, so don't mess with - # backslashes. 2) The dirname needs to include a separator. - path = pathlib.Path(fm.findfont(family)) - latex_fontspec.append(r"\%s{%s}[Path=\detokenize{%s}]" % ( - command, path.name, path.parent.as_posix() + "/")) - - return "\n".join(latex_fontspec) +@_api.deprecated("3.6") +def get_fontspec(): + """Build fontspec preamble from rc.""" + with mpl.rc_context({"pgf.preamble": ""}): + return _get_preamble() +@_api.deprecated("3.6") def get_preamble(): """Get LaTeX preamble from rc.""" return mpl.rcParams["pgf.preamble"] -############################################################################### -# This almost made me cry!!! -# In the end, it's better to use only one unit for all coordinates, since the +def _get_preamble(): + """Prepare a LaTeX preamble based on the rcParams configuration.""" + preamble = [mpl.rcParams["pgf.preamble"]] + if mpl.rcParams["pgf.texsystem"] != "pdflatex": + preamble.append("\\usepackage{fontspec}") + if mpl.rcParams["pgf.rcfonts"]: + families = ["serif", "sans\\-serif", "monospace"] + commands = ["setmainfont", "setsansfont", "setmonofont"] + for family, command in zip(families, commands): + # 1) Forward slashes also work on Windows, so don't mess with + # backslashes. 2) The dirname needs to include a separator. + path = pathlib.Path(fm.findfont(family)) + preamble.append(r"\%s{%s}[Path=\detokenize{%s/}]" % ( + command, path.name, path.parent.as_posix())) + preamble.append(mpl.texmanager._usepackage_if_not_loaded( + "underscore", option="strings")) # Documented as "must come last". + return "\n".join(preamble) + + +# It's better to use only one unit for all coordinates, since the # arithmetic in latex seems to produce inaccurate conversions. latex_pt_to_in = 1. / 72.27 latex_in_to_pt = 1. / latex_pt_to_in mpl_pt_to_in = 1. / 72. mpl_in_to_pt = 1. / mpl_pt_to_in -############################################################################### -# helper functions - -NO_ESCAPE = r"(? 3 else 1.0 if has_fill: - writeln(self.fh, - r"\definecolor{currentfill}{rgb}{%f,%f,%f}" - % tuple(rgbFace[:3])) - writeln(self.fh, r"\pgfsetfillcolor{currentfill}") + _writeln(self.fh, + r"\definecolor{currentfill}{rgb}{%f,%f,%f}" + % tuple(rgbFace[:3])) + _writeln(self.fh, r"\pgfsetfillcolor{currentfill}") if has_fill and fillopacity != 1.0: - writeln(self.fh, r"\pgfsetfillopacity{%f}" % fillopacity) + _writeln(self.fh, r"\pgfsetfillopacity{%f}" % fillopacity) # linewidth and color lw = gc.get_linewidth() * mpl_pt_to_in * latex_in_to_pt stroke_rgba = gc.get_rgb() - writeln(self.fh, r"\pgfsetlinewidth{%fpt}" % lw) - writeln(self.fh, - r"\definecolor{currentstroke}{rgb}{%f,%f,%f}" - % stroke_rgba[:3]) - writeln(self.fh, r"\pgfsetstrokecolor{currentstroke}") + _writeln(self.fh, r"\pgfsetlinewidth{%fpt}" % lw) + _writeln(self.fh, + r"\definecolor{currentstroke}{rgb}{%f,%f,%f}" + % stroke_rgba[:3]) + _writeln(self.fh, r"\pgfsetstrokecolor{currentstroke}") if strokeopacity != 1.0: - writeln(self.fh, r"\pgfsetstrokeopacity{%f}" % strokeopacity) + _writeln(self.fh, r"\pgfsetstrokeopacity{%f}" % strokeopacity) # line style dash_offset, dash_list = gc.get_dashes() if dash_list is None: - writeln(self.fh, r"\pgfsetdash{}{0pt}") + _writeln(self.fh, r"\pgfsetdash{}{0pt}") else: - writeln(self.fh, - r"\pgfsetdash{%s}{%fpt}" - % ("".join(r"{%fpt}" % dash for dash in dash_list), - dash_offset)) + _writeln(self.fh, + r"\pgfsetdash{%s}{%fpt}" + % ("".join(r"{%fpt}" % dash for dash in dash_list), + dash_offset)) def _print_pgf_path(self, gc, path, transform, rgbFace=None): f = 1. / self.dpi # check for clip box / ignore clip for filled paths bbox = gc.get_clip_rectangle() if gc else None + maxcoord = 16383 / 72.27 * self.dpi # Max dimensions in LaTeX. if bbox and (rgbFace is None): p1, p2 = bbox.get_points() - clip = (p1[0], p1[1], p2[0], p2[1]) + clip = (max(p1[0], -maxcoord), max(p1[1], -maxcoord), + min(p2[0], maxcoord), min(p2[1], maxcoord)) else: - clip = None + clip = (-maxcoord, -maxcoord, maxcoord, maxcoord) # build path for points, code in path.iter_segments(transform, clip=clip): if code == Path.MOVETO: x, y = tuple(points) - writeln(self.fh, - r"\pgfpathmoveto{\pgfqpoint{%fin}{%fin}}" % - (f * x, f * y)) + _writeln(self.fh, + r"\pgfpathmoveto{\pgfqpoint{%fin}{%fin}}" % + (f * x, f * y)) elif code == Path.CLOSEPOLY: - writeln(self.fh, r"\pgfpathclose") + _writeln(self.fh, r"\pgfpathclose") elif code == Path.LINETO: x, y = tuple(points) - writeln(self.fh, - r"\pgfpathlineto{\pgfqpoint{%fin}{%fin}}" % - (f * x, f * y)) + _writeln(self.fh, + r"\pgfpathlineto{\pgfqpoint{%fin}{%fin}}" % + (f * x, f * y)) elif code == Path.CURVE3: cx, cy, px, py = tuple(points) coords = cx * f, cy * f, px * f, py * f - writeln(self.fh, - r"\pgfpathquadraticcurveto" - r"{\pgfqpoint{%fin}{%fin}}{\pgfqpoint{%fin}{%fin}}" - % coords) + _writeln(self.fh, + r"\pgfpathquadraticcurveto" + r"{\pgfqpoint{%fin}{%fin}}{\pgfqpoint{%fin}{%fin}}" + % coords) elif code == Path.CURVE4: c1x, c1y, c2x, c2y, px, py = tuple(points) coords = c1x * f, c1y * f, c2x * f, c2y * f, px * f, py * f - writeln(self.fh, - r"\pgfpathcurveto" - r"{\pgfqpoint{%fin}{%fin}}" - r"{\pgfqpoint{%fin}{%fin}}" - r"{\pgfqpoint{%fin}{%fin}}" - % coords) + _writeln(self.fh, + r"\pgfpathcurveto" + r"{\pgfqpoint{%fin}{%fin}}" + r"{\pgfqpoint{%fin}{%fin}}" + r"{\pgfqpoint{%fin}{%fin}}" + % coords) + + # apply pgf decorators + sketch_params = gc.get_sketch_params() if gc else None + if sketch_params is not None: + # Only "length" directly maps to "segment length" in PGF's API. + # PGF uses "amplitude" to pass the combined deviation in both x- + # and y-direction, while matplotlib only varies the length of the + # wiggle along the line ("randomness" and "length" parameters) + # and has a separate "scale" argument for the amplitude. + # -> Use "randomness" as PRNG seed to allow the user to force the + # same shape on multiple sketched lines + scale, length, randomness = sketch_params + if scale is not None: + # make matplotlib and PGF rendering visually similar + length *= 0.5 + scale *= 2 + # PGF guarantees that repeated loading is a no-op + _writeln(self.fh, r"\usepgfmodule{decorations}") + _writeln(self.fh, r"\usepgflibrary{decorations.pathmorphing}") + _writeln(self.fh, r"\pgfkeys{/pgf/decoration/.cd, " + f"segment length = {(length * f):f}in, " + f"amplitude = {(scale * f):f}in}}") + _writeln(self.fh, f"\\pgfmathsetseed{{{int(randomness)}}}") + _writeln(self.fh, r"\pgfdecoratecurrentpath{random steps}") def _pgf_path_draw(self, stroke=True, fill=False): actions = [] @@ -617,7 +642,7 @@ def _pgf_path_draw(self, stroke=True, fill=False): actions.append("stroke") if fill: actions.append("fill") - writeln(self.fh, r"\pgfusepath{%s}" % ",".join(actions)) + _writeln(self.fh, r"\pgfusepath{%s}" % ",".join(actions)) def option_scale_image(self): # docstring inherited @@ -635,9 +660,9 @@ def draw_image(self, gc, x, y, im, transform=None): return if not os.path.exists(getattr(self.fh, "name", "")): - _api.warn_external( + raise ValueError( "streamed pgf-code does not support raster graphics, consider " - "using the pgf-to-pdf option.") + "using the pgf-to-pdf option") # save the images to png files path = pathlib.Path(self.fh.name) @@ -646,29 +671,29 @@ def draw_image(self, gc, x, y, im, transform=None): self.image_counter += 1 # reference the image in the pgf picture - writeln(self.fh, r"\begin{pgfscope}") + _writeln(self.fh, r"\begin{pgfscope}") self._print_pgf_clip(gc) f = 1. / self.dpi # from display coords to inch if transform is None: - writeln(self.fh, - r"\pgfsys@transformshift{%fin}{%fin}" % (x * f, y * f)) + _writeln(self.fh, + r"\pgfsys@transformshift{%fin}{%fin}" % (x * f, y * f)) w, h = w * f, h * f else: tr1, tr2, tr3, tr4, tr5, tr6 = transform.frozen().to_values() - writeln(self.fh, - r"\pgfsys@transformcm{%f}{%f}{%f}{%f}{%fin}{%fin}" % - (tr1 * f, tr2 * f, tr3 * f, tr4 * f, - (tr5 + x) * f, (tr6 + y) * f)) + _writeln(self.fh, + r"\pgfsys@transformcm{%f}{%f}{%f}{%f}{%fin}{%fin}" % + (tr1 * f, tr2 * f, tr3 * f, tr4 * f, + (tr5 + x) * f, (tr6 + y) * f)) w = h = 1 # scale is already included in the transform interp = str(transform is None).lower() # interpolation in PDF reader - writeln(self.fh, - r"\pgftext[left,bottom]" - r"{%s[interpolate=%s,width=%fin,height=%fin]{%s}}" % - (_get_image_inclusion_command(), - interp, w, h, fname_img)) - writeln(self.fh, r"\end{pgfscope}") - - def draw_tex(self, gc, x, y, s, prop, angle, ismath="TeX!", mtext=None): + _writeln(self.fh, + r"\pgftext[left,bottom]" + r"{%s[interpolate=%s,width=%fin,height=%fin]{%s}}" % + (_get_image_inclusion_command(), + interp, w, h, fname_img)) + _writeln(self.fh, r"\end{pgfscope}") + + def draw_tex(self, gc, x, y, s, prop, angle, ismath="TeX", mtext=None): # docstring inherited self.draw_text(gc, x, y, s, prop, angle, ismath, mtext) @@ -676,20 +701,18 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): # docstring inherited # prepare string for tex - s = common_texification(s) - prop_cmds = _font_properties_str(prop) - s = r"%s %s" % (prop_cmds, s) + s = _escape_and_apply_props(s, prop) - writeln(self.fh, r"\begin{pgfscope}") + _writeln(self.fh, r"\begin{pgfscope}") alpha = gc.get_alpha() if alpha != 1.0: - writeln(self.fh, r"\pgfsetfillopacity{%f}" % alpha) - writeln(self.fh, r"\pgfsetstrokeopacity{%f}" % alpha) + _writeln(self.fh, r"\pgfsetfillopacity{%f}" % alpha) + _writeln(self.fh, r"\pgfsetstrokeopacity{%f}" % alpha) rgb = tuple(gc.get_rgb())[:3] - writeln(self.fh, r"\definecolor{textcolor}{rgb}{%f,%f,%f}" % rgb) - writeln(self.fh, r"\pgfsetstrokecolor{textcolor}") - writeln(self.fh, r"\pgfsetfillcolor{textcolor}") + _writeln(self.fh, r"\definecolor{textcolor}{rgb}{%f,%f,%f}" % rgb) + _writeln(self.fh, r"\pgfsetstrokecolor{textcolor}") + _writeln(self.fh, r"\pgfsetfillcolor{textcolor}") s = r"\color{textcolor}" + s dpi = self.figure.dpi @@ -718,15 +741,11 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): if angle != 0: text_args.append("rotate=%f" % angle) - writeln(self.fh, r"\pgftext[%s]{%s}" % (",".join(text_args), s)) - writeln(self.fh, r"\end{pgfscope}") + _writeln(self.fh, r"\pgftext[%s]{%s}" % (",".join(text_args), s)) + _writeln(self.fh, r"\end{pgfscope}") def get_text_width_height_descent(self, s, prop, ismath): # docstring inherited - - # check if the math is supposed to be displaystyled - s = common_texification(s) - # get text metrics in units of latex pt, convert to display units w, h, d = (LatexManager._get_cached_or_new() .get_width_height_descent(s, prop)) @@ -750,36 +769,6 @@ def points_to_pixels(self, points): return points * mpl_pt_to_in * self.dpi -@_api.deprecated("3.3", alternative="GraphicsContextBase") -class GraphicsContextPgf(GraphicsContextBase): - pass - - -@_api.deprecated("3.4") -class TmpDirCleaner: - _remaining_tmpdirs = set() - - @_api.classproperty - @_api.deprecated("3.4") - def remaining_tmpdirs(cls): - return cls._remaining_tmpdirs - - @staticmethod - @_api.deprecated("3.4") - def add(tmpdir): - TmpDirCleaner._remaining_tmpdirs.add(tmpdir) - - @staticmethod - @_api.deprecated("3.4") - @atexit.register - def cleanup_remaining_tmpdirs(): - for tmpdir in TmpDirCleaner._remaining_tmpdirs: - error_message = "error deleting tmp directory {}".format(tmpdir) - shutil.rmtree( - tmpdir, - onerror=lambda *args: _log.error(error_message)) - - class FigureCanvasPgf(FigureCanvasBase): filetypes = {"pgf": "LaTeX PGF picture", "pdf": "LaTeX compiled PGF picture", @@ -788,7 +777,6 @@ class FigureCanvasPgf(FigureCanvasBase): def get_default_filetype(self): return 'pdf' - @_check_savefig_extra_args def _print_pgf_to_fh(self, fh, *, bbox_inches_restore=None): header_text = """%% Creator: Matplotlib, PGF backend @@ -799,6 +787,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 @@ -811,39 +803,37 @@ def _print_pgf_to_fh(self, fh, *, bbox_inches_restore=None): # append the preamble used by the backend as a comment for debugging header_info_preamble = ["%% Matplotlib used the following preamble"] - for line in get_preamble().splitlines(): - header_info_preamble.append("%% " + line) - for line in get_fontspec().splitlines(): + for line in _get_preamble().splitlines(): header_info_preamble.append("%% " + line) header_info_preamble.append("%%") header_info_preamble = "\n".join(header_info_preamble) # get figure size in inch w, h = self.figure.get_figwidth(), self.figure.get_figheight() - dpi = self.figure.get_dpi() + dpi = self.figure.dpi # create pgfpicture environment and write the pgf code fh.write(header_text) fh.write(header_info_preamble) fh.write("\n") - writeln(fh, r"\begingroup") - writeln(fh, r"\makeatletter") - writeln(fh, r"\begin{pgfpicture}") - writeln(fh, - r"\pgfpathrectangle{\pgfpointorigin}{\pgfqpoint{%fin}{%fin}}" - % (w, h)) - writeln(fh, r"\pgfusepath{use as bounding box, clip}") + _writeln(fh, r"\begingroup") + _writeln(fh, r"\makeatletter") + _writeln(fh, r"\begin{pgfpicture}") + _writeln(fh, + r"\pgfpathrectangle{\pgfpointorigin}{\pgfqpoint{%fin}{%fin}}" + % (w, h)) + _writeln(fh, r"\pgfusepath{use as bounding box, clip}") renderer = MixedModeRenderer(self.figure, w, h, dpi, RendererPgf(self.figure, fh), bbox_inches_restore=bbox_inches_restore) self.figure.draw(renderer) # end the pgfpicture environment - writeln(fh, r"\end{pgfpicture}") - writeln(fh, r"\makeatother") - writeln(fh, r"\endgroup") + _writeln(fh, r"\end{pgfpicture}") + _writeln(fh, r"\makeatother") + _writeln(fh, r"\endgroup") - def print_pgf(self, fname_or_fh, *args, **kwargs): + def print_pgf(self, fname_or_fh, **kwargs): """ Output pgf macros for drawing the figure so it can be included and rendered in latex documents. @@ -851,54 +841,49 @@ def print_pgf(self, fname_or_fh, *args, **kwargs): with cbook.open_file_cm(fname_or_fh, "w", encoding="utf-8") as file: if not cbook.file_requires_unicode(file): file = codecs.getwriter("utf-8")(file) - self._print_pgf_to_fh(file, *args, **kwargs) + self._print_pgf_to_fh(file, **kwargs) - def print_pdf(self, fname_or_fh, *args, metadata=None, **kwargs): + def print_pdf(self, fname_or_fh, *, metadata=None, **kwargs): """Use LaTeX to compile a pgf generated figure to pdf.""" - w, h = self.figure.get_figwidth(), self.figure.get_figheight() + w, h = self.figure.get_size_inches() info_dict = _create_pdf_info_dict('pgf', metadata or {}) - hyperref_options = ','.join( + pdfinfo = ','.join( _metadata_to_str(k, v) for k, v in info_dict.items()) + # print figure to pgf and compile it with latex with TemporaryDirectory() as tmpdir: tmppath = pathlib.Path(tmpdir) - - # print figure to pgf and compile it with latex - self.print_pgf(tmppath / "figure.pgf", *args, **kwargs) - - latexcode = """ -\\PassOptionsToPackage{pdfinfo={%s}}{hyperref} -\\RequirePackage{hyperref} -\\documentclass[12pt]{minimal} -\\usepackage[paperwidth=%fin, paperheight=%fin, margin=0in]{geometry} -%s -%s -\\usepackage{pgf} - -\\begin{document} -\\centering -\\input{figure.pgf} -\\end{document}""" % (hyperref_options, w, h, get_preamble(), get_fontspec()) - (tmppath / "figure.tex").write_text(latexcode, encoding="utf-8") - + self.print_pgf(tmppath / "figure.pgf", **kwargs) + (tmppath / "figure.tex").write_text( + "\n".join([ + r"\documentclass[12pt]{article}", + r"\usepackage[pdfinfo={%s}]{hyperref}" % pdfinfo, + r"\usepackage[papersize={%fin,%fin}, margin=0in]{geometry}" + % (w, h), + r"\usepackage{pgf}", + _get_preamble(), + r"\begin{document}", + r"\centering", + r"\input{figure.pgf}", + r"\end{document}", + ]), encoding="utf-8") texcommand = mpl.rcParams["pgf.texsystem"] cbook._check_and_log_subprocess( [texcommand, "-interaction=nonstopmode", "-halt-on-error", "figure.tex"], _log, cwd=tmpdir) - with (tmppath / "figure.pdf").open("rb") as orig, \ cbook.open_file_cm(fname_or_fh, "wb") as dest: shutil.copyfileobj(orig, dest) # copy file contents to target - def print_png(self, fname_or_fh, *args, **kwargs): + def print_png(self, fname_or_fh, **kwargs): """Use LaTeX to compile a pgf figure to pdf and convert it to png.""" converter = make_pdf_to_png_converter() with TemporaryDirectory() as tmpdir: tmppath = pathlib.Path(tmpdir) pdf_path = tmppath / "figure.pdf" png_path = tmppath / "figure.png" - self.print_pdf(pdf_path, *args, **kwargs) + self.print_pdf(pdf_path, **kwargs) converter(pdf_path, png_path, dpi=self.figure.dpi) with png_path.open("rb") as orig, \ cbook.open_file_cm(fname_or_fh, "wb") as dest: @@ -908,7 +893,7 @@ def get_renderer(self): return RendererPgf(self.figure, None) def draw(self): - _no_output_draw(self.figure) + self.figure.draw_without_rendering() return super().draw() @@ -943,7 +928,6 @@ class PdfPages: '_info_dict', '_metadata', ) - metadata = _api.deprecated('3.3')(property(lambda self: self._metadata)) def __init__(self, filename, *, keep_empty=True, metadata=None): """ @@ -968,58 +952,30 @@ def __init__(self, filename, *, keep_empty=True, metadata=None): 'Creator', 'Producer', 'CreationDate', 'ModDate', and 'Trapped'. Values have been predefined for 'Creator', 'Producer' and 'CreationDate'. They can be removed by setting them to `None`. + + Note that some versions of LaTeX engines may ignore the 'Producer' + key and set it to themselves. """ self._output_name = filename self._n_figures = 0 self.keep_empty = keep_empty self._metadata = (metadata or {}).copy() - if metadata: - for key in metadata: - canonical = { - 'creationdate': 'CreationDate', - 'moddate': 'ModDate', - }.get(key.lower(), key.lower().title()) - if canonical != key: - _api.warn_deprecated( - '3.3', message='Support for setting PDF metadata keys ' - 'case-insensitively is deprecated since %(since)s and ' - 'will be removed %(removal)s; ' - f'set {canonical} instead of {key}.') - self._metadata[canonical] = self._metadata.pop(key) self._info_dict = _create_pdf_info_dict('pgf', self._metadata) self._file = BytesIO() def _write_header(self, width_inches, height_inches): - hyperref_options = ','.join( + pdfinfo = ','.join( _metadata_to_str(k, v) for k, v in self._info_dict.items()) - - latex_preamble = get_preamble() - latex_fontspec = get_fontspec() - latex_header = r"""\PassOptionsToPackage{{ - pdfinfo={{ - {metadata} - }} -}}{{hyperref}} -\RequirePackage{{hyperref}} -\documentclass[12pt]{{minimal}} -\usepackage[ - paperwidth={width}in, - paperheight={height}in, - margin=0in -]{{geometry}} -{preamble} -{fontspec} -\usepackage{{pgf}} -\setlength{{\parindent}}{{0pt}} - -\begin{{document}}%% -""".format( - width=width_inches, - height=height_inches, - preamble=latex_preamble, - fontspec=latex_fontspec, - metadata=hyperref_options, - ) + latex_header = "\n".join([ + r"\documentclass[12pt]{article}", + r"\usepackage[pdfinfo={%s}]{hyperref}" % pdfinfo, + r"\usepackage[papersize={%fin,%fin}, margin=0in]{geometry}" + % (width_inches, height_inches), + r"\usepackage{pgf}", + _get_preamble(), + r"\setlength{\parindent}{0pt}", + r"\begin{document}%", + ]) self._file.write(latex_header.encode('utf-8')) def __enter__(self): @@ -1059,11 +1015,8 @@ def savefig(self, figure=None, **kwargs): Parameters ---------- - figure : `.Figure` or int, optional - Specifies what figure is saved to file. If not specified, the - active figure is saved. If a `.Figure` instance is provided, this - figure is saved. If an int is specified, the figure instance to - save is looked up by number. + figure : `.Figure` or int, default: the active figure + The figure, or index of the figure, that is saved to the file. """ if not isinstance(figure, Figure): if figure is None: diff --git a/lib/matplotlib/backends/backend_ps.py b/lib/matplotlib/backends/backend_ps.py index 5dbc841adc92..629f0133ed52 100644 --- a/lib/matplotlib/backends/backend_ps.py +++ b/lib/matplotlib/backends/backend_ps.py @@ -2,12 +2,12 @@ A PostScript backend, which can produce both PostScript .ps and .eps. """ +import codecs import datetime from enum import Enum -import glob -from io import StringIO, TextIOWrapper +import functools +from io import StringIO import logging -import math import os import pathlib import re @@ -18,17 +18,14 @@ import numpy as np import matplotlib as mpl -from matplotlib import _api, cbook, _path -from matplotlib import _text_layout -from matplotlib.afm import AFM +from matplotlib import _api, cbook, _path, _text_helpers +from matplotlib._afm import AFM from matplotlib.backend_bases import ( - _Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase, - GraphicsContextBase, RendererBase, _no_output_draw) + _Backend, FigureCanvasBase, FigureManagerBase, RendererBase) from matplotlib.cbook import is_writable_file_like, file_requires_unicode from matplotlib.font_manager import get_font -from matplotlib.ft2font import LOAD_NO_HINTING, LOAD_NO_SCALE +from matplotlib.ft2font import LOAD_NO_SCALE, FT2Font from matplotlib._ttconv import convert_ttf_to_ps -from matplotlib.mathtext import MathTextParser from matplotlib._mathtext_data import uni2type1 from matplotlib.path import Path from matplotlib.texmanager import TexManager @@ -36,11 +33,9 @@ from matplotlib.backends.backend_mixed import MixedModeRenderer from . import _backend_pdf_ps -_log = logging.getLogger(__name__) - -backend_version = 'Level II' -debugPS = 0 +_log = logging.getLogger(__name__) +debugPS = False class PsBackendHelper: @@ -62,9 +57,9 @@ def __init__(self): 'a5': (5.83, 8.27), 'a6': (4.13, 5.83), 'a7': (2.91, 4.13), - 'a8': (2.07, 2.91), - 'a9': (1.457, 2.05), - 'a10': (1.02, 1.457), + 'a8': (2.05, 2.91), + 'a9': (1.46, 2.05), + 'a10': (1.02, 1.46), 'b0': (40.55, 57.32), 'b1': (28.66, 40.55), 'b2': (20.27, 28.66), @@ -87,24 +82,11 @@ def _get_papertype(w, h): return 'a0' -def _num_to_str(val): - if isinstance(val, str): - return val - - ival = int(val) - if val == ival: - return str(ival) - - s = "%1.3f" % val - s = s.rstrip("0") - s = s.rstrip(".") - return s - - def _nums_to_str(*args): - return ' '.join(map(_num_to_str, args)) + return " ".join(f"{arg:1.3f}".rstrip("0").rstrip(".") for arg in args) +@_api.deprecated("3.6", alternative="a vendored copy of this function") def quote_ps_string(s): """ Quote dangerous characters of S for use in a PostScript string constant. @@ -134,16 +116,16 @@ def _move_path_to_path_or_stream(src, dst): shutil.move(src, dst, copy_function=shutil.copyfile) -def _font_to_ps_type3(font_path, glyph_ids): +def _font_to_ps_type3(font_path, chars): """ - Subset *glyph_ids* from the font at *font_path* into a Type 3 font. + Subset *chars* from the font at *font_path* into a Type 3 font. Parameters ---------- font_path : path-like Path to the font to be subsetted. - glyph_ids : list of int - The glyph indices to include in the subsetted font. + chars : str + The characters to include in the subsetted font. Returns ------- @@ -152,6 +134,7 @@ def _font_to_ps_type3(font_path, glyph_ids): verbatim into a PostScript file. """ font = get_font(font_path, hinting_factor=1) + glyph_ids = [font.get_char_index(c) for c in chars] preamble = """\ %!PS-Adobe-3.0 Resource-Font @@ -180,12 +163,12 @@ def _font_to_ps_type3(font_path, glyph_ids): 2 copy known not {pop /.notdef} if true 3 1 roll get exec end -} d +} _d /BuildChar { 1 index /Encoding get exch get 1 index /BuildGlyph get exec -} d +} _d FontName currentdict end definefont pop """ @@ -208,12 +191,64 @@ def _font_to_ps_type3(font_path, glyph_ids): # decomposer always explicitly moving to the closing point # first). [b"m", b"l", b"", b"c", b""], True).decode("ascii") - + "ce} d" + + "ce} _d" ) return preamble + "\n".join(entries) + postamble +def _font_to_ps_type42(font_path, chars, fh): + """ + Subset *chars* from the font at *font_path* into a Type 42 font at *fh*. + + Parameters + ---------- + font_path : path-like + Path to the font to be subsetted. + chars : str + The characters to include in the subsetted font. + fh : file-like + Where to write the font. + """ + subset_str = ''.join(chr(c) for c in chars) + _log.debug("SUBSET %s characters: %s", font_path, subset_str) + try: + fontdata = _backend_pdf_ps.get_glyphs_subset(font_path, subset_str) + _log.debug("SUBSET %s %d -> %d", font_path, os.stat(font_path).st_size, + fontdata.getbuffer().nbytes) + + # Give ttconv a subsetted font along with updated glyph_ids. + font = FT2Font(fontdata) + glyph_ids = [font.get_char_index(c) for c in chars] + with TemporaryDirectory() as tmpdir: + tmpfile = os.path.join(tmpdir, "tmp.ttf") + + with open(tmpfile, 'wb') as tmp: + tmp.write(fontdata.getvalue()) + + # TODO: allow convert_ttf_to_ps to input file objects (BytesIO) + convert_ttf_to_ps(os.fsencode(tmpfile), fh, 42, glyph_ids) + except RuntimeError: + _log.warning( + "The PostScript backend does not currently " + "support the selected font.") + raise + + +def _log_if_debug_on(meth): + """ + Wrap `RendererPS` method *meth* to emit a PS comment with the method name, + if the global flag `debugPS` is set. + """ + @functools.wraps(meth) + def wrapper(self, *args, **kwargs): + if debugPS: + self._pswriter.write(f"% {meth.__name__}\n") + return meth(self, *args, **kwargs) + + return wrapper + + class RendererPS(_backend_pdf_ps.RendererPDFPSBase): """ The renderer handles all the drawing primitives using a graphics @@ -223,11 +258,6 @@ class RendererPS(_backend_pdf_ps.RendererPDFPSBase): _afm_font_dir = cbook._get_data_path("fonts/afm") _use_afm_rc_name = "ps.useafm" - mathtext_parser = _api.deprecated("3.4")(property( - lambda self: MathTextParser("PS"))) - used_characters = _api.deprecated("3.3")(property( - lambda self: self._character_tracker.used_characters)) - def __init__(self, width, height, pswriter, imagedpi=72): # Although postscript itself is dpi independent, we need to inform the # image code about a requested dpi to generate high resolution images @@ -253,23 +283,27 @@ def __init__(self, width, height, pswriter, imagedpi=72): self._path_collection_id = 0 self._character_tracker = _backend_pdf_ps.CharacterTracker() - - @_api.deprecated("3.3") - def track_characters(self, *args, **kwargs): - """Keep track of which characters are required from each font.""" - self._character_tracker.track(*args, **kwargs) - - @_api.deprecated("3.3") - def merge_used_characters(self, *args, **kwargs): - self._character_tracker.merge(*args, **kwargs) + self._logwarn_once = functools.lru_cache(None)(_log.warning) + + def _is_transparent(self, rgb_or_rgba): + if rgb_or_rgba is None: + return True # Consistent with rgbFace semantics. + elif len(rgb_or_rgba) == 4: + if rgb_or_rgba[3] == 0: + return True + if rgb_or_rgba[3] != 1: + self._logwarn_once( + "The PostScript backend does not support transparency; " + "partially transparent artists will be rendered opaque.") + return False + else: # len() == 3. + return False def set_color(self, r, g, b, store=True): if (r, g, b) != self.color: - if r == g and r == b: - self._pswriter.write("%1.3f setgray\n" % r) - else: - self._pswriter.write( - "%1.3f %1.3f %1.3f setrgbcolor\n" % (r, g, b)) + self._pswriter.write(f"{r:1.3f} setgray\n" + if r == g == b else + f"{r:1.3f} {g:1.3f} {b:1.3f} setrgbcolor\n") if store: self.color = (r, g, b) @@ -312,11 +346,10 @@ def set_linedash(self, offset, seq, store=True): if np.array_equal(seq, oldseq) and oldo == offset: return - if seq is not None and len(seq): - s = "[%s] %d setdash\n" % (_nums_to_str(*seq), offset) - self._pswriter.write(s) - else: - self._pswriter.write("[] 0 setdash\n") + self._pswriter.write(f"[{_nums_to_str(*seq)}]" + f" {_nums_to_str(offset)} setdash\n" + if seq is not None and len(seq) else + "[] 0 setdash\n") if store: self.linedash = (offset, seq) @@ -344,7 +377,7 @@ def create_hatch(self, hatch): /PaintProc {{ pop - {linewidth:f} setlinewidth + {linewidth:g} setlinewidth {self._convert_path( Path.hatch(hatch), Affine2D().scale(sidelen), simplify=False)} gsave @@ -354,7 +387,7 @@ def create_hatch(self, hatch): }} bind >> matrix - 0.0 {pageheight:f} translate + 0 {pageheight:g} translate makepattern /{name} exch def """) @@ -388,7 +421,7 @@ def _get_clip_cmd(self, gc): key = (path, id(trf)) custom_clip_cmd = self._clip_paths.get(key) if custom_clip_cmd is None: - custom_clip_cmd = "c%x" % len(self._clip_paths) + custom_clip_cmd = "c%d" % len(self._clip_paths) self._pswriter.write(f"""\ /{custom_clip_cmd} {{ {self._convert_path(path, trf, simplify=False)} @@ -400,23 +433,14 @@ def _get_clip_cmd(self, gc): clip.append(f"{custom_clip_cmd}\n") return "".join(clip) + @_log_if_debug_on def draw_image(self, gc, x, y, im, transform=None): # docstring inherited h, w = im.shape[:2] imagecmd = "false 3 colorimage" data = im[::-1, :, :3] # Vertically flipped rgb values. - # data.tobytes().hex() has no spaces, so can be linewrapped by simply - # splitting data every nchars. It's equivalent to textwrap.fill only - # much faster. - nchars = 128 - data = data.tobytes().hex() - hexlines = "\n".join( - [ - data[n * nchars:(n + 1) * nchars] - for n in range(math.ceil(len(data) / nchars)) - ] - ) + hexdata = data.tobytes().hex("\n", -64) # Linewrap to 128 chars. if transform is None: matrix = "1 0 0 1 0 0" @@ -430,18 +454,19 @@ def draw_image(self, gc, x, y, im, transform=None): self._pswriter.write(f"""\ gsave {self._get_clip_cmd(gc)} -{x:f} {y:f} translate +{x:g} {y:g} translate [{matrix}] concat -{xscale:f} {yscale:f} scale +{xscale:g} {yscale:g} scale /DataString {w:d} string def {w:d} {h:d} 8 [ {w:d} 0 0 -{h:d} 0 {h:d} ] {{ currentfile DataString readhexstring pop }} bind {imagecmd} -{hexlines} +{hexdata} grestore """) + @_log_if_debug_on def draw_path(self, gc, path, transform, rgbFace=None): # docstring inherited clip = rgbFace is None and gc.get_hatch_path() is None @@ -449,16 +474,14 @@ def draw_path(self, gc, path, transform, rgbFace=None): ps = self._convert_path(path, transform, clip=clip, simplify=simplify) self._draw_ps(ps, gc, rgbFace) + @_log_if_debug_on def draw_markers( self, gc, marker_path, marker_trans, path, trans, rgbFace=None): # docstring inherited - if debugPS: - self._pswriter.write('% draw_markers \n') - ps_color = ( None - if _is_transparent(rgbFace) + if self._is_transparent(rgbFace) else '%1.3f setgray' % rgbFace[0] if rgbFace[0] == rgbFace[1] == rgbFace[2] else '%1.3f %1.3f %1.3f setrgbcolor' % rgbFace[:3]) @@ -504,8 +527,9 @@ def draw_markers( ps = '\n'.join(ps_cmd) self._draw_ps(ps, gc, rgbFace, fill=False, stroke=False) + @_log_if_debug_on def draw_path_collection(self, gc, master_transform, paths, all_transforms, - offsets, offsetTrans, facecolors, edgecolors, + offsets, offset_trans, facecolors, edgecolors, linewidths, linestyles, antialiaseds, urls, offset_position): # Is the optimization worth it? Rough calculation: @@ -521,14 +545,14 @@ def draw_path_collection(self, gc, master_transform, paths, all_transforms, if not should_do_optimization: return RendererBase.draw_path_collection( self, gc, master_transform, paths, all_transforms, - offsets, offsetTrans, facecolors, edgecolors, + offsets, offset_trans, facecolors, edgecolors, linewidths, linestyles, antialiaseds, urls, offset_position) path_codes = [] for i, (path, transform) in enumerate(self._iter_collection_raw_paths( master_transform, paths, all_transforms)): - name = 'p%x_%x' % (self._path_collection_id, i) + name = 'p%d_%d' % (self._path_collection_id, i) path_bytes = self._convert_path(path, transform, simplify=False) self._pswriter.write(f"""\ /{name} {{ @@ -540,19 +564,22 @@ def draw_path_collection(self, gc, master_transform, paths, all_transforms, path_codes.append(name) for xo, yo, path_id, gc0, rgbFace in self._iter_collection( - gc, master_transform, all_transforms, path_codes, offsets, - offsetTrans, facecolors, edgecolors, linewidths, linestyles, + gc, path_codes, offsets, offset_trans, + facecolors, edgecolors, linewidths, linestyles, antialiaseds, urls, offset_position): ps = "%g %g %s" % (xo, yo, path_id) self._draw_ps(ps, gc0, rgbFace) self._path_collection_id += 1 - @_api.delete_parameter("3.3", "ismath") - def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!', mtext=None): + @_log_if_debug_on + def draw_tex(self, gc, x, y, s, prop, angle, *, mtext=None): # docstring inherited + if self._is_transparent(gc.get_rgb()): + return # Special handling for fully transparent. + if not hasattr(self, "psfrag"): - _log.warning( + self._logwarn_once( "The PS backend determines usetex status solely based on " "rcParams['text.usetex'] and does not support having " "usetex=True only for some elements; this element will thus " @@ -570,19 +597,11 @@ def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!', mtext=None): s = fontcmd % s tex = r'\color[rgb]{%s} %s' % (color, s) - corr = 0 # w/2*(fontsize-10)/10 - if dict.__getitem__(mpl.rcParams, 'text.latex.preview'): - # use baseline alignment! - pos = _nums_to_str(x-corr, y) - self.psfrag.append( - r'\psfrag{%s}[Bl][Bl][1][%f]{\fontsize{%f}{%f}%s}' % ( - thetext, angle, fontsize, fontsize*1.25, tex)) - else: - # Stick to the bottom alignment. - pos = _nums_to_str(x-corr, y-bl) - self.psfrag.append( - r'\psfrag{%s}[bl][bl][1][%f]{\fontsize{%f}{%f}%s}' % ( - thetext, angle, fontsize, fontsize*1.25, tex)) + # Stick to the bottom alignment. + pos = _nums_to_str(x, y-bl) + self.psfrag.append( + r'\psfrag{%s}[bl][bl][1][%f]{\fontsize{%f}{%f}%s}' % ( + thetext, angle, fontsize, fontsize*1.25, tex)) self._pswriter.write(f"""\ gsave @@ -593,13 +612,11 @@ def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!', mtext=None): """) self.textcnt += 1 + @_log_if_debug_on def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): # docstring inherited - if debugPS: - self._pswriter.write("% text\n") - - if _is_transparent(gc.get_rgb()): + if self._is_transparent(gc.get_rgb()): return # Special handling for fully transparent. if ismath == 'TeX': @@ -611,7 +628,7 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): if mpl.rcParams['ps.useafm']: font = self._get_font_afm(prop) scale = 0.001 * prop.get_size_in_points() - + stream = [] thisx = 0 last_name = None # kerns returns 0 for None. xs_names = [] @@ -627,64 +644,78 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): thisx += kern * scale xs_names.append((thisx, name)) thisx += width * scale + ps_name = (font.postscript_name + .encode("ascii", "replace").decode("ascii")) + stream.append((ps_name, xs_names)) else: font = self._get_font_ttf(prop) - font.set_text(s, 0, flags=LOAD_NO_HINTING) self._character_tracker.track(font, s) - xs_names = [(item.x, font.get_glyph_name(item.glyph_idx)) - for item in _text_layout.layout(s, font)] + stream = [] + prev_font = curr_stream = None + for item in _text_helpers.layout(s, font): + ps_name = (item.ft_object.postscript_name + .encode("ascii", "replace").decode("ascii")) + if item.ft_object is not prev_font: + if curr_stream: + stream.append(curr_stream) + prev_font = item.ft_object + curr_stream = [ps_name, []] + curr_stream[1].append( + (item.x, item.ft_object.get_glyph_name(item.glyph_idx)) + ) + # append the last entry if exists + if curr_stream: + stream.append(curr_stream) self.set_color(*gc.get_rgb()) - ps_name = (font.postscript_name - .encode("ascii", "replace").decode("ascii")) - self.set_font(ps_name, prop.get_size_in_points()) - thetext = "\n".join(f"{x:f} 0 m /{name:s} glyphshow" - for x, name in xs_names) - self._pswriter.write(f"""\ + + for ps_name, xs_names in stream: + self.set_font(ps_name, prop.get_size_in_points(), False) + thetext = "\n".join(f"{x:g} 0 m /{name:s} glyphshow" + for x, name in xs_names) + self._pswriter.write(f"""\ gsave {self._get_clip_cmd(gc)} -{x:f} {y:f} translate -{angle:f} rotate +{x:g} {y:g} translate +{angle:g} rotate {thetext} grestore """) + @_log_if_debug_on def draw_mathtext(self, gc, x, y, s, prop, angle): """Draw the math text using matplotlib.mathtext.""" - if debugPS: - self._pswriter.write("% mathtext\n") - width, height, descent, glyphs, rects = \ - self._text2path.mathtext_parser.parse( - s, 72, prop, - _force_standard_ps_fonts=mpl.rcParams["ps.useafm"]) + self._text2path.mathtext_parser.parse(s, 72, prop) self.set_color(*gc.get_rgb()) self._pswriter.write( f"gsave\n" - f"{x:f} {y:f} translate\n" - f"{angle:f} rotate\n") + f"{x:g} {y:g} translate\n" + f"{angle:g} rotate\n") lastfont = None for font, fontsize, num, ox, oy in glyphs: - self._character_tracker.track(font, chr(num)) + self._character_tracker.track_glyph(font, num) if (font.postscript_name, fontsize) != lastfont: lastfont = font.postscript_name, fontsize self._pswriter.write( f"/{font.postscript_name} {fontsize} selectfont\n") - symbol_name = ( + glyph_name = ( font.get_name_char(chr(num)) if isinstance(font, AFM) else font.get_glyph_name(font.get_char_index(num))) self._pswriter.write( - f"{ox:f} {oy:f} moveto\n" - f"/{symbol_name} glyphshow\n") + f"{ox:g} {oy:g} moveto\n" + f"/{glyph_name} glyphshow\n") for ox, oy, w, h in rects: self._pswriter.write(f"{ox} {oy} {w} {h} rectfill\n") self._pswriter.write("grestore\n") + @_log_if_debug_on def draw_gouraud_triangle(self, gc, points, colors, trans): self.draw_gouraud_triangles(gc, points.reshape((1, 3, 2)), colors.reshape((1, 3, 4)), trans) + @_log_if_debug_on def draw_gouraud_triangles(self, gc, points, colors, trans): assert len(points) == len(colors) assert points.ndim == 3 @@ -705,13 +736,13 @@ def draw_gouraud_triangles(self, gc, points, colors, trans): xmin, ymin = points_min xmax, ymax = points_max - streamarr = np.empty( + data = np.empty( shape[0] * shape[1], dtype=[('flags', 'u1'), ('points', '2>u4'), ('colors', '3u1')]) - streamarr['flags'] = 0 - streamarr['points'] = (flat_points - points_min) * factor - streamarr['colors'] = flat_colors[:, :3] * 255.0 - stream = quote_ps_string(streamarr.tobytes()) + data['flags'] = 0 + data['points'] = (flat_points - points_min) * factor + data['colors'] = flat_colors[:, :3] * 255.0 + hexdata = data.tobytes().hex("\n", -64) # Linewrap to 128 chars. self._pswriter.write(f"""\ gsave @@ -721,32 +752,30 @@ def draw_gouraud_triangles(self, gc, points, colors, trans): /BitsPerComponent 8 /BitsPerFlag 8 /AntiAlias true - /Decode [ {xmin:f} {xmax:f} {ymin:f} {ymax:f} 0 1 0 1 0 1 ] - /DataSource ({stream}) + /Decode [ {xmin:g} {xmax:g} {ymin:g} {ymax:g} 0 1 0 1 0 1 ] + /DataSource < +{hexdata} +> >> shfill grestore """) - def _draw_ps(self, ps, gc, rgbFace, fill=True, stroke=True, command=None): + def _draw_ps(self, ps, gc, rgbFace, *, fill=True, stroke=True): """ - Emit the PostScript snippet 'ps' with all the attributes from 'gc' - applied. 'ps' must consist of PostScript commands to construct a path. + Emit the PostScript snippet *ps* with all the attributes from *gc* + applied. *ps* must consist of PostScript commands to construct a path. - The fill and/or stroke kwargs can be set to False if the - 'ps' string already includes filling and/or stroking, in - which case _draw_ps is just supplying properties and - clipping. + The *fill* and/or *stroke* kwargs can be set to False if the *ps* + string already includes filling and/or stroking, in which case + `_draw_ps` is just supplying properties and clipping. """ - # local variable eliminates all repeated attribute lookups write = self._pswriter.write - if debugPS and command: - write("% "+command+"\n") mightstroke = (gc.get_linewidth() > 0 - and not _is_transparent(gc.get_rgb())) + and not self._is_transparent(gc.get_rgb())) if not mightstroke: stroke = False - if _is_transparent(rgbFace): + if self._is_transparent(rgbFace): fill = False hatch = gc.get_hatch() @@ -755,12 +784,12 @@ def _draw_ps(self, ps, gc, rgbFace, fill=True, stroke=True, command=None): self.set_linejoin(gc.get_joinstyle()) self.set_linecap(gc.get_capstyle()) self.set_linedash(*gc.get_dashes()) + if mightstroke or hatch: self.set_color(*gc.get_rgb()[:3]) write('gsave\n') write(self._get_clip_cmd(gc)) - # Jochen, is the strip necessary? - this could be a honking big string write(ps.strip()) write("\n") @@ -784,30 +813,6 @@ def _draw_ps(self, ps, gc, rgbFace, fill=True, stroke=True, command=None): write("grestore\n") -def _is_transparent(rgb_or_rgba): - if rgb_or_rgba is None: - return True # Consistent with rgbFace semantics. - elif len(rgb_or_rgba) == 4: - if rgb_or_rgba[3] == 0: - return True - if rgb_or_rgba[3] != 1: - _log.warning( - "The PostScript backend does not support transparency; " - "partially transparent artists will be rendered opaque.") - return False - else: # len() == 3. - return False - - -@_api.deprecated("3.4", alternative="GraphicsContextBase") -class GraphicsContextPS(GraphicsContextBase): - def get_capstyle(self): - return {'butt': 0, 'round': 1, 'projecting': 2}[super().get_capstyle()] - - def get_joinstyle(self): - return {'miter': 0, 'round': 1, 'bevel': 2}[super().get_joinstyle()] - - class _Orientation(Enum): portrait, landscape = range(2) @@ -823,21 +828,14 @@ class FigureCanvasPS(FigureCanvasBase): def get_default_filetype(self): return 'ps' - def print_ps(self, outfile, *args, **kwargs): - return self._print_ps(outfile, 'ps', *args, **kwargs) - - def print_eps(self, outfile, *args, **kwargs): - return self._print_ps(outfile, 'eps', *args, **kwargs) - - @_api.delete_parameter("3.4", "dpi") + @_api.delete_parameter("3.5", "args") def _print_ps( - self, outfile, format, *args, - dpi=None, metadata=None, papertype=None, orientation='portrait', + self, fmt, outfile, *args, + metadata=None, papertype=None, orientation='portrait', **kwargs): - if dpi is None: # always use this branch after deprecation elapses. - dpi = self.figure.get_dpi() - self.figure.set_dpi(72) # Override the dpi kwarg + dpi = self.figure.dpi + self.figure.dpi = 72 # Override the dpi kwarg dsc_comments = {} if isinstance(outfile, (str, os.PathLike)): @@ -868,12 +866,11 @@ def _print_ps( printer = (self._print_figure_tex if mpl.rcParams['text.usetex'] else self._print_figure) - printer(outfile, format, dpi=dpi, dsc_comments=dsc_comments, + printer(fmt, outfile, dpi=dpi, dsc_comments=dsc_comments, orientation=orientation, papertype=papertype, **kwargs) - @_check_savefig_extra_args def _print_figure( - self, outfile, format, *, + self, fmt, outfile, *, dpi, dsc_comments, orientation, papertype, bbox_inches_restore=None): """ @@ -883,13 +880,9 @@ def _print_figure( all string containing Document Structuring Convention comments, generated from the *metadata* parameter to `.print_figure`. """ - is_eps = format == 'eps' - if isinstance(outfile, (str, os.PathLike)): - outfile = os.fspath(outfile) - passed_in_file_object = False - elif is_writable_file_like(outfile): - passed_in_file_object = True - else: + is_eps = fmt == 'eps' + if not (isinstance(outfile, (str, os.PathLike)) + or is_writable_file_like(outfile)): raise ValueError("outfile must be a path or a file-like object") # find the appropriate papertype @@ -960,24 +953,15 @@ def print_figure_impl(fh): in ps_renderer._character_tracker.used.items(): if not chars: continue - font = get_font(font_path) - glyph_ids = [font.get_char_index(c) for c in chars] fonttype = mpl.rcParams['ps.fonttype'] # Can't use more than 255 chars from a single Type 3 font. - if len(glyph_ids) > 255: + if len(chars) > 255: fonttype = 42 fh.flush() if fonttype == 3: - fh.write(_font_to_ps_type3(font_path, glyph_ids)) - else: - try: - convert_ttf_to_ps(os.fsencode(font_path), - fh, fonttype, glyph_ids) - except RuntimeError: - _log.warning( - "The PostScript backend does not currently " - "support the selected font.") - raise + fh.write(_font_to_ps_type3(font_path, chars)) + else: # Type 42 only. + _font_to_ps_type42(font_path, chars, fh) print("end", file=fh) print("%%EndProlog", file=fh) @@ -1016,27 +1000,14 @@ def print_figure_impl(fh): tmpfile, is_eps, ptype=papertype, bbox=bbox) _move_path_to_path_or_stream(tmpfile, outfile) - else: - # Write directly to outfile. - if passed_in_file_object: - requires_unicode = file_requires_unicode(outfile) - - if not requires_unicode: - fh = TextIOWrapper(outfile, encoding="latin-1") - # Prevent the TextIOWrapper from closing the underlying - # file. - fh.close = lambda: None - else: - fh = outfile - - print_figure_impl(fh) - else: - with open(outfile, 'w', encoding='latin-1') as fh: - print_figure_impl(fh) + else: # Write directly to outfile. + with cbook.open_file_cm(outfile, "w", encoding="latin-1") as file: + if not file_requires_unicode(file): + file = codecs.getwriter("latin-1")(file) + print_figure_impl(file) - @_check_savefig_extra_args def _print_figure_tex( - self, outfile, format, *, + self, fmt, outfile, *, dpi, dsc_comments, orientation, papertype, bbox_inches_restore=None): """ @@ -1046,7 +1017,7 @@ def _print_figure_tex( The rest of the behavior is as for `._print_figure`. """ - is_eps = format == 'eps' + is_eps = fmt == 'eps' width, height = self.figure.get_size_inches() xo = 0 @@ -1070,8 +1041,8 @@ def _print_figure_tex( # write to a temp file, we'll move it to outfile when done with TemporaryDirectory() as tmpdir: - tmpfile = os.path.join(tmpdir, "tmp.ps") - pathlib.Path(tmpfile).write_text( + tmppath = pathlib.Path(tmpdir, "tmp.ps") + tmppath.write_text( f"""\ %!PS-Adobe-3.0 EPSF-3.0 {dsc_comments} @@ -1107,35 +1078,38 @@ def _print_figure_tex( papertype = _get_papertype(width, height) paper_width, paper_height = papersize[papertype] - texmanager = ps_renderer.get_texmanager() - font_preamble = texmanager.get_font_preamble() - custom_preamble = texmanager.get_custom_preamble() - - psfrag_rotated = convert_psfrags(tmpfile, ps_renderer.psfrag, - font_preamble, - custom_preamble, paper_width, - paper_height, - orientation.name) + psfrag_rotated = _convert_psfrags( + tmppath, ps_renderer.psfrag, paper_width, paper_height, + orientation.name) if (mpl.rcParams['ps.usedistiller'] == 'ghostscript' or mpl.rcParams['text.usetex']): _try_distill(gs_distill, - tmpfile, is_eps, ptype=papertype, bbox=bbox, + tmppath, is_eps, ptype=papertype, bbox=bbox, rotated=psfrag_rotated) elif mpl.rcParams['ps.usedistiller'] == 'xpdf': _try_distill(xpdf_distill, - tmpfile, is_eps, ptype=papertype, bbox=bbox, + tmppath, is_eps, ptype=papertype, bbox=bbox, rotated=psfrag_rotated) - _move_path_to_path_or_stream(tmpfile, outfile) + _move_path_to_path_or_stream(tmppath, outfile) + + print_ps = functools.partialmethod(_print_ps, "ps") + print_eps = functools.partialmethod(_print_ps, "eps") def draw(self): - _no_output_draw(self.figure) + self.figure.draw_without_rendering() return super().draw() +@_api.deprecated("3.6") def convert_psfrags(tmpfile, psfrags, font_preamble, custom_preamble, paper_width, paper_height, orientation): + return _convert_psfrags( + pathlib.Path(tmpfile), psfrags, paper_width, paper_height, orientation) + + +def _convert_psfrags(tmppath, psfrags, paper_width, paper_height, orientation): """ When we want to use the LaTeX backend with postscript, we write PSFrag tags to a temporary postscript file, each one marking a position for LaTeX to @@ -1146,8 +1120,9 @@ def convert_psfrags(tmpfile, psfrags, font_preamble, custom_preamble, with mpl.rc_context({ "text.latex.preamble": mpl.rcParams["text.latex.preamble"] + - r"\usepackage{psfrag,color}""\n" - r"\usepackage[dvips]{graphicx}""\n" + mpl.texmanager._usepackage_if_not_loaded("color") + + mpl.texmanager._usepackage_if_not_loaded("graphicx") + + mpl.texmanager._usepackage_if_not_loaded("psfrag") + r"\geometry{papersize={%(width)sin,%(height)sin},margin=0in}" % {"width": paper_width, "height": paper_height} }): @@ -1161,7 +1136,7 @@ def convert_psfrags(tmpfile, psfrags, font_preamble, custom_preamble, % { "psfrags": "\n".join(psfrags), "angle": 90 if orientation == 'landscape' else 0, - "epsfile": pathlib.Path(tmpfile).resolve().as_posix(), + "epsfile": tmppath.resolve().as_posix(), }, fontsize=10) # tex's default fontsize. @@ -1169,7 +1144,7 @@ def convert_psfrags(tmpfile, psfrags, font_preamble, custom_preamble, psfile = os.path.join(tmpdir, "tmp.ps") cbook._check_and_log_subprocess( ['dvips', '-q', '-R0', '-o', psfile, dvifile], _log) - shutil.move(psfile, tmpfile) + shutil.move(psfile, tmppath) # check if the dvips created a ps in landscape paper. Somehow, # above latex+dvips results in a ps file in a landscape mode for a @@ -1178,14 +1153,14 @@ def convert_psfrags(tmpfile, psfrags, font_preamble, custom_preamble, # the generated ps file is in landscape and return this # information. The return value is used in pstoeps step to recover # the correct bounding box. 2010-06-05 JJL - with open(tmpfile) as fh: + with open(tmppath) as fh: psfrag_rotated = "Landscape" in fh.read(1000) return psfrag_rotated -def _try_distill(func, *args, **kwargs): +def _try_distill(func, tmppath, *args, **kwargs): try: - func(*args, **kwargs) + func(str(tmppath), *args, **kwargs) except mpl.ExecutableNotFoundError as exc: _log.warning("%s. Distillation step skipped.", exc) @@ -1234,32 +1209,26 @@ def xpdf_distill(tmpfile, eps=False, ptype='letter', bbox=None, rotated=False): mpl._get_executable_info("gs") # Effectively checks for ps2pdf. mpl._get_executable_info("pdftops") - pdffile = tmpfile + '.pdf' - psfile = tmpfile + '.ps' - - # Pass options as `-foo#bar` instead of `-foo=bar` to keep Windows happy - # (https://www.ghostscript.com/doc/9.22/Use.htm#MS_Windows). - cbook._check_and_log_subprocess( - ["ps2pdf", - "-dAutoFilterColorImages#false", - "-dAutoFilterGrayImages#false", - "-sAutoRotatePages#None", - "-sGrayImageFilter#FlateEncode", - "-sColorImageFilter#FlateEncode", - "-dEPSCrop" if eps else "-sPAPERSIZE#%s" % ptype, - tmpfile, pdffile], _log) - cbook._check_and_log_subprocess( - ["pdftops", "-paper", "match", "-level2", pdffile, psfile], _log) - - os.remove(tmpfile) - shutil.move(psfile, tmpfile) - + with TemporaryDirectory() as tmpdir: + tmppdf = pathlib.Path(tmpdir, "tmp.pdf") + tmpps = pathlib.Path(tmpdir, "tmp.ps") + # Pass options as `-foo#bar` instead of `-foo=bar` to keep Windows + # happy (https://ghostscript.com/doc/9.56.1/Use.htm#MS_Windows). + cbook._check_and_log_subprocess( + ["ps2pdf", + "-dAutoFilterColorImages#false", + "-dAutoFilterGrayImages#false", + "-sAutoRotatePages#None", + "-sGrayImageFilter#FlateEncode", + "-sColorImageFilter#FlateEncode", + "-dEPSCrop" if eps else "-sPAPERSIZE#%s" % ptype, + tmpfile, tmppdf], _log) + cbook._check_and_log_subprocess( + ["pdftops", "-paper", "match", "-level2", tmppdf, tmpps], _log) + shutil.move(tmpps, tmpfile) if eps: pstoeps(tmpfile) - for fname in glob.glob(tmpfile+'.*'): - os.remove(fname) - def get_bbox_header(lbrt, rotated=False): """ @@ -1348,28 +1317,31 @@ def pstoeps(tmpfile, bbox=None, rotated=False): # the matplotlib primitives and some abbreviations. # # References: -# http://www.adobe.com/products/postscript/pdfs/PLRM.pdf -# http://www.mactech.com/articles/mactech/Vol.09/09.04/PostscriptTutorial/ +# https://www.adobe.com/content/dam/acom/en/devnet/actionscript/articles/PLRM.pdf +# http://preserve.mactech.com/articles/mactech/Vol.09/09.04/PostscriptTutorial # http://www.math.ubc.ca/people/faculty/cass/graphics/text/www/ # # The usage comments use the notation of the operator summary # in the PostScript Language reference manual. psDefs = [ - # name proc *d* - - "/d { bind def } bind def", + # name proc *_d* - + # Note that this cannot be bound to /d, because when embedding a Type3 font + # we may want to define a "d" glyph using "/d{...} d" which would locally + # overwrite the definition. + "/_d { bind def } bind def", # x y *m* - - "/m { moveto } d", + "/m { moveto } _d", # x y *l* - - "/l { lineto } d", + "/l { lineto } _d", # x y *r* - - "/r { rlineto } d", + "/r { rlineto } _d", # x1 y1 x2 y2 x y *c* - - "/c { curveto } d", + "/c { curveto } _d", # *cl* - - "/cl { closepath } d", + "/cl { closepath } _d", # *ce* - - "/ce { closepath eofill } d", + "/ce { closepath eofill } _d", # w h x y *box* - """/box { m @@ -1377,18 +1349,19 @@ def pstoeps(tmpfile, bbox=None, rotated=False): 0 exch r neg 0 r cl - } d""", + } _d""", # w h x y *clipbox* - """/clipbox { box clip newpath - } d""", + } _d""", # wx wy llx lly urx ury *setcachedevice* - - "/sc { setcachedevice } d", + "/sc { setcachedevice } _d", ] @_Backend.export class _BackendPS(_Backend): + backend_version = 'Level II' FigureCanvas = FigureCanvasPS diff --git a/lib/matplotlib/backends/backend_qt.py b/lib/matplotlib/backends/backend_qt.py new file mode 100644 index 000000000000..2afff892ad2e --- /dev/null +++ b/lib/matplotlib/backends/backend_qt.py @@ -0,0 +1,1022 @@ +import functools +import os +import sys +import traceback + +import matplotlib as mpl +from matplotlib import _api, backend_tools, cbook +from matplotlib._pylab_helpers import Gcf +from matplotlib.backend_bases import ( + _Backend, FigureCanvasBase, FigureManagerBase, NavigationToolbar2, + TimerBase, cursors, ToolContainerBase, MouseButton, + CloseEvent, KeyEvent, LocationEvent, MouseEvent, ResizeEvent) +import matplotlib.backends.qt_editor.figureoptions as figureoptions +from . import qt_compat +from .qt_compat import ( + QtCore, QtGui, QtWidgets, __version__, QT_API, + _enum, _to_int, + _devicePixelRatioF, _isdeleted, _setDevicePixelRatio, + _maybe_allow_interrupt +) + + +# SPECIAL_KEYS are Qt::Key that do *not* return their Unicode name +# instead they have manually specified names. +SPECIAL_KEYS = { + _to_int(getattr(_enum("QtCore.Qt.Key"), k)): v for k, v in [ + ("Key_Escape", "escape"), + ("Key_Tab", "tab"), + ("Key_Backspace", "backspace"), + ("Key_Return", "enter"), + ("Key_Enter", "enter"), + ("Key_Insert", "insert"), + ("Key_Delete", "delete"), + ("Key_Pause", "pause"), + ("Key_SysReq", "sysreq"), + ("Key_Clear", "clear"), + ("Key_Home", "home"), + ("Key_End", "end"), + ("Key_Left", "left"), + ("Key_Up", "up"), + ("Key_Right", "right"), + ("Key_Down", "down"), + ("Key_PageUp", "pageup"), + ("Key_PageDown", "pagedown"), + ("Key_Shift", "shift"), + # In OSX, the control and super (aka cmd/apple) keys are switched. + ("Key_Control", "control" if sys.platform != "darwin" else "cmd"), + ("Key_Meta", "meta" if sys.platform != "darwin" else "control"), + ("Key_Alt", "alt"), + ("Key_CapsLock", "caps_lock"), + ("Key_F1", "f1"), + ("Key_F2", "f2"), + ("Key_F3", "f3"), + ("Key_F4", "f4"), + ("Key_F5", "f5"), + ("Key_F6", "f6"), + ("Key_F7", "f7"), + ("Key_F8", "f8"), + ("Key_F9", "f9"), + ("Key_F10", "f10"), + ("Key_F10", "f11"), + ("Key_F12", "f12"), + ("Key_Super_L", "super"), + ("Key_Super_R", "super"), + ] +} +# Define which modifier keys are collected on keyboard events. +# Elements are (Qt::KeyboardModifiers, Qt::Key) tuples. +# Order determines the modifier order (ctrl+alt+...) reported by Matplotlib. +_MODIFIER_KEYS = [ + (_to_int(getattr(_enum("QtCore.Qt.KeyboardModifier"), mod)), + _to_int(getattr(_enum("QtCore.Qt.Key"), key))) + for mod, key in [ + ("ControlModifier", "Key_Control"), + ("AltModifier", "Key_Alt"), + ("ShiftModifier", "Key_Shift"), + ("MetaModifier", "Key_Meta"), + ] +] +cursord = { + k: getattr(_enum("QtCore.Qt.CursorShape"), v) for k, v in [ + (cursors.MOVE, "SizeAllCursor"), + (cursors.HAND, "PointingHandCursor"), + (cursors.POINTER, "ArrowCursor"), + (cursors.SELECT_REGION, "CrossCursor"), + (cursors.WAIT, "WaitCursor"), + (cursors.RESIZE_HORIZONTAL, "SizeHorCursor"), + (cursors.RESIZE_VERTICAL, "SizeVerCursor"), + ] +} + + +@_api.caching_module_getattr +class __getattr__: + qApp = _api.deprecated( + "3.6", alternative="QtWidgets.QApplication.instance()")( + property(lambda self: QtWidgets.QApplication.instance())) + + +# lru_cache keeps a reference to the QApplication instance, keeping it from +# being GC'd. +@functools.lru_cache(1) +def _create_qApp(): + app = QtWidgets.QApplication.instance() + + # Create a new QApplication and configure if if non exists yet, as only one + # QApplication can exist at a time. + if app is None: + # display_is_valid returns False only if on Linux and neither X11 + # nor Wayland display can be opened. + if not mpl._c_internal_utils.display_is_valid(): + raise RuntimeError('Invalid DISPLAY variable') + + # Check to make sure a QApplication from a different major version + # of Qt is not instantiated in the process + if QT_API in {'PyQt6', 'PySide6'}: + other_bindings = ('PyQt5', 'PySide2') + elif QT_API in {'PyQt5', 'PySide2'}: + other_bindings = ('PyQt6', 'PySide6') + else: + raise RuntimeError("Should never be here") + + for binding in other_bindings: + mod = sys.modules.get(f'{binding}.QtWidgets') + if mod is not None and mod.QApplication.instance() is not None: + other_core = sys.modules.get(f'{binding}.QtCore') + _api.warn_external( + f'Matplotlib is using {QT_API} which wraps ' + f'{QtCore.qVersion()} however an instantiated ' + f'QApplication from {binding} which wraps ' + f'{other_core.qVersion()} exists. Mixing Qt major ' + 'versions may not work as expected.' + ) + break + try: + QtWidgets.QApplication.setAttribute( + QtCore.Qt.AA_EnableHighDpiScaling) + except AttributeError: # Only for Qt>=5.6, <6. + pass + try: + QtWidgets.QApplication.setHighDpiScaleFactorRoundingPolicy( + QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough) + except AttributeError: # Only for Qt>=5.14. + pass + app = QtWidgets.QApplication(["matplotlib"]) + if sys.platform == "darwin": + image = str(cbook._get_data_path('images/matplotlib.svg')) + icon = QtGui.QIcon(image) + app.setWindowIcon(icon) + app.lastWindowClosed.connect(app.quit) + cbook._setup_new_guiapp() + + try: + app.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps) # Only for Qt<6. + except AttributeError: + pass + + return app + + +class TimerQT(TimerBase): + """Subclass of `.TimerBase` using QTimer events.""" + + def __init__(self, *args, **kwargs): + # Create a new timer and connect the timeout() signal to the + # _on_timer method. + self._timer = QtCore.QTimer() + self._timer.timeout.connect(self._on_timer) + super().__init__(*args, **kwargs) + + def __del__(self): + # The check for deletedness is needed to avoid an error at animation + # shutdown with PySide2. + if not _isdeleted(self._timer): + self._timer_stop() + + def _timer_set_single_shot(self): + self._timer.setSingleShot(self._single) + + def _timer_set_interval(self): + self._timer.setInterval(self._interval) + + def _timer_start(self): + self._timer.start() + + def _timer_stop(self): + self._timer.stop() + + +class FigureCanvasQT(FigureCanvasBase, QtWidgets.QWidget): + required_interactive_framework = "qt" + _timer_cls = TimerQT + manager_class = _api.classproperty(lambda cls: FigureManagerQT) + + buttond = { + getattr(_enum("QtCore.Qt.MouseButton"), k): v for k, v in [ + ("LeftButton", MouseButton.LEFT), + ("RightButton", MouseButton.RIGHT), + ("MiddleButton", MouseButton.MIDDLE), + ("XButton1", MouseButton.BACK), + ("XButton2", MouseButton.FORWARD), + ] + } + + def __init__(self, figure=None): + _create_qApp() + super().__init__(figure=figure) + + self._draw_pending = False + self._is_drawing = False + self._draw_rect_callback = lambda painter: None + self._in_resize_event = False + + self.setAttribute( + _enum("QtCore.Qt.WidgetAttribute").WA_OpaquePaintEvent) + self.setMouseTracking(True) + self.resize(*self.get_width_height()) + + palette = QtGui.QPalette(QtGui.QColor("white")) + self.setPalette(palette) + + def _update_pixel_ratio(self): + if self._set_device_pixel_ratio(_devicePixelRatioF(self)): + # The easiest way to resize the canvas is to emit a resizeEvent + # since we implement all the logic for resizing the canvas for + # that event. + event = QtGui.QResizeEvent(self.size(), self.size()) + self.resizeEvent(event) + + def _update_screen(self, screen): + # Handler for changes to a window's attached screen. + self._update_pixel_ratio() + if screen is not None: + screen.physicalDotsPerInchChanged.connect(self._update_pixel_ratio) + screen.logicalDotsPerInchChanged.connect(self._update_pixel_ratio) + + def showEvent(self, event): + # Set up correct pixel ratio, and connect to any signal changes for it, + # once the window is shown (and thus has these attributes). + window = self.window().windowHandle() + window.screenChanged.connect(self._update_screen) + self._update_screen(window.screen()) + + def set_cursor(self, cursor): + # docstring inherited + self.setCursor(_api.check_getitem(cursord, cursor=cursor)) + + def mouseEventCoords(self, pos=None): + """ + Calculate mouse coordinates in physical pixels. + + Qt uses logical pixels, but the figure is scaled to physical + pixels for rendering. Transform to physical pixels so that + all of the down-stream transforms work as expected. + + Also, the origin is different and needs to be corrected. + """ + if pos is None: + pos = self.mapFromGlobal(QtGui.QCursor.pos()) + elif hasattr(pos, "position"): # qt6 QtGui.QEvent + pos = pos.position() + elif hasattr(pos, "pos"): # qt5 QtCore.QEvent + pos = pos.pos() + # (otherwise, it's already a QPoint) + x = pos.x() + # flip y so y=0 is bottom of canvas + y = self.figure.bbox.height / self.device_pixel_ratio - pos.y() + return x * self.device_pixel_ratio, y * self.device_pixel_ratio + + def enterEvent(self, event): + LocationEvent("figure_enter_event", self, + *self.mouseEventCoords(event), + guiEvent=event)._process() + + def leaveEvent(self, event): + QtWidgets.QApplication.restoreOverrideCursor() + LocationEvent("figure_leave_event", self, + *self.mouseEventCoords(), + guiEvent=event)._process() + + def mousePressEvent(self, event): + button = self.buttond.get(event.button()) + if button is not None: + MouseEvent("button_press_event", self, + *self.mouseEventCoords(event), button, + guiEvent=event)._process() + + def mouseDoubleClickEvent(self, event): + button = self.buttond.get(event.button()) + if button is not None: + MouseEvent("button_press_event", self, + *self.mouseEventCoords(event), button, dblclick=True, + guiEvent=event)._process() + + def mouseMoveEvent(self, event): + MouseEvent("motion_notify_event", self, + *self.mouseEventCoords(event), + guiEvent=event)._process() + + def mouseReleaseEvent(self, event): + button = self.buttond.get(event.button()) + if button is not None: + MouseEvent("button_release_event", self, + *self.mouseEventCoords(event), button, + guiEvent=event)._process() + + def wheelEvent(self, event): + # 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() + if steps: + MouseEvent("scroll_event", self, + *self.mouseEventCoords(event), step=steps, + guiEvent=event)._process() + + def keyPressEvent(self, event): + key = self._get_key(event) + if key is not None: + KeyEvent("key_press_event", self, + key, *self.mouseEventCoords(), + guiEvent=event)._process() + + def keyReleaseEvent(self, event): + key = self._get_key(event) + if key is not None: + KeyEvent("key_release_event", self, + key, *self.mouseEventCoords(), + guiEvent=event)._process() + + def resizeEvent(self, event): + if self._in_resize_event: # Prevent PyQt6 recursion + return + self._in_resize_event = True + try: + w = event.size().width() * self.device_pixel_ratio + h = event.size().height() * self.device_pixel_ratio + dpival = self.figure.dpi + winch = w / dpival + hinch = h / dpival + self.figure.set_size_inches(winch, hinch, forward=False) + # pass back into Qt to let it finish + QtWidgets.QWidget.resizeEvent(self, event) + # emit our resize events + ResizeEvent("resize_event", self)._process() + self.draw_idle() + finally: + self._in_resize_event = False + + def sizeHint(self): + w, h = self.get_width_height() + return QtCore.QSize(w, h) + + def minumumSizeHint(self): + return QtCore.QSize(10, 10) + + def _get_key(self, event): + event_key = event.key() + event_mods = _to_int(event.modifiers()) # actually a bitmask + + # get names of the pressed modifier keys + # 'control' is named 'control' when a standalone key, but 'ctrl' when a + # modifier + # bit twiddling to pick out modifier keys from event_mods bitmask, + # if event_key is a MODIFIER, it should not be duplicated in mods + mods = [SPECIAL_KEYS[key].replace('control', 'ctrl') + for mod, key in _MODIFIER_KEYS + if event_key != key and event_mods & mod] + try: + # for certain keys (enter, left, backspace, etc) use a word for the + # key, rather than Unicode + key = SPECIAL_KEYS[event_key] + except KeyError: + # Unicode defines code points up to 0x10ffff (sys.maxunicode) + # QT will use Key_Codes larger than that for keyboard keys that are + # are not Unicode characters (like multimedia keys) + # skip these + # if you really want them, you should add them to SPECIAL_KEYS + if event_key > sys.maxunicode: + return None + + key = chr(event_key) + # qt delivers capitalized letters. fix capitalization + # note that capslock is ignored + if 'shift' in mods: + mods.remove('shift') + else: + key = key.lower() + + return '+'.join(mods + [key]) + + def flush_events(self): + # docstring inherited + QtWidgets.QApplication.instance().processEvents() + + def start_event_loop(self, timeout=0): + # docstring inherited + if hasattr(self, "_event_loop") and self._event_loop.isRunning(): + raise RuntimeError("Event loop already running") + self._event_loop = event_loop = QtCore.QEventLoop() + if timeout > 0: + _ = QtCore.QTimer.singleShot(int(timeout * 1000), event_loop.quit) + + with _maybe_allow_interrupt(event_loop): + qt_compat._exec(event_loop) + + def stop_event_loop(self, event=None): + # docstring inherited + if hasattr(self, "_event_loop"): + self._event_loop.quit() + + def draw(self): + """Render the figure, and queue a request for a Qt draw.""" + # The renderer draw is done here; delaying causes problems with code + # that uses the result of the draw() to update plot elements. + if self._is_drawing: + return + with cbook._setattr_cm(self, _is_drawing=True): + super().draw() + self.update() + + def draw_idle(self): + """Queue redraw of the Agg buffer and request Qt paintEvent.""" + # The Agg draw needs to be handled by the same thread Matplotlib + # modifies the scene graph from. Post Agg draw request to the + # current event loop in order to ensure thread affinity and to + # accumulate multiple draw requests from event handling. + # TODO: queued signal connection might be safer than singleShot + if not (getattr(self, '_draw_pending', False) or + getattr(self, '_is_drawing', False)): + self._draw_pending = True + QtCore.QTimer.singleShot(0, self._draw_idle) + + def blit(self, bbox=None): + # docstring inherited + if bbox is None and self.figure: + bbox = self.figure.bbox # Blit the entire canvas if bbox is None. + # repaint uses logical pixels, not physical pixels like the renderer. + l, b, w, h = [int(pt / self.device_pixel_ratio) for pt in bbox.bounds] + t = b + h + self.repaint(l, self.rect().height() - t, w, h) + + def _draw_idle(self): + with self._idle_draw_cntx(): + if not self._draw_pending: + return + self._draw_pending = False + if self.height() < 0 or self.width() < 0: + return + try: + self.draw() + except Exception: + # Uncaught exceptions are fatal for PyQt5, so catch them. + traceback.print_exc() + + def drawRectangle(self, rect): + # Draw the zoom rectangle to the QPainter. _draw_rect_callback needs + # to be called at the end of paintEvent. + if rect is not None: + x0, y0, w, h = [int(pt / self.device_pixel_ratio) for pt in rect] + x1 = x0 + w + y1 = y0 + h + def _draw_rect_callback(painter): + pen = QtGui.QPen( + QtGui.QColor("black"), + 1 / self.device_pixel_ratio + ) + + pen.setDashPattern([3, 3]) + for color, offset in [ + (QtGui.QColor("black"), 0), + (QtGui.QColor("white"), 3), + ]: + pen.setDashOffset(offset) + pen.setColor(color) + painter.setPen(pen) + # Draw the lines from x0, y0 towards x1, y1 so that the + # dashes don't "jump" when moving the zoom box. + painter.drawLine(x0, y0, x0, y1) + painter.drawLine(x0, y0, x1, y0) + painter.drawLine(x0, y1, x1, y1) + painter.drawLine(x1, y0, x1, y1) + else: + def _draw_rect_callback(painter): + return + self._draw_rect_callback = _draw_rect_callback + self.update() + + +class MainWindow(QtWidgets.QMainWindow): + closing = QtCore.Signal() + + def closeEvent(self, event): + self.closing.emit() + super().closeEvent(event) + + +class FigureManagerQT(FigureManagerBase): + """ + Attributes + ---------- + canvas : `FigureCanvas` + The FigureCanvas instance + num : int or str + The Figure number + toolbar : qt.QToolBar + The qt.QToolBar + window : qt.QMainWindow + The qt.QMainWindow + """ + + def __init__(self, canvas, num): + self.window = MainWindow() + super().__init__(canvas, num) + self.window.closing.connect( + # The lambda prevents the event from being immediately gc'd. + lambda: CloseEvent("close_event", self.canvas)._process()) + self.window.closing.connect(self._widgetclosed) + + if sys.platform != "darwin": + image = str(cbook._get_data_path('images/matplotlib.svg')) + icon = QtGui.QIcon(image) + self.window.setWindowIcon(icon) + + self.window._destroying = False + + if self.toolbar: + self.window.addToolBar(self.toolbar) + tbs_height = self.toolbar.sizeHint().height() + else: + tbs_height = 0 + + # resize the main window so it will display the canvas with the + # requested size: + cs = canvas.sizeHint() + cs_height = cs.height() + height = cs_height + tbs_height + self.window.resize(cs.width(), height) + + self.window.setCentralWidget(self.canvas) + + if mpl.is_interactive(): + self.window.show() + self.canvas.draw_idle() + + # Give the keyboard focus to the figure instead of the manager: + # StrongFocus accepts both tab and click to focus and will enable the + # canvas to process event without clicking. + # https://doc.qt.io/qt-5/qt.html#FocusPolicy-enum + self.canvas.setFocusPolicy(_enum("QtCore.Qt.FocusPolicy").StrongFocus) + self.canvas.setFocus() + + self.window.raise_() + + def full_screen_toggle(self): + if self.window.isFullScreen(): + self.window.showNormal() + else: + self.window.showFullScreen() + + def _widgetclosed(self): + if self.window._destroying: + return + self.window._destroying = True + try: + Gcf.destroy(self) + except AttributeError: + pass + # It seems that when the python session is killed, + # Gcf can get destroyed before the Gcf.destroy + # line is run, leading to a useless AttributeError. + + def resize(self, width, height): + # The Qt methods return sizes in 'virtual' pixels so we do need to + # rescale from physical to logical pixels. + width = int(width / self.canvas.device_pixel_ratio) + height = int(height / self.canvas.device_pixel_ratio) + extra_width = self.window.width() - self.canvas.width() + extra_height = self.window.height() - self.canvas.height() + self.canvas.resize(width, height) + self.window.resize(width + extra_width, height + extra_height) + + def show(self): + self.window.show() + if mpl.rcParams['figure.raise_window']: + self.window.activateWindow() + self.window.raise_() + + def destroy(self, *args): + # check for qApp first, as PySide deletes it in its atexit handler + if QtWidgets.QApplication.instance() is None: + return + if self.window._destroying: + return + self.window._destroying = True + if self.toolbar: + self.toolbar.destroy() + self.window.close() + + def get_window_title(self): + return self.window.windowTitle() + + def set_window_title(self, title): + self.window.setWindowTitle(title) + + +class NavigationToolbar2QT(NavigationToolbar2, QtWidgets.QToolBar): + message = QtCore.Signal(str) + + toolitems = [*NavigationToolbar2.toolitems] + toolitems.insert( + # Add 'customize' action after 'subplots' + [name for name, *_ in toolitems].index("Subplots") + 1, + ("Customize", "Edit axis, curve and image parameters", + "qt4_editor_options", "edit_parameters")) + + def __init__(self, canvas, parent=None, coordinates=True): + """coordinates: should we show the coordinates on the right?""" + QtWidgets.QToolBar.__init__(self, parent) + self.setAllowedAreas(QtCore.Qt.ToolBarArea( + _to_int(_enum("QtCore.Qt.ToolBarArea").TopToolBarArea) | + _to_int(_enum("QtCore.Qt.ToolBarArea").BottomToolBarArea))) + + self.coordinates = coordinates + self._actions = {} # mapping of toolitem method names to QActions. + self._subplot_dialog = None + + for text, tooltip_text, image_file, callback in self.toolitems: + if text is None: + self.addSeparator() + else: + a = self.addAction(self._icon(image_file + '.png'), + text, getattr(self, callback)) + self._actions[callback] = a + if callback in ['zoom', 'pan']: + a.setCheckable(True) + if tooltip_text is not None: + a.setToolTip(tooltip_text) + + # Add the (x, y) location widget at the right side of the toolbar + # The stretch factor is 1 which means any resizing of the toolbar + # will resize this label instead of the buttons. + if self.coordinates: + self.locLabel = QtWidgets.QLabel("", self) + self.locLabel.setAlignment(QtCore.Qt.AlignmentFlag( + _to_int(_enum("QtCore.Qt.AlignmentFlag").AlignRight) | + _to_int(_enum("QtCore.Qt.AlignmentFlag").AlignVCenter))) + self.locLabel.setSizePolicy(QtWidgets.QSizePolicy( + _enum("QtWidgets.QSizePolicy.Policy").Expanding, + _enum("QtWidgets.QSizePolicy.Policy").Ignored, + )) + labelAction = self.addWidget(self.locLabel) + labelAction.setVisible(True) + + NavigationToolbar2.__init__(self, canvas) + + def _icon(self, name): + """ + Construct a `.QIcon` from an image file *name*, including the extension + and relative to Matplotlib's "images" data directory. + """ + # use a high-resolution icon with suffix '_large' if available + # note: user-provided icons may not have '_large' versions + path_regular = cbook._get_data_path('images', name) + path_large = path_regular.with_name( + path_regular.name.replace('.png', '_large.png')) + filename = str(path_large if path_large.exists() else path_regular) + + pm = QtGui.QPixmap(filename) + _setDevicePixelRatio(pm, _devicePixelRatioF(self)) + if self.palette().color(self.backgroundRole()).value() < 128: + icon_color = self.palette().color(self.foregroundRole()) + mask = pm.createMaskFromColor( + QtGui.QColor('black'), + _enum("QtCore.Qt.MaskMode").MaskOutColor) + pm.fill(icon_color) + pm.setMask(mask) + return QtGui.QIcon(pm) + + def edit_parameters(self): + axes = self.canvas.figure.get_axes() + if not axes: + QtWidgets.QMessageBox.warning( + self.canvas.parent(), "Error", "There are no axes to edit.") + return + elif len(axes) == 1: + ax, = axes + else: + titles = [ + ax.get_label() or + ax.get_title() or + ax.get_title("left") or + ax.get_title("right") or + " - ".join(filter(None, [ax.get_xlabel(), ax.get_ylabel()])) or + f"" + for ax in axes] + duplicate_titles = [ + title for title in titles if titles.count(title) > 1] + for i, ax in enumerate(axes): + if titles[i] in duplicate_titles: + titles[i] += f" (id: {id(ax):#x})" # Deduplicate titles. + item, ok = QtWidgets.QInputDialog.getItem( + self.canvas.parent(), + 'Customize', 'Select axes:', titles, 0, False) + if not ok: + return + ax = axes[titles.index(item)] + figureoptions.figure_edit(ax, self) + + def _update_buttons_checked(self): + # sync button checkstates to match active mode + if 'pan' in self._actions: + self._actions['pan'].setChecked(self.mode.name == 'PAN') + if 'zoom' in self._actions: + self._actions['zoom'].setChecked(self.mode.name == 'ZOOM') + + def pan(self, *args): + super().pan(*args) + self._update_buttons_checked() + + def zoom(self, *args): + super().zoom(*args) + self._update_buttons_checked() + + def set_message(self, s): + self.message.emit(s) + if self.coordinates: + self.locLabel.setText(s) + + def draw_rubberband(self, event, x0, y0, x1, y1): + height = self.canvas.figure.bbox.height + y1 = height - y1 + y0 = height - y0 + rect = [int(val) for val in (x0, y0, x1 - x0, y1 - y0)] + self.canvas.drawRectangle(rect) + + def remove_rubberband(self): + self.canvas.drawRectangle(None) + + def configure_subplots(self): + if self._subplot_dialog is None: + self._subplot_dialog = SubplotToolQt( + self.canvas.figure, self.canvas.parent()) + self.canvas.mpl_connect( + "close_event", lambda e: self._subplot_dialog.reject()) + self._subplot_dialog.update_from_current_subplotpars() + self._subplot_dialog.show() + return self._subplot_dialog + + def save_figure(self, *args): + filetypes = self.canvas.get_supported_filetypes_grouped() + sorted_filetypes = sorted(filetypes.items()) + default_filetype = self.canvas.get_default_filetype() + + startpath = os.path.expanduser(mpl.rcParams['savefig.directory']) + start = os.path.join(startpath, self.canvas.get_default_filename()) + filters = [] + selectedFilter = None + for name, exts in sorted_filetypes: + exts_list = " ".join(['*.%s' % ext for ext in exts]) + filter = '%s (%s)' % (name, exts_list) + if default_filetype in exts: + selectedFilter = filter + filters.append(filter) + filters = ';;'.join(filters) + + fname, filter = qt_compat._getSaveFileName( + self.canvas.parent(), "Choose a filename to save to", start, + filters, selectedFilter) + if fname: + # Save dir for next time, unless empty str (i.e., use cwd). + if startpath != "": + mpl.rcParams['savefig.directory'] = os.path.dirname(fname) + try: + self.canvas.figure.savefig(fname) + except Exception as e: + QtWidgets.QMessageBox.critical( + self, "Error saving file", str(e), + _enum("QtWidgets.QMessageBox.StandardButton").Ok, + _enum("QtWidgets.QMessageBox.StandardButton").NoButton) + + def set_history_buttons(self): + can_backward = self._nav_stack._pos > 0 + can_forward = self._nav_stack._pos < len(self._nav_stack._elements) - 1 + if 'back' in self._actions: + self._actions['back'].setEnabled(can_backward) + if 'forward' in self._actions: + self._actions['forward'].setEnabled(can_forward) + + +class SubplotToolQt(QtWidgets.QDialog): + def __init__(self, targetfig, parent): + super().__init__() + self.setWindowIcon(QtGui.QIcon( + str(cbook._get_data_path("images/matplotlib.png")))) + self.setObjectName("SubplotTool") + self._spinboxes = {} + main_layout = QtWidgets.QHBoxLayout() + self.setLayout(main_layout) + for group, spinboxes, buttons in [ + ("Borders", + ["top", "bottom", "left", "right"], + [("Export values", self._export_values)]), + ("Spacings", + ["hspace", "wspace"], + [("Tight layout", self._tight_layout), + ("Reset", self._reset), + ("Close", self.close)])]: + layout = QtWidgets.QVBoxLayout() + main_layout.addLayout(layout) + box = QtWidgets.QGroupBox(group) + layout.addWidget(box) + inner = QtWidgets.QFormLayout(box) + for name in spinboxes: + self._spinboxes[name] = spinbox = QtWidgets.QDoubleSpinBox() + spinbox.setRange(0, 1) + spinbox.setDecimals(3) + spinbox.setSingleStep(0.005) + spinbox.setKeyboardTracking(False) + spinbox.valueChanged.connect(self._on_value_changed) + inner.addRow(name, spinbox) + layout.addStretch(1) + for name, method in buttons: + button = QtWidgets.QPushButton(name) + # Don't trigger on , which is used to input values. + button.setAutoDefault(False) + button.clicked.connect(method) + layout.addWidget(button) + if name == "Close": + button.setFocus() + self._figure = targetfig + self._defaults = {} + self._export_values_dialog = None + self.update_from_current_subplotpars() + + def update_from_current_subplotpars(self): + self._defaults = {spinbox: getattr(self._figure.subplotpars, name) + for name, spinbox in self._spinboxes.items()} + self._reset() # Set spinbox current values without triggering signals. + + def _export_values(self): + # Explicitly round to 3 decimals (which is also the spinbox precision) + # to avoid numbers of the form 0.100...001. + self._export_values_dialog = QtWidgets.QDialog() + layout = QtWidgets.QVBoxLayout() + self._export_values_dialog.setLayout(layout) + text = QtWidgets.QPlainTextEdit() + text.setReadOnly(True) + layout.addWidget(text) + text.setPlainText( + ",\n".join(f"{attr}={spinbox.value():.3}" + for attr, spinbox in self._spinboxes.items())) + # Adjust the height of the text widget to fit the whole text, plus + # some padding. + size = text.maximumSize() + size.setHeight( + QtGui.QFontMetrics(text.document().defaultFont()) + .size(0, text.toPlainText()).height() + 20) + text.setMaximumSize(size) + self._export_values_dialog.show() + + def _on_value_changed(self): + spinboxes = self._spinboxes + # Set all mins and maxes, so that this can also be used in _reset(). + for lower, higher in [("bottom", "top"), ("left", "right")]: + spinboxes[higher].setMinimum(spinboxes[lower].value() + .001) + spinboxes[lower].setMaximum(spinboxes[higher].value() - .001) + self._figure.subplots_adjust( + **{attr: spinbox.value() for attr, spinbox in spinboxes.items()}) + self._figure.canvas.draw_idle() + + def _tight_layout(self): + self._figure.tight_layout() + for attr, spinbox in self._spinboxes.items(): + spinbox.blockSignals(True) + spinbox.setValue(vars(self._figure.subplotpars)[attr]) + spinbox.blockSignals(False) + self._figure.canvas.draw_idle() + + def _reset(self): + for spinbox, value in self._defaults.items(): + spinbox.setRange(0, 1) + spinbox.blockSignals(True) + spinbox.setValue(value) + spinbox.blockSignals(False) + self._on_value_changed() + + +class ToolbarQt(ToolContainerBase, QtWidgets.QToolBar): + def __init__(self, toolmanager, parent=None): + ToolContainerBase.__init__(self, toolmanager) + QtWidgets.QToolBar.__init__(self, parent) + self.setAllowedAreas(QtCore.Qt.ToolBarArea( + _to_int(_enum("QtCore.Qt.ToolBarArea").TopToolBarArea) | + _to_int(_enum("QtCore.Qt.ToolBarArea").BottomToolBarArea))) + message_label = QtWidgets.QLabel("") + message_label.setAlignment(QtCore.Qt.AlignmentFlag( + _to_int(_enum("QtCore.Qt.AlignmentFlag").AlignRight) | + _to_int(_enum("QtCore.Qt.AlignmentFlag").AlignVCenter))) + message_label.setSizePolicy(QtWidgets.QSizePolicy( + _enum("QtWidgets.QSizePolicy.Policy").Expanding, + _enum("QtWidgets.QSizePolicy.Policy").Ignored, + )) + self._message_action = self.addWidget(message_label) + self._toolitems = {} + self._groups = {} + + def add_toolitem( + self, name, group, position, image_file, description, toggle): + + button = QtWidgets.QToolButton(self) + if image_file: + button.setIcon(NavigationToolbar2QT._icon(self, image_file)) + button.setText(name) + if description: + button.setToolTip(description) + + def handler(): + self.trigger_tool(name) + if toggle: + button.setCheckable(True) + button.toggled.connect(handler) + else: + button.clicked.connect(handler) + + self._toolitems.setdefault(name, []) + self._add_to_group(group, name, button, position) + self._toolitems[name].append((button, handler)) + + def _add_to_group(self, group, name, button, position): + gr = self._groups.get(group, []) + if not gr: + sep = self.insertSeparator(self._message_action) + gr.append(sep) + before = gr[position] + widget = self.insertWidget(before, button) + gr.insert(position, widget) + self._groups[group] = gr + + def toggle_toolitem(self, name, toggled): + if name not in self._toolitems: + return + for button, handler in self._toolitems[name]: + button.toggled.disconnect(handler) + button.setChecked(toggled) + button.toggled.connect(handler) + + def remove_toolitem(self, name): + for button, handler in self._toolitems[name]: + button.setParent(None) + del self._toolitems[name] + + def set_message(self, s): + self.widgetForAction(self._message_action).setText(s) + + +@backend_tools._register_tool_class(FigureCanvasQT) +class ConfigureSubplotsQt(backend_tools.ConfigureSubplotsBase): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._subplot_dialog = None + + def trigger(self, *args): + NavigationToolbar2QT.configure_subplots(self) + + +@backend_tools._register_tool_class(FigureCanvasQT) +class SaveFigureQt(backend_tools.SaveFigureBase): + def trigger(self, *args): + NavigationToolbar2QT.save_figure( + self._make_classic_style_pseudo_toolbar()) + + +@_api.deprecated("3.5", alternative="ToolSetCursor") +class SetCursorQt(backend_tools.SetCursorBase): + def set_cursor(self, cursor): + NavigationToolbar2QT.set_cursor( + self._make_classic_style_pseudo_toolbar(), cursor) + + +@backend_tools._register_tool_class(FigureCanvasQT) +class RubberbandQt(backend_tools.RubberbandBase): + def draw_rubberband(self, x0, y0, x1, y1): + NavigationToolbar2QT.draw_rubberband( + self._make_classic_style_pseudo_toolbar(), None, x0, y0, x1, y1) + + def remove_rubberband(self): + NavigationToolbar2QT.remove_rubberband( + self._make_classic_style_pseudo_toolbar()) + + +@backend_tools._register_tool_class(FigureCanvasQT) +class HelpQt(backend_tools.ToolHelpBase): + def trigger(self, *args): + QtWidgets.QMessageBox.information(None, "Help", self._get_help_html()) + + +@backend_tools._register_tool_class(FigureCanvasQT) +class ToolCopyToClipboardQT(backend_tools.ToolCopyToClipboardBase): + def trigger(self, *args, **kwargs): + pixmap = self.canvas.grab() + QtWidgets.QApplication.instance().clipboard().setPixmap(pixmap) + + +FigureManagerQT._toolbar2_class = NavigationToolbar2QT +FigureManagerQT._toolmanager_toolbar_class = ToolbarQt + + +@_Backend.export +class _BackendQT(_Backend): + backend_version = __version__ + FigureCanvas = FigureCanvasQT + FigureManager = FigureManagerQT + + @staticmethod + def mainloop(): + qapp = QtWidgets.QApplication.instance() + with _maybe_allow_interrupt(qapp): + qt_compat._exec(qapp) diff --git a/lib/matplotlib/backends/backend_qt4.py b/lib/matplotlib/backends/backend_qt4.py deleted file mode 100644 index 9443bff90557..000000000000 --- a/lib/matplotlib/backends/backend_qt4.py +++ /dev/null @@ -1,15 +0,0 @@ -from .. import _api -from .backend_qt5 import ( - backend_version, SPECIAL_KEYS, - SUPER, ALT, CTRL, SHIFT, MODIFIER_KEYS, # These are deprecated. - cursord, _create_qApp, _BackendQT5, TimerQT, MainWindow, FigureCanvasQT, - FigureManagerQT, NavigationToolbar2QT, SubplotToolQt, exception_handler) - - -_api.warn_deprecated("3.3", name=__name__, obj_type="backend") - - -@_BackendQT5.export -class _BackendQT4(_BackendQT5): - class FigureCanvas(FigureCanvasQT): - required_interactive_framework = "qt4" diff --git a/lib/matplotlib/backends/backend_qt4agg.py b/lib/matplotlib/backends/backend_qt4agg.py deleted file mode 100644 index 9f028c477643..000000000000 --- a/lib/matplotlib/backends/backend_qt4agg.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -Render to qt from agg -""" - -from .. import _api -from .backend_qt5agg import ( - _BackendQT5Agg, FigureCanvasQTAgg, FigureManagerQT, NavigationToolbar2QT) - - -_api.warn_deprecated("3.3", name=__name__, obj_type="backend") - - -@_BackendQT5Agg.export -class _BackendQT4Agg(_BackendQT5Agg): - class FigureCanvas(FigureCanvasQTAgg): - required_interactive_framework = "qt4" diff --git a/lib/matplotlib/backends/backend_qt4cairo.py b/lib/matplotlib/backends/backend_qt4cairo.py deleted file mode 100644 index 650b8166a6d1..000000000000 --- a/lib/matplotlib/backends/backend_qt4cairo.py +++ /dev/null @@ -1,11 +0,0 @@ -from .. import _api -from .backend_qt5cairo import _BackendQT5Cairo, FigureCanvasQTCairo - - -_api.warn_deprecated("3.3", name=__name__, obj_type="backend") - - -@_BackendQT5Cairo.export -class _BackendQT4Cairo(_BackendQT5Cairo): - class FigureCanvas(FigureCanvasQTCairo): - required_interactive_framework = "qt4" diff --git a/lib/matplotlib/backends/backend_qt5.py b/lib/matplotlib/backends/backend_qt5.py index 0c2d32e1e8d1..b6f643a34aa9 100644 --- a/lib/matplotlib/backends/backend_qt5.py +++ b/lib/matplotlib/backends/backend_qt5.py @@ -1,1031 +1,28 @@ -import functools -import os -import signal -import sys -import traceback +from .. import backends -import matplotlib as mpl -from matplotlib import _api, backend_tools, cbook -from matplotlib._pylab_helpers import Gcf -from matplotlib.backend_bases import ( - _Backend, FigureCanvasBase, FigureManagerBase, NavigationToolbar2, - TimerBase, cursors, ToolContainerBase, StatusbarBase, MouseButton) -import matplotlib.backends.qt_editor.figureoptions as figureoptions -from matplotlib.backends.qt_editor._formsubplottool import UiSubplotTool -from . import qt_compat -from .qt_compat import ( - QtCore, QtGui, QtWidgets, __version__, QT_API, - _devicePixelRatioF, _isdeleted, _setDevicePixelRatio, -) - -backend_version = __version__ - -# SPECIAL_KEYS are keys that do *not* return their unicode name -# instead they have manually specified names -SPECIAL_KEYS = {QtCore.Qt.Key_Control: 'control', - QtCore.Qt.Key_Shift: 'shift', - QtCore.Qt.Key_Alt: 'alt', - QtCore.Qt.Key_Meta: 'meta', - QtCore.Qt.Key_Super_L: 'super', - QtCore.Qt.Key_Super_R: 'super', - QtCore.Qt.Key_CapsLock: 'caps_lock', - QtCore.Qt.Key_Return: 'enter', - QtCore.Qt.Key_Left: 'left', - QtCore.Qt.Key_Up: 'up', - QtCore.Qt.Key_Right: 'right', - QtCore.Qt.Key_Down: 'down', - QtCore.Qt.Key_Escape: 'escape', - QtCore.Qt.Key_F1: 'f1', - QtCore.Qt.Key_F2: 'f2', - QtCore.Qt.Key_F3: 'f3', - QtCore.Qt.Key_F4: 'f4', - QtCore.Qt.Key_F5: 'f5', - QtCore.Qt.Key_F6: 'f6', - QtCore.Qt.Key_F7: 'f7', - QtCore.Qt.Key_F8: 'f8', - QtCore.Qt.Key_F9: 'f9', - QtCore.Qt.Key_F10: 'f10', - QtCore.Qt.Key_F11: 'f11', - QtCore.Qt.Key_F12: 'f12', - QtCore.Qt.Key_Home: 'home', - QtCore.Qt.Key_End: 'end', - QtCore.Qt.Key_PageUp: 'pageup', - QtCore.Qt.Key_PageDown: 'pagedown', - QtCore.Qt.Key_Tab: 'tab', - QtCore.Qt.Key_Backspace: 'backspace', - QtCore.Qt.Key_Enter: 'enter', - QtCore.Qt.Key_Insert: 'insert', - QtCore.Qt.Key_Delete: 'delete', - QtCore.Qt.Key_Pause: 'pause', - QtCore.Qt.Key_SysReq: 'sysreq', - QtCore.Qt.Key_Clear: 'clear', } -if sys.platform == 'darwin': - # in OSX, the control and super (aka cmd/apple) keys are switched, so - # switch them back. - SPECIAL_KEYS.update({QtCore.Qt.Key_Control: 'cmd', # cmd/apple key - QtCore.Qt.Key_Meta: 'control', - }) -# Define which modifier keys are collected on keyboard events. -# Elements are (Modifier Flag, Qt Key) tuples. -# Order determines the modifier order (ctrl+alt+...) reported by Matplotlib. -_MODIFIER_KEYS = [ - (QtCore.Qt.ControlModifier, QtCore.Qt.Key_Control), - (QtCore.Qt.AltModifier, QtCore.Qt.Key_Alt), - (QtCore.Qt.ShiftModifier, QtCore.Qt.Key_Shift), - (QtCore.Qt.MetaModifier, QtCore.Qt.Key_Meta), -] -cursord = { - cursors.MOVE: QtCore.Qt.SizeAllCursor, - cursors.HAND: QtCore.Qt.PointingHandCursor, - cursors.POINTER: QtCore.Qt.ArrowCursor, - cursors.SELECT_REGION: QtCore.Qt.CrossCursor, - cursors.WAIT: QtCore.Qt.WaitCursor, - } -SUPER = 0 # Deprecated. -ALT = 1 # Deprecated. -CTRL = 2 # Deprecated. -SHIFT = 3 # Deprecated. -MODIFIER_KEYS = [ # Deprecated. - (SPECIAL_KEYS[key], mod, key) for mod, key in _MODIFIER_KEYS] - - -# make place holder -qApp = None - - -def _create_qApp(): - """ - Only one qApp can exist at a time, so check before creating one. - """ - global qApp - - if qApp is None: - app = QtWidgets.QApplication.instance() - if app is None: - # display_is_valid returns False only if on Linux and neither X11 - # nor Wayland display can be opened. - if not mpl._c_internal_utils.display_is_valid(): - raise RuntimeError('Invalid DISPLAY variable') - try: - QtWidgets.QApplication.setAttribute( - QtCore.Qt.AA_EnableHighDpiScaling) - except AttributeError: # Attribute only exists for Qt>=5.6. - pass - try: - QtWidgets.QApplication.setHighDpiScaleFactorRoundingPolicy( - QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough) - except AttributeError: # Added in Qt>=5.14. - pass - qApp = QtWidgets.QApplication(["matplotlib"]) - qApp.lastWindowClosed.connect(qApp.quit) - cbook._setup_new_guiapp() - else: - qApp = app - - try: - qApp.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps) - except AttributeError: - pass - - -def _allow_super_init(__init__): - """ - Decorator for ``__init__`` to allow ``super().__init__`` on PyQt4/PySide2. - """ - - if QT_API == "PyQt5": - - return __init__ - - else: - # To work around lack of cooperative inheritance in PyQt4, PySide, - # and PySide2, when calling FigureCanvasQT.__init__, we temporarily - # patch QWidget.__init__ by a cooperative version, that first calls - # QWidget.__init__ with no additional arguments, and then finds the - # next class in the MRO with an __init__ that does support cooperative - # inheritance (i.e., not defined by the PyQt4, PySide, PySide2, sip - # or Shiboken packages), and manually call its `__init__`, once again - # passing the additional arguments. - - qwidget_init = QtWidgets.QWidget.__init__ - - def cooperative_qwidget_init(self, *args, **kwargs): - qwidget_init(self) - mro = type(self).__mro__ - next_coop_init = next( - cls for cls in mro[mro.index(QtWidgets.QWidget) + 1:] - if cls.__module__.split(".")[0] not in [ - "PyQt4", "sip", "PySide", "PySide2", "Shiboken"]) - next_coop_init.__init__(self, *args, **kwargs) - - @functools.wraps(__init__) - def wrapper(self, *args, **kwargs): - with cbook._setattr_cm(QtWidgets.QWidget, - __init__=cooperative_qwidget_init): - __init__(self, *args, **kwargs) - - return wrapper - - -class TimerQT(TimerBase): - """Subclass of `.TimerBase` using QTimer events.""" - - def __init__(self, *args, **kwargs): - # Create a new timer and connect the timeout() signal to the - # _on_timer method. - self._timer = QtCore.QTimer() - self._timer.timeout.connect(self._on_timer) - super().__init__(*args, **kwargs) - - def __del__(self): - # The check for deletedness is needed to avoid an error at animation - # shutdown with PySide2. - if not _isdeleted(self._timer): - self._timer_stop() - - def _timer_set_single_shot(self): - self._timer.setSingleShot(self._single) - - def _timer_set_interval(self): - self._timer.setInterval(self._interval) - - def _timer_start(self): - self._timer.start() - - def _timer_stop(self): - self._timer.stop() - - -class FigureCanvasQT(QtWidgets.QWidget, FigureCanvasBase): - required_interactive_framework = "qt5" - _timer_cls = TimerQT - - # map Qt button codes to MouseEvent's ones: - buttond = {QtCore.Qt.LeftButton: MouseButton.LEFT, - QtCore.Qt.MidButton: MouseButton.MIDDLE, - QtCore.Qt.RightButton: MouseButton.RIGHT, - QtCore.Qt.XButton1: MouseButton.BACK, - QtCore.Qt.XButton2: MouseButton.FORWARD, - } - - @_allow_super_init - def __init__(self, figure=None): - _create_qApp() - super().__init__(figure=figure) - - # We don't want to scale up the figure DPI more than once. - # Note, we don't handle a signal for changing DPI yet. - self.figure._original_dpi = self.figure.dpi - self._update_figure_dpi() - # In cases with mixed resolution displays, we need to be careful if the - # dpi_ratio changes - in this case we need to resize the canvas - # accordingly. - self._dpi_ratio_prev = self._dpi_ratio - - self._draw_pending = False - self._is_drawing = False - self._draw_rect_callback = lambda painter: None - - self.setAttribute(QtCore.Qt.WA_OpaquePaintEvent) - self.setMouseTracking(True) - self.resize(*self.get_width_height()) - - palette = QtGui.QPalette(QtCore.Qt.white) - self.setPalette(palette) - - def _update_figure_dpi(self): - dpi = self._dpi_ratio * self.figure._original_dpi - self.figure._set_dpi(dpi, forward=False) - - @property - def _dpi_ratio(self): - return _devicePixelRatioF(self) - - def _update_pixel_ratio(self): - # We need to be careful in cases with mixed resolution displays if - # dpi_ratio changes. - if self._dpi_ratio != self._dpi_ratio_prev: - # We need to update the figure DPI. - self._update_figure_dpi() - self._dpi_ratio_prev = self._dpi_ratio - # The easiest way to resize the canvas is to emit a resizeEvent - # since we implement all the logic for resizing the canvas for - # that event. - event = QtGui.QResizeEvent(self.size(), self.size()) - self.resizeEvent(event) - # resizeEvent triggers a paintEvent itself, so we exit this one - # (after making sure that the event is immediately handled). - - def _update_screen(self, screen): - # Handler for changes to a window's attached screen. - self._update_pixel_ratio() - if screen is not None: - screen.physicalDotsPerInchChanged.connect(self._update_pixel_ratio) - screen.logicalDotsPerInchChanged.connect(self._update_pixel_ratio) - - def showEvent(self, event): - # Set up correct pixel ratio, and connect to any signal changes for it, - # once the window is shown (and thus has these attributes). - window = self.window().windowHandle() - window.screenChanged.connect(self._update_screen) - self._update_screen(window.screen()) - - def get_width_height(self): - w, h = FigureCanvasBase.get_width_height(self) - return int(w / self._dpi_ratio), int(h / self._dpi_ratio) - - def enterEvent(self, event): - try: - x, y = self.mouseEventCoords(event.pos()) - except AttributeError: - # the event from PyQt4 does not include the position - x = y = None - FigureCanvasBase.enter_notify_event(self, guiEvent=event, xy=(x, y)) - - def leaveEvent(self, event): - QtWidgets.QApplication.restoreOverrideCursor() - FigureCanvasBase.leave_notify_event(self, guiEvent=event) - - def mouseEventCoords(self, pos): - """ - Calculate mouse coordinates in physical pixels. - - Qt5 use logical pixels, but the figure is scaled to physical - pixels for rendering. Transform to physical pixels so that - all of the down-stream transforms work as expected. - - Also, the origin is different and needs to be corrected. - """ - dpi_ratio = self._dpi_ratio - x = pos.x() - # flip y so y=0 is bottom of canvas - y = self.figure.bbox.height / dpi_ratio - pos.y() - return x * dpi_ratio, y * dpi_ratio - - def mousePressEvent(self, event): - x, y = self.mouseEventCoords(event.pos()) - button = self.buttond.get(event.button()) - if button is not None: - FigureCanvasBase.button_press_event(self, x, y, button, - guiEvent=event) - - def mouseDoubleClickEvent(self, event): - x, y = self.mouseEventCoords(event.pos()) - button = self.buttond.get(event.button()) - if button is not None: - FigureCanvasBase.button_press_event(self, x, y, - button, dblclick=True, - guiEvent=event) - - def mouseMoveEvent(self, event): - x, y = self.mouseEventCoords(event) - FigureCanvasBase.motion_notify_event(self, x, y, guiEvent=event) - - def mouseReleaseEvent(self, event): - x, y = self.mouseEventCoords(event) - button = self.buttond.get(event.button()) - if button is not None: - FigureCanvasBase.button_release_event(self, x, y, button, - guiEvent=event) - - if QtCore.qVersion() >= "5.": - def wheelEvent(self, event): - x, y = self.mouseEventCoords(event) - # from QWheelEvent::delta doc - if event.pixelDelta().x() == 0 and event.pixelDelta().y() == 0: - steps = event.angleDelta().y() / 120 - else: - steps = event.pixelDelta().y() - if steps: - FigureCanvasBase.scroll_event( - self, x, y, steps, guiEvent=event) - else: - def wheelEvent(self, event): - x = event.x() - # flipy so y=0 is bottom of canvas - y = self.figure.bbox.height - event.y() - # from QWheelEvent::delta doc - steps = event.delta() / 120 - if event.orientation() == QtCore.Qt.Vertical: - FigureCanvasBase.scroll_event( - self, x, y, steps, guiEvent=event) - - def keyPressEvent(self, event): - key = self._get_key(event) - if key is not None: - FigureCanvasBase.key_press_event(self, key, guiEvent=event) - - def keyReleaseEvent(self, event): - key = self._get_key(event) - if key is not None: - FigureCanvasBase.key_release_event(self, key, guiEvent=event) - - def resizeEvent(self, event): - w = event.size().width() * self._dpi_ratio - h = event.size().height() * self._dpi_ratio - dpival = self.figure.dpi - winch = w / dpival - hinch = h / dpival - self.figure.set_size_inches(winch, hinch, forward=False) - # pass back into Qt to let it finish - QtWidgets.QWidget.resizeEvent(self, event) - # emit our resize events - FigureCanvasBase.resize_event(self) - - def sizeHint(self): - w, h = self.get_width_height() - return QtCore.QSize(w, h) - - def minumumSizeHint(self): - return QtCore.QSize(10, 10) - - def _get_key(self, event): - event_key = event.key() - event_mods = int(event.modifiers()) # actually a bitmask - - # get names of the pressed modifier keys - # 'control' is named 'control' when a standalone key, but 'ctrl' when a - # modifier - # bit twiddling to pick out modifier keys from event_mods bitmask, - # if event_key is a MODIFIER, it should not be duplicated in mods - mods = [SPECIAL_KEYS[key].replace('control', 'ctrl') - for mod, key in _MODIFIER_KEYS - if event_key != key and event_mods & mod] - try: - # for certain keys (enter, left, backspace, etc) use a word for the - # key, rather than unicode - key = SPECIAL_KEYS[event_key] - except KeyError: - # unicode defines code points up to 0x10ffff (sys.maxunicode) - # QT will use Key_Codes larger than that for keyboard keys that are - # are not unicode characters (like multimedia keys) - # skip these - # if you really want them, you should add them to SPECIAL_KEYS - if event_key > sys.maxunicode: - return None - - key = chr(event_key) - # qt delivers capitalized letters. fix capitalization - # note that capslock is ignored - if 'shift' in mods: - mods.remove('shift') - else: - key = key.lower() - - return '+'.join(mods + [key]) - - def flush_events(self): - # docstring inherited - qApp.processEvents() - - def start_event_loop(self, timeout=0): - # docstring inherited - if hasattr(self, "_event_loop") and self._event_loop.isRunning(): - raise RuntimeError("Event loop already running") - self._event_loop = event_loop = QtCore.QEventLoop() - if timeout > 0: - timer = QtCore.QTimer.singleShot(int(timeout * 1000), - event_loop.quit) - event_loop.exec_() - - def stop_event_loop(self, event=None): - # docstring inherited - if hasattr(self, "_event_loop"): - self._event_loop.quit() - - def draw(self): - """Render the figure, and queue a request for a Qt draw.""" - # The renderer draw is done here; delaying causes problems with code - # that uses the result of the draw() to update plot elements. - if self._is_drawing: - return - with cbook._setattr_cm(self, _is_drawing=True): - super().draw() - self.update() - - def draw_idle(self): - """Queue redraw of the Agg buffer and request Qt paintEvent.""" - # The Agg draw needs to be handled by the same thread Matplotlib - # modifies the scene graph from. Post Agg draw request to the - # current event loop in order to ensure thread affinity and to - # accumulate multiple draw requests from event handling. - # TODO: queued signal connection might be safer than singleShot - if not (getattr(self, '_draw_pending', False) or - getattr(self, '_is_drawing', False)): - self._draw_pending = True - QtCore.QTimer.singleShot(0, self._draw_idle) - - def blit(self, bbox=None): - # docstring inherited - if bbox is None and self.figure: - bbox = self.figure.bbox # Blit the entire canvas if bbox is None. - # repaint uses logical pixels, not physical pixels like the renderer. - l, b, w, h = [int(pt / self._dpi_ratio) for pt in bbox.bounds] - t = b + h - self.repaint(l, self.rect().height() - t, w, h) - - def _draw_idle(self): - with self._idle_draw_cntx(): - if not self._draw_pending: - return - self._draw_pending = False - if self.height() < 0 or self.width() < 0: - return - try: - self.draw() - except Exception: - # Uncaught exceptions are fatal for PyQt5, so catch them. - traceback.print_exc() - - def drawRectangle(self, rect): - # Draw the zoom rectangle to the QPainter. _draw_rect_callback needs - # to be called at the end of paintEvent. - if rect is not None: - x0, y0, w, h = [int(pt / self._dpi_ratio) for pt in rect] - x1 = x0 + w - y1 = y0 + h - def _draw_rect_callback(painter): - pen = QtGui.QPen(QtCore.Qt.black, 1 / self._dpi_ratio) - pen.setDashPattern([3, 3]) - for color, offset in [ - (QtCore.Qt.black, 0), (QtCore.Qt.white, 3)]: - pen.setDashOffset(offset) - pen.setColor(color) - painter.setPen(pen) - # Draw the lines from x0, y0 towards x1, y1 so that the - # dashes don't "jump" when moving the zoom box. - painter.drawLine(x0, y0, x0, y1) - painter.drawLine(x0, y0, x1, y0) - painter.drawLine(x0, y1, x1, y1) - painter.drawLine(x1, y0, x1, y1) - else: - def _draw_rect_callback(painter): - return - self._draw_rect_callback = _draw_rect_callback - self.update() - - -class MainWindow(QtWidgets.QMainWindow): - closing = QtCore.Signal() - - def closeEvent(self, event): - self.closing.emit() - super().closeEvent(event) - - -class FigureManagerQT(FigureManagerBase): - """ - Attributes - ---------- - canvas : `FigureCanvas` - The FigureCanvas instance - num : int or str - The Figure number - toolbar : qt.QToolBar - The qt.QToolBar - window : qt.QMainWindow - The qt.QMainWindow - """ - - def __init__(self, canvas, num): - self.window = MainWindow() - super().__init__(canvas, num) - self.window.closing.connect(canvas.close_event) - self.window.closing.connect(self._widgetclosed) - - image = str(cbook._get_data_path('images/matplotlib.svg')) - self.window.setWindowIcon(QtGui.QIcon(image)) - - self.window._destroying = False - - self.toolbar = self._get_toolbar(self.canvas, self.window) - - if self.toolmanager: - backend_tools.add_tools_to_manager(self.toolmanager) - if self.toolbar: - backend_tools.add_tools_to_container(self.toolbar) +backends._QT_FORCE_QT5_BINDING = True - if self.toolbar: - self.window.addToolBar(self.toolbar) - tbs_height = self.toolbar.sizeHint().height() - else: - tbs_height = 0 - - # resize the main window so it will display the canvas with the - # requested size: - cs = canvas.sizeHint() - cs_height = cs.height() - height = cs_height + tbs_height - self.window.resize(cs.width(), height) - - self.window.setCentralWidget(self.canvas) - - if mpl.is_interactive(): - self.window.show() - self.canvas.draw_idle() - - # Give the keyboard focus to the figure instead of the manager: - # StrongFocus accepts both tab and click to focus and will enable the - # canvas to process event without clicking. - # https://doc.qt.io/qt-5/qt.html#FocusPolicy-enum - self.canvas.setFocusPolicy(QtCore.Qt.StrongFocus) - self.canvas.setFocus() - - self.window.raise_() - - def full_screen_toggle(self): - if self.window.isFullScreen(): - self.window.showNormal() - else: - self.window.showFullScreen() - - def _widgetclosed(self): - if self.window._destroying: - return - self.window._destroying = True - try: - Gcf.destroy(self) - except AttributeError: - pass - # It seems that when the python session is killed, - # Gcf can get destroyed before the Gcf.destroy - # line is run, leading to a useless AttributeError. - - def _get_toolbar(self, canvas, parent): - # must be inited after the window, drawingArea and figure - # attrs are set - if mpl.rcParams['toolbar'] == 'toolbar2': - toolbar = NavigationToolbar2QT(canvas, parent, True) - elif mpl.rcParams['toolbar'] == 'toolmanager': - toolbar = ToolbarQt(self.toolmanager, self.window) - else: - toolbar = None - return toolbar - - def resize(self, width, height): - # these are Qt methods so they return sizes in 'virtual' pixels - # so we do not need to worry about dpi scaling here. - extra_width = self.window.width() - self.canvas.width() - extra_height = self.window.height() - self.canvas.height() - self.canvas.resize(width, height) - self.window.resize(width + extra_width, height + extra_height) - - def show(self): - self.window.show() - if mpl.rcParams['figure.raise_window']: - self.window.activateWindow() - self.window.raise_() - - def destroy(self, *args): - # check for qApp first, as PySide deletes it in its atexit handler - if QtWidgets.QApplication.instance() is None: - return - if self.window._destroying: - return - self.window._destroying = True - if self.toolbar: - self.toolbar.destroy() - self.window.close() - - def get_window_title(self): - return self.window.windowTitle() - - def set_window_title(self, title): - self.window.setWindowTitle(title) - - -class NavigationToolbar2QT(NavigationToolbar2, QtWidgets.QToolBar): - message = QtCore.Signal(str) - - toolitems = [*NavigationToolbar2.toolitems] - toolitems.insert( - # Add 'customize' action after 'subplots' - [name for name, *_ in toolitems].index("Subplots") + 1, - ("Customize", "Edit axis, curve and image parameters", - "qt4_editor_options", "edit_parameters")) - - def __init__(self, canvas, parent, coordinates=True): - """coordinates: should we show the coordinates on the right?""" - QtWidgets.QToolBar.__init__(self, parent) - self.setAllowedAreas( - QtCore.Qt.TopToolBarArea | QtCore.Qt.BottomToolBarArea) - - self.coordinates = coordinates - self._actions = {} # mapping of toolitem method names to QActions. - - for text, tooltip_text, image_file, callback in self.toolitems: - if text is None: - self.addSeparator() - else: - a = self.addAction(self._icon(image_file + '.png'), - text, getattr(self, callback)) - self._actions[callback] = a - if callback in ['zoom', 'pan']: - a.setCheckable(True) - if tooltip_text is not None: - a.setToolTip(tooltip_text) - - # Add the (x, y) location widget at the right side of the toolbar - # The stretch factor is 1 which means any resizing of the toolbar - # will resize this label instead of the buttons. - if self.coordinates: - self.locLabel = QtWidgets.QLabel("", self) - self.locLabel.setAlignment( - QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) - self.locLabel.setSizePolicy( - QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, - QtWidgets.QSizePolicy.Ignored)) - labelAction = self.addWidget(self.locLabel) - labelAction.setVisible(True) - - NavigationToolbar2.__init__(self, canvas) - - @_api.deprecated("3.3", alternative="self.canvas.parent()") - @property - def parent(self): - return self.canvas.parent() - - @_api.deprecated("3.3", alternative="self.canvas.setParent()") - @parent.setter - def parent(self, value): - pass - - @_api.deprecated( - "3.3", alternative="os.path.join(mpl.get_data_path(), 'images')") - @property - def basedir(self): - return str(cbook._get_data_path('images')) - - def _icon(self, name): - """ - Construct a `.QIcon` from an image file *name*, including the extension - and relative to Matplotlib's "images" data directory. - """ - if QtCore.qVersion() >= '5.': - name = name.replace('.png', '_large.png') - pm = QtGui.QPixmap(str(cbook._get_data_path('images', name))) - _setDevicePixelRatio(pm, _devicePixelRatioF(self)) - if self.palette().color(self.backgroundRole()).value() < 128: - icon_color = self.palette().color(self.foregroundRole()) - mask = pm.createMaskFromColor(QtGui.QColor('black'), - QtCore.Qt.MaskOutColor) - pm.fill(icon_color) - pm.setMask(mask) - return QtGui.QIcon(pm) - - def edit_parameters(self): - axes = self.canvas.figure.get_axes() - if not axes: - QtWidgets.QMessageBox.warning( - self.canvas.parent(), "Error", "There are no axes to edit.") - return - elif len(axes) == 1: - ax, = axes - else: - titles = [ - ax.get_label() or - ax.get_title() or - " - ".join(filter(None, [ax.get_xlabel(), ax.get_ylabel()])) or - f"" - for ax in axes] - duplicate_titles = [ - title for title in titles if titles.count(title) > 1] - for i, ax in enumerate(axes): - if titles[i] in duplicate_titles: - titles[i] += f" (id: {id(ax):#x})" # Deduplicate titles. - item, ok = QtWidgets.QInputDialog.getItem( - self.canvas.parent(), - 'Customize', 'Select axes:', titles, 0, False) - if not ok: - return - ax = axes[titles.index(item)] - figureoptions.figure_edit(ax, self) - - def _update_buttons_checked(self): - # sync button checkstates to match active mode - if 'pan' in self._actions: - self._actions['pan'].setChecked(self.mode.name == 'PAN') - if 'zoom' in self._actions: - self._actions['zoom'].setChecked(self.mode.name == 'ZOOM') - - def pan(self, *args): - super().pan(*args) - self._update_buttons_checked() - - def zoom(self, *args): - super().zoom(*args) - self._update_buttons_checked() - - def set_message(self, s): - self.message.emit(s) - if self.coordinates: - self.locLabel.setText(s) - - def set_cursor(self, cursor): - self.canvas.setCursor(cursord[cursor]) - - def draw_rubberband(self, event, x0, y0, x1, y1): - height = self.canvas.figure.bbox.height - y1 = height - y1 - y0 = height - y0 - rect = [int(val) for val in (x0, y0, x1 - x0, y1 - y0)] - self.canvas.drawRectangle(rect) - - def remove_rubberband(self): - self.canvas.drawRectangle(None) - - def configure_subplots(self): - image = str(cbook._get_data_path('images/matplotlib.png')) - dia = SubplotToolQt(self.canvas.figure, self.canvas.parent()) - dia.setWindowIcon(QtGui.QIcon(image)) - dia.exec_() - - def save_figure(self, *args): - filetypes = self.canvas.get_supported_filetypes_grouped() - sorted_filetypes = sorted(filetypes.items()) - default_filetype = self.canvas.get_default_filetype() - - startpath = os.path.expanduser(mpl.rcParams['savefig.directory']) - start = os.path.join(startpath, self.canvas.get_default_filename()) - filters = [] - selectedFilter = None - for name, exts in sorted_filetypes: - exts_list = " ".join(['*.%s' % ext for ext in exts]) - filter = '%s (%s)' % (name, exts_list) - if default_filetype in exts: - selectedFilter = filter - filters.append(filter) - filters = ';;'.join(filters) - - fname, filter = qt_compat._getSaveFileName( - self.canvas.parent(), "Choose a filename to save to", start, - filters, selectedFilter) - if fname: - # Save dir for next time, unless empty str (i.e., use cwd). - if startpath != "": - mpl.rcParams['savefig.directory'] = os.path.dirname(fname) - try: - self.canvas.figure.savefig(fname) - except Exception as e: - QtWidgets.QMessageBox.critical( - self, "Error saving file", str(e), - QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.NoButton) - - def set_history_buttons(self): - can_backward = self._nav_stack._pos > 0 - can_forward = self._nav_stack._pos < len(self._nav_stack._elements) - 1 - if 'back' in self._actions: - self._actions['back'].setEnabled(can_backward) - if 'forward' in self._actions: - self._actions['forward'].setEnabled(can_forward) - - -class SubplotToolQt(UiSubplotTool): - def __init__(self, targetfig, parent): - super().__init__(None) - - self._figure = targetfig - - for lower, higher in [("bottom", "top"), ("left", "right")]: - self._widgets[lower].valueChanged.connect( - lambda val: self._widgets[higher].setMinimum(val + .001)) - self._widgets[higher].valueChanged.connect( - lambda val: self._widgets[lower].setMaximum(val - .001)) - - self._attrs = ["top", "bottom", "left", "right", "hspace", "wspace"] - self._defaults = {attr: vars(self._figure.subplotpars)[attr] - for attr in self._attrs} - - # Set values after setting the range callbacks, but before setting up - # the redraw callbacks. - self._reset() - - for attr in self._attrs: - self._widgets[attr].valueChanged.connect(self._on_value_changed) - for action, method in [("Export values", self._export_values), - ("Tight layout", self._tight_layout), - ("Reset", self._reset), - ("Close", self.close)]: - self._widgets[action].clicked.connect(method) - - def _export_values(self): - # Explicitly round to 3 decimals (which is also the spinbox precision) - # to avoid numbers of the form 0.100...001. - dialog = QtWidgets.QDialog() - layout = QtWidgets.QVBoxLayout() - dialog.setLayout(layout) - text = QtWidgets.QPlainTextEdit() - text.setReadOnly(True) - layout.addWidget(text) - text.setPlainText( - ",\n".join("{}={:.3}".format(attr, self._widgets[attr].value()) - for attr in self._attrs)) - # Adjust the height of the text widget to fit the whole text, plus - # some padding. - size = text.maximumSize() - size.setHeight( - QtGui.QFontMetrics(text.document().defaultFont()) - .size(0, text.toPlainText()).height() + 20) - text.setMaximumSize(size) - dialog.exec_() - - def _on_value_changed(self): - self._figure.subplots_adjust(**{attr: self._widgets[attr].value() - for attr in self._attrs}) - self._figure.canvas.draw_idle() - - def _tight_layout(self): - self._figure.tight_layout() - for attr in self._attrs: - widget = self._widgets[attr] - widget.blockSignals(True) - widget.setValue(vars(self._figure.subplotpars)[attr]) - widget.blockSignals(False) - self._figure.canvas.draw_idle() - - def _reset(self): - for attr, value in self._defaults.items(): - self._widgets[attr].setValue(value) - - -class ToolbarQt(ToolContainerBase, QtWidgets.QToolBar): - def __init__(self, toolmanager, parent): - ToolContainerBase.__init__(self, toolmanager) - QtWidgets.QToolBar.__init__(self, parent) - self.setAllowedAreas( - QtCore.Qt.TopToolBarArea | QtCore.Qt.BottomToolBarArea) - message_label = QtWidgets.QLabel("") - message_label.setAlignment( - QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) - message_label.setSizePolicy( - QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, - QtWidgets.QSizePolicy.Ignored)) - self._message_action = self.addWidget(message_label) - self._toolitems = {} - self._groups = {} - - def add_toolitem( - self, name, group, position, image_file, description, toggle): - - button = QtWidgets.QToolButton(self) - if image_file: - button.setIcon(NavigationToolbar2QT._icon(self, image_file)) - button.setText(name) - if description: - button.setToolTip(description) - - def handler(): - self.trigger_tool(name) - if toggle: - button.setCheckable(True) - button.toggled.connect(handler) - else: - button.clicked.connect(handler) - - self._toolitems.setdefault(name, []) - self._add_to_group(group, name, button, position) - self._toolitems[name].append((button, handler)) - - def _add_to_group(self, group, name, button, position): - gr = self._groups.get(group, []) - if not gr: - sep = self.insertSeparator(self._message_action) - gr.append(sep) - before = gr[position] - widget = self.insertWidget(before, button) - gr.insert(position, widget) - self._groups[group] = gr - - def toggle_toolitem(self, name, toggled): - if name not in self._toolitems: - return - for button, handler in self._toolitems[name]: - button.toggled.disconnect(handler) - button.setChecked(toggled) - button.toggled.connect(handler) - - def remove_toolitem(self, name): - for button, handler in self._toolitems[name]: - button.setParent(None) - del self._toolitems[name] - - def set_message(self, s): - self.widgetForAction(self._message_action).setText(s) - - -@_api.deprecated("3.3") -class StatusbarQt(StatusbarBase, QtWidgets.QLabel): - def __init__(self, window, *args, **kwargs): - StatusbarBase.__init__(self, *args, **kwargs) - QtWidgets.QLabel.__init__(self) - window.statusBar().addWidget(self) - - def set_message(self, s): - self.setText(s) - - -class ConfigureSubplotsQt(backend_tools.ConfigureSubplotsBase): - def trigger(self, *args): - NavigationToolbar2QT.configure_subplots( - self._make_classic_style_pseudo_toolbar()) - - -class SaveFigureQt(backend_tools.SaveFigureBase): - def trigger(self, *args): - NavigationToolbar2QT.save_figure( - self._make_classic_style_pseudo_toolbar()) - - -class SetCursorQt(backend_tools.SetCursorBase): - def set_cursor(self, cursor): - NavigationToolbar2QT.set_cursor( - self._make_classic_style_pseudo_toolbar(), cursor) - - -class RubberbandQt(backend_tools.RubberbandBase): - def draw_rubberband(self, x0, y0, x1, y1): - NavigationToolbar2QT.draw_rubberband( - self._make_classic_style_pseudo_toolbar(), None, x0, y0, x1, y1) - - def remove_rubberband(self): - NavigationToolbar2QT.remove_rubberband( - self._make_classic_style_pseudo_toolbar()) - - -class HelpQt(backend_tools.ToolHelpBase): - def trigger(self, *args): - QtWidgets.QMessageBox.information(None, "Help", self._get_help_html()) - - -class ToolCopyToClipboardQT(backend_tools.ToolCopyToClipboardBase): - def trigger(self, *args, **kwargs): - pixmap = self.canvas.grab() - qApp.clipboard().setPixmap(pixmap) +from .backend_qt import ( # noqa + SPECIAL_KEYS, + # Public API + cursord, _create_qApp, _BackendQT, TimerQT, MainWindow, FigureCanvasQT, + FigureManagerQT, ToolbarQt, NavigationToolbar2QT, SubplotToolQt, + SaveFigureQt, ConfigureSubplotsQt, SetCursorQt, RubberbandQt, + HelpQt, ToolCopyToClipboardQT, + # internal re-exports + FigureCanvasBase, FigureManagerBase, MouseButton, NavigationToolbar2, + TimerBase, ToolContainerBase, figureoptions, Gcf +) +from . import backend_qt as _backend_qt # noqa -backend_tools.ToolSaveFigure = SaveFigureQt -backend_tools.ToolConfigureSubplots = ConfigureSubplotsQt -backend_tools.ToolSetCursor = SetCursorQt -backend_tools.ToolRubberband = RubberbandQt -backend_tools.ToolHelp = HelpQt -backend_tools.ToolCopyToClipboard = ToolCopyToClipboardQT +@_BackendQT.export +class _BackendQT5(_BackendQT): + pass -@_Backend.export -class _BackendQT5(_Backend): - FigureCanvas = FigureCanvasQT - FigureManager = FigureManagerQT - @staticmethod - def mainloop(): - old_signal = signal.getsignal(signal.SIGINT) - # allow SIGINT exceptions to close the plot window. - is_python_signal_handler = old_signal is not None - if is_python_signal_handler: - signal.signal(signal.SIGINT, signal.SIG_DFL) - try: - qApp.exec_() - finally: - # reset the SIGINT exception handler - if is_python_signal_handler: - signal.signal(signal.SIGINT, old_signal) +def __getattr__(name): + if name == 'qApp': + return _backend_qt.qApp + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/lib/matplotlib/backends/backend_qt5agg.py b/lib/matplotlib/backends/backend_qt5agg.py index 897c28c38f04..8a92fd5135d5 100644 --- a/lib/matplotlib/backends/backend_qt5agg.py +++ b/lib/matplotlib/backends/backend_qt5agg.py @@ -1,84 +1,14 @@ """ -Render to qt from agg. +Render to qt from agg """ +from .. import backends -import ctypes +backends._QT_FORCE_QT5_BINDING = True +from .backend_qtagg import ( # noqa: F401, E402 # pylint: disable=W0611 + _BackendQTAgg, FigureCanvasQTAgg, FigureManagerQT, NavigationToolbar2QT, + FigureCanvasAgg, FigureCanvasQT) -from matplotlib.transforms import Bbox -from .. import cbook -from .backend_agg import FigureCanvasAgg -from .backend_qt5 import ( - QtCore, QtGui, QtWidgets, _BackendQT5, FigureCanvasQT, FigureManagerQT, - NavigationToolbar2QT, backend_version) -from .qt_compat import QT_API, _setDevicePixelRatio - - -class FigureCanvasQTAgg(FigureCanvasAgg, FigureCanvasQT): - - def __init__(self, figure=None): - # Must pass 'figure' as kwarg to Qt base class. - super().__init__(figure=figure) - - def paintEvent(self, event): - """ - Copy the image from the Agg canvas to the qt.drawable. - - In Qt, all drawing should be done inside of here when a widget is - shown onscreen. - """ - self._draw_idle() # Only does something if a draw is pending. - - # If the canvas does not have a renderer, then give up and wait for - # FigureCanvasAgg.draw(self) to be called. - if not hasattr(self, 'renderer'): - return - - painter = QtGui.QPainter(self) - try: - # See documentation of QRect: bottom() and right() are off - # by 1, so use left() + width() and top() + height(). - rect = event.rect() - # scale rect dimensions using the screen dpi ratio to get - # correct values for the Figure coordinates (rather than - # QT5's coords) - width = rect.width() * self._dpi_ratio - height = rect.height() * self._dpi_ratio - left, top = self.mouseEventCoords(rect.topLeft()) - # shift the "top" by the height of the image to get the - # correct corner for our coordinate system - bottom = top - height - # same with the right side of the image - right = left + width - # create a buffer using the image bounding box - bbox = Bbox([[left, bottom], [right, top]]) - reg = self.copy_from_bbox(bbox) - buf = cbook._unmultiplied_rgba8888_to_premultiplied_argb32( - memoryview(reg)) - - # clear the widget canvas - painter.eraseRect(rect) - - qimage = QtGui.QImage(buf, buf.shape[1], buf.shape[0], - QtGui.QImage.Format_ARGB32_Premultiplied) - _setDevicePixelRatio(qimage, self._dpi_ratio) - # set origin using original QT coordinates - origin = QtCore.QPoint(rect.left(), rect.top()) - painter.drawImage(origin, qimage) - # Adjust the buf reference count to work around a memory - # leak bug in QImage under PySide on Python 3. - if QT_API in ('PySide', 'PySide2'): - ctypes.c_long.from_address(id(buf)).value = 1 - - self._draw_rect_callback(painter) - finally: - painter.end() - - def print_figure(self, *args, **kwargs): - super().print_figure(*args, **kwargs) - self.draw() - - -@_BackendQT5.export -class _BackendQT5Agg(_BackendQT5): - FigureCanvas = FigureCanvasQTAgg +@_BackendQTAgg.export +class _BackendQT5Agg(_BackendQTAgg): + pass diff --git a/lib/matplotlib/backends/backend_qt5cairo.py b/lib/matplotlib/backends/backend_qt5cairo.py index 4b6d7305e7c1..a4263f597119 100644 --- a/lib/matplotlib/backends/backend_qt5cairo.py +++ b/lib/matplotlib/backends/backend_qt5cairo.py @@ -1,45 +1,11 @@ -import ctypes +from .. import backends -from .backend_cairo import cairo, FigureCanvasCairo, RendererCairo -from .backend_qt5 import QtCore, QtGui, _BackendQT5, FigureCanvasQT -from .qt_compat import QT_API, _setDevicePixelRatio +backends._QT_FORCE_QT5_BINDING = True +from .backend_qtcairo import ( # noqa: F401, E402 # pylint: disable=W0611 + _BackendQTCairo, FigureCanvasQTCairo, FigureCanvasCairo, FigureCanvasQT +) -class FigureCanvasQTCairo(FigureCanvasQT, FigureCanvasCairo): - def __init__(self, figure=None): - super().__init__(figure=figure) - self._renderer = RendererCairo(self.figure.dpi) - self._renderer.set_width_height(-1, -1) # Invalid values. - - def draw(self): - if hasattr(self._renderer.gc, "ctx"): - self.figure.draw(self._renderer) - super().draw() - - def paintEvent(self, event): - dpi_ratio = self._dpi_ratio - width = int(dpi_ratio * self.width()) - height = int(dpi_ratio * self.height()) - if (width, height) != self._renderer.get_canvas_width_height(): - surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) - self._renderer.set_ctx_from_surface(surface) - self._renderer.set_width_height(width, height) - self.figure.draw(self._renderer) - buf = self._renderer.gc.ctx.get_target().get_data() - qimage = QtGui.QImage(buf, width, height, - QtGui.QImage.Format_ARGB32_Premultiplied) - # Adjust the buf reference count to work around a memory leak bug in - # QImage under PySide on Python 3. - if QT_API == 'PySide': - ctypes.c_long.from_address(id(buf)).value = 1 - _setDevicePixelRatio(qimage, dpi_ratio) - painter = QtGui.QPainter(self) - painter.eraseRect(event.rect()) - painter.drawImage(0, 0, qimage) - self._draw_rect_callback(painter) - painter.end() - - -@_BackendQT5.export -class _BackendQT5Cairo(_BackendQT5): - FigureCanvas = FigureCanvasQTCairo +@_BackendQTCairo.export +class _BackendQT5Cairo(_BackendQTCairo): + pass diff --git a/lib/matplotlib/backends/backend_qtagg.py b/lib/matplotlib/backends/backend_qtagg.py new file mode 100644 index 000000000000..dde185107e98 --- /dev/null +++ b/lib/matplotlib/backends/backend_qtagg.py @@ -0,0 +1,87 @@ +""" +Render to qt from agg. +""" + +import ctypes + +from matplotlib.transforms import Bbox + +from .qt_compat import QT_API, _enum, _setDevicePixelRatio +from .. import cbook +from .backend_agg import FigureCanvasAgg +from .backend_qt import QtCore, QtGui, _BackendQT, FigureCanvasQT +from .backend_qt import ( # noqa: F401 # pylint: disable=W0611 + FigureManagerQT, NavigationToolbar2QT) + + +class FigureCanvasQTAgg(FigureCanvasAgg, FigureCanvasQT): + + def paintEvent(self, event): + """ + Copy the image from the Agg canvas to the qt.drawable. + + In Qt, all drawing should be done inside of here when a widget is + shown onscreen. + """ + self._draw_idle() # Only does something if a draw is pending. + + # If the canvas does not have a renderer, then give up and wait for + # FigureCanvasAgg.draw(self) to be called. + if not hasattr(self, 'renderer'): + return + + painter = QtGui.QPainter(self) + try: + # See documentation of QRect: bottom() and right() are off + # by 1, so use left() + width() and top() + height(). + rect = event.rect() + # scale rect dimensions using the screen dpi ratio to get + # correct values for the Figure coordinates (rather than + # QT5's coords) + width = rect.width() * self.device_pixel_ratio + height = rect.height() * self.device_pixel_ratio + left, top = self.mouseEventCoords(rect.topLeft()) + # shift the "top" by the height of the image to get the + # correct corner for our coordinate system + bottom = top - height + # same with the right side of the image + right = left + width + # create a buffer using the image bounding box + bbox = Bbox([[left, bottom], [right, top]]) + reg = self.copy_from_bbox(bbox) + buf = cbook._unmultiplied_rgba8888_to_premultiplied_argb32( + memoryview(reg)) + + # clear the widget canvas + painter.eraseRect(rect) + + if QT_API == "PyQt6": + from PyQt6 import sip + ptr = int(sip.voidptr(buf)) + else: + ptr = buf + qimage = QtGui.QImage( + ptr, buf.shape[1], buf.shape[0], + _enum("QtGui.QImage.Format").Format_ARGB32_Premultiplied) + _setDevicePixelRatio(qimage, self.device_pixel_ratio) + # set origin using original QT coordinates + origin = QtCore.QPoint(rect.left(), rect.top()) + painter.drawImage(origin, qimage) + # Adjust the buf reference count to work around a memory + # leak bug in QImage under PySide. + if QT_API in ('PySide', 'PySide2'): + if QtCore.__version_info__ < (5, 12): + ctypes.c_long.from_address(id(buf)).value = 1 + + self._draw_rect_callback(painter) + finally: + painter.end() + + def print_figure(self, *args, **kwargs): + super().print_figure(*args, **kwargs) + self.draw() + + +@_BackendQT.export +class _BackendQTAgg(_BackendQT): + FigureCanvas = FigureCanvasQTAgg diff --git a/lib/matplotlib/backends/backend_qtcairo.py b/lib/matplotlib/backends/backend_qtcairo.py new file mode 100644 index 000000000000..f6064285df5b --- /dev/null +++ b/lib/matplotlib/backends/backend_qtcairo.py @@ -0,0 +1,47 @@ +import ctypes + +from .backend_cairo import cairo, FigureCanvasCairo +from .backend_qt import QtCore, QtGui, _BackendQT, FigureCanvasQT +from .qt_compat import QT_API, _enum, _setDevicePixelRatio + + +class FigureCanvasQTCairo(FigureCanvasCairo, FigureCanvasQT): + def draw(self): + if hasattr(self._renderer.gc, "ctx"): + self._renderer.dpi = self.figure.dpi + self.figure.draw(self._renderer) + super().draw() + + def paintEvent(self, event): + width = int(self.device_pixel_ratio * self.width()) + height = int(self.device_pixel_ratio * self.height()) + if (width, height) != self._renderer.get_canvas_width_height(): + surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) + self._renderer.set_context(cairo.Context(surface)) + self._renderer.dpi = self.figure.dpi + self.figure.draw(self._renderer) + buf = self._renderer.gc.ctx.get_target().get_data() + if QT_API == "PyQt6": + from PyQt6 import sip + ptr = int(sip.voidptr(buf)) + else: + ptr = buf + qimage = QtGui.QImage( + ptr, width, height, + _enum("QtGui.QImage.Format").Format_ARGB32_Premultiplied) + # Adjust the buf reference count to work around a memory leak bug in + # QImage under PySide. + if QT_API in ('PySide', 'PySide2'): + if QtCore.__version_info__ < (5, 12): + ctypes.c_long.from_address(id(buf)).value = 1 + _setDevicePixelRatio(qimage, self.device_pixel_ratio) + painter = QtGui.QPainter(self) + painter.eraseRect(event.rect()) + painter.drawImage(0, 0, qimage) + self._draw_rect_callback(painter) + painter.end() + + +@_BackendQT.export +class _BackendQTCairo(_BackendQT): + FigureCanvas = FigureCanvasQTCairo diff --git a/lib/matplotlib/backends/backend_svg.py b/lib/matplotlib/backends/backend_svg.py index 21a853693b1c..9a472899c193 100644 --- a/lib/matplotlib/backends/backend_svg.py +++ b/lib/matplotlib/backends/backend_svg.py @@ -1,9 +1,9 @@ -from collections import OrderedDict import base64 +import codecs import datetime import gzip import hashlib -from io import BytesIO, StringIO, TextIOWrapper +from io import BytesIO import itertools import logging import os @@ -14,23 +14,19 @@ from PIL import Image import matplotlib as mpl -from matplotlib import _api, cbook +from matplotlib import _api, cbook, font_manager as fm from matplotlib.backend_bases import ( - _Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase, - RendererBase, _no_output_draw) + _Backend, FigureCanvasBase, FigureManagerBase, RendererBase) from matplotlib.backends.backend_mixed import MixedModeRenderer from matplotlib.colors import rgb2hex from matplotlib.dates import UTC -from matplotlib.font_manager import findfont, get_font -from matplotlib.ft2font import LOAD_NO_HINTING -from matplotlib.mathtext import MathTextParser from matplotlib.path import Path from matplotlib import _path from matplotlib.transforms import Affine2D, Affine2DBase + _log = logging.getLogger(__name__) -backend_version = mpl.__version__ # ---------------------------------------------------------------------- # SimpleXMLWriter class @@ -70,7 +66,12 @@ # -------------------------------------------------------------------- +@_api.deprecated("3.6", alternative="a vendored copy of _escape_cdata") def escape_cdata(s): + return _escape_cdata(s) + + +def _escape_cdata(s): s = s.replace("&", "&") s = s.replace("<", "<") s = s.replace(">", ">") @@ -80,12 +81,22 @@ def escape_cdata(s): _escape_xml_comment = re.compile(r'-(?=-)') +@_api.deprecated("3.6", alternative="a vendored copy of _escape_comment") def escape_comment(s): - s = escape_cdata(s) + return _escape_comment.sub(s) + + +def _escape_comment(s): + s = _escape_cdata(s) return _escape_xml_comment.sub('- ', s) +@_api.deprecated("3.6", alternative="a vendored copy of _escape_attrib") def escape_attrib(s): + return _escape_attrib(s) + + +def _escape_attrib(s): s = s.replace("&", "&") s = s.replace("'", "'") s = s.replace('"', """) @@ -94,7 +105,18 @@ def escape_attrib(s): return s +def _quote_escape_attrib(s): + return ('"' + _escape_cdata(s) + '"' if '"' not in s else + "'" + _escape_cdata(s) + "'" if "'" not in s else + '"' + _escape_attrib(s) + '"') + + +@_api.deprecated("3.6", alternative="a vendored copy of _short_float_fmt") def short_float_fmt(x): + return _short_float_fmt(x) + + +def _short_float_fmt(x): """ Create a short string representation of a float, which is %f formatting with trailing zeros and the decimal point removed. @@ -128,7 +150,7 @@ def __flush(self, indent=True): self.__open = 0 if self.__data: data = ''.join(self.__data) - self.__write(escape_cdata(data)) + self.__write(_escape_cdata(data)) self.__data = [] def start(self, tag, attrib={}, **extra): @@ -151,16 +173,16 @@ def start(self, tag, attrib={}, **extra): An element identifier. """ self.__flush() - tag = escape_cdata(tag) + tag = _escape_cdata(tag) self.__data = [] self.__tags.append(tag) self.__write(self.__indentation[:len(self.__tags) - 1]) self.__write("<%s" % tag) - for k, v in sorted({**attrib, **extra}.items()): + for k, v in {**attrib, **extra}.items(): if v: - k = escape_cdata(k) - v = escape_attrib(v) - self.__write(' %s="%s"' % (k, v)) + k = _escape_cdata(k) + v = _quote_escape_attrib(v) + self.__write(' %s=%s' % (k, v)) self.__open = 1 return len(self.__tags) - 1 @@ -175,7 +197,7 @@ def comment(self, comment): """ self.__flush() self.__write(self.__indentation[:len(self.__tags)]) - self.__write("\n" % escape_comment(comment)) + self.__write("\n" % _escape_comment(comment)) def data(self, text): """ @@ -201,7 +223,7 @@ def end(self, tag=None, indent=True): """ if tag: assert self.__tags, "unbalanced end(%s)" % tag - assert escape_cdata(tag) == self.__tags[-1], \ + assert _escape_cdata(tag) == self.__tags[-1], \ "expected end(%s), got %s" % (self.__tags[-1], tag) else: assert self.__tags, "unbalanced end()" @@ -245,37 +267,54 @@ def flush(self): pass # replaced by the constructor -def generate_transform(transform_list=[]): - if len(transform_list): - output = StringIO() - for type, value in transform_list: - if (type == 'scale' and (value == (1,) or value == (1, 1)) - or type == 'translate' and value == (0, 0) - or type == 'rotate' and value == (0,)): - continue - if type == 'matrix' and isinstance(value, Affine2DBase): - value = value.to_values() - output.write('%s(%s)' % ( - type, ' '.join(short_float_fmt(x) for x in value))) - return output.getvalue() - return '' - - -def generate_css(attrib={}): - if attrib: - output = StringIO() - attrib = sorted(attrib.items()) - for k, v in attrib: - k = escape_attrib(k) - v = escape_attrib(v) - output.write("%s:%s;" % (k, v)) - return output.getvalue() - return '' +def _generate_transform(transform_list): + parts = [] + for type, value in transform_list: + if (type == 'scale' and (value == (1,) or value == (1, 1)) + or type == 'translate' and value == (0, 0) + or type == 'rotate' and value == (0,)): + continue + if type == 'matrix' and isinstance(value, Affine2DBase): + value = value.to_values() + parts.append('%s(%s)' % ( + type, ' '.join(_short_float_fmt(x) for x in value))) + return ' '.join(parts) + + +@_api.deprecated("3.6") +def generate_transform(transform_list=None): + return _generate_transform(transform_list or []) + + +def _generate_css(attrib): + return "; ".join(f"{k}: {v}" for k, v in attrib.items()) + + +@_api.deprecated("3.6") +def generate_css(attrib=None): + return _generate_css(attrib or {}) _capstyle_d = {'projecting': 'square', 'butt': 'butt', 'round': 'round'} +def _check_is_str(info, key): + if not isinstance(info, str): + raise TypeError(f'Invalid type for {key} metadata. Expected str, not ' + f'{type(info)}.') + + +def _check_is_iterable_of_str(infos, key): + if np.iterable(infos): + for info in infos: + if not isinstance(info, str): + raise TypeError(f'Invalid type for {key} metadata. Expected ' + f'iterable of str, not {type(info)}.') + else: + raise TypeError(f'Invalid type for {key} metadata. Expected str or ' + f'iterable of str, not {type(infos)}.') + + class RendererSVG(RendererBase): def __init__(self, width, height, svgwriter, basename=None, image_dpi=72, *, metadata=None): @@ -284,21 +323,25 @@ def __init__(self, width, height, svgwriter, basename=None, image_dpi=72, self.writer = XMLWriter(svgwriter) self.image_dpi = image_dpi # actual dpi at which we rasterize stuff - self._groupd = {} + if basename is None: + basename = getattr(svgwriter, "name", "") + if not isinstance(basename, str): + basename = "" self.basename = basename + + self._groupd = {} self._image_counter = itertools.count() - self._clipd = OrderedDict() + self._clipd = {} self._markers = {} self._path_collection_id = 0 - self._hatchd = OrderedDict() + self._hatchd = {} self._has_gouraud = False self._n_gradients = 0 - self._fonts = OrderedDict() super().__init__() self._glyph_map = dict() - str_height = short_float_fmt(height) - str_width = short_float_fmt(width) + str_height = _short_float_fmt(height) + str_width = _short_float_fmt(width) svgwriter.write(svgProlog) self._start_id = self.writer.start( 'svg', @@ -311,11 +354,6 @@ def __init__(self, width, height, svgwriter, basename=None, image_dpi=72, self._write_metadata(metadata) self._write_default_style() - @_api.deprecated("3.4") - @property - def mathtext_parser(self): - return MathTextParser('SVG') - def finalize(self): self._write_clips() self._write_hatches() @@ -338,7 +376,9 @@ def _write_metadata(self, metadata): writer = self.writer if 'Title' in metadata: - writer.element('title', text=metadata['Title']) + title = metadata['Title'] + _check_is_str(title, 'Title') + writer.element('title', text=title) # Special handling. date = metadata.get('Date', None) @@ -355,14 +395,14 @@ def _write_metadata(self, metadata): elif isinstance(d, (datetime.datetime, datetime.date)): dates.append(d.isoformat()) else: - raise ValueError( - 'Invalid type for Date metadata. ' - 'Expected iterable of str, date, or datetime, ' - 'not {!r}.'.format(type(d))) + raise TypeError( + f'Invalid type for Date metadata. ' + f'Expected iterable of str, date, or datetime, ' + f'not {type(d)}.') else: - raise ValueError('Invalid type for Date metadata. ' - 'Expected str, date, datetime, or iterable ' - 'of the same, not {!r}.'.format(type(date))) + raise TypeError(f'Invalid type for Date metadata. ' + f'Expected str, date, datetime, or iterable ' + f'of the same, not {type(date)}.') metadata['Date'] = '/'.join(dates) elif 'Date' not in metadata: # Do not add `Date` if the user explicitly set `Date` to `None` @@ -394,36 +434,40 @@ def ensure_metadata(mid): writer.element('dc:type', attrib={'rdf:resource': uri}) # Single value only. - for key in ['title', 'coverage', 'date', 'description', 'format', - 'identifier', 'language', 'relation', 'source']: - info = metadata.pop(key.title(), None) + for key in ['Title', 'Coverage', 'Date', 'Description', 'Format', + 'Identifier', 'Language', 'Relation', 'Source']: + info = metadata.pop(key, None) if info is not None: mid = ensure_metadata(mid) - writer.element(f'dc:{key}', text=info) + _check_is_str(info, key) + writer.element(f'dc:{key.lower()}', text=info) # Multiple Agent values. - for key in ['creator', 'contributor', 'publisher', 'rights']: - agents = metadata.pop(key.title(), None) + for key in ['Creator', 'Contributor', 'Publisher', 'Rights']: + agents = metadata.pop(key, None) if agents is None: continue if isinstance(agents, str): agents = [agents] + _check_is_iterable_of_str(agents, key) + # Now we know that we have an iterable of str mid = ensure_metadata(mid) - writer.start(f'dc:{key}') + writer.start(f'dc:{key.lower()}') for agent in agents: writer.start('cc:Agent') writer.element('dc:title', text=agent) writer.end('cc:Agent') - writer.end(f'dc:{key}') + writer.end(f'dc:{key.lower()}') # Multiple values. keywords = metadata.pop('Keywords', None) if keywords is not None: if isinstance(keywords, str): keywords = [keywords] - + _check_is_iterable_of_str(keywords, 'Keywords') + # Now we know that we have an iterable of str mid = ensure_metadata(mid) writer.start('dc:subject') writer.start('rdf:Bag') @@ -441,7 +485,7 @@ def ensure_metadata(mid): def _write_default_style(self): writer = self.writer - default_style = generate_css({ + default_style = _generate_css({ 'stroke-linejoin': 'round', 'stroke-linecap': 'butt'}) writer.start('defs') @@ -458,18 +502,7 @@ def _make_id(self, type, content): return '%s%s' % (type, m.hexdigest()[:10]) def _make_flip_transform(self, transform): - return (transform + - Affine2D() - .scale(1.0, -1.0) - .translate(0.0, self.height)) - - def _get_font(self, prop): - fname = findfont(prop) - font = get_font(fname) - font.clear() - size = prop.get_size_in_points() - font.set_size(size, 72.0) - return font + return transform + Affine2D().scale(1, -1).translate(0, self.height) def _get_hatch(self, gc, rgbFace): """ @@ -528,7 +561,7 @@ def _write_hatches(self): writer.element( 'path', d=path_data, - style=generate_css(hatch_style) + style=_generate_css(hatch_style) ) writer.end('pattern') writer.end('defs') @@ -543,7 +576,7 @@ def _get_style_dict(self, gc, rgbFace): attrib['fill'] = "url(#%s)" % self._get_hatch(gc, rgbFace) if (rgbFace is not None and len(rgbFace) == 4 and rgbFace[3] != 1.0 and not forced_alpha): - attrib['fill-opacity'] = short_float_fmt(rgbFace[3]) + attrib['fill-opacity'] = _short_float_fmt(rgbFace[3]) else: if rgbFace is None: attrib['fill'] = 'none' @@ -552,25 +585,25 @@ def _get_style_dict(self, gc, rgbFace): attrib['fill'] = rgb2hex(rgbFace) if (len(rgbFace) == 4 and rgbFace[3] != 1.0 and not forced_alpha): - attrib['fill-opacity'] = short_float_fmt(rgbFace[3]) + attrib['fill-opacity'] = _short_float_fmt(rgbFace[3]) if forced_alpha and gc.get_alpha() != 1.0: - attrib['opacity'] = short_float_fmt(gc.get_alpha()) + attrib['opacity'] = _short_float_fmt(gc.get_alpha()) offset, seq = gc.get_dashes() if seq is not None: attrib['stroke-dasharray'] = ','.join( - short_float_fmt(val) for val in seq) - attrib['stroke-dashoffset'] = short_float_fmt(float(offset)) + _short_float_fmt(val) for val in seq) + attrib['stroke-dashoffset'] = _short_float_fmt(float(offset)) linewidth = gc.get_linewidth() if linewidth: rgb = gc.get_rgb() attrib['stroke'] = rgb2hex(rgb) if not forced_alpha and rgb[3] != 1.0: - attrib['stroke-opacity'] = short_float_fmt(rgb[3]) + attrib['stroke-opacity'] = _short_float_fmt(rgb[3]) if linewidth != 1.0: - attrib['stroke-width'] = short_float_fmt(linewidth) + attrib['stroke-width'] = _short_float_fmt(linewidth) if gc.get_joinstyle() != 'round': attrib['stroke-linejoin'] = gc.get_joinstyle() if gc.get_capstyle() != 'butt': @@ -579,9 +612,9 @@ def _get_style_dict(self, gc, rgbFace): return attrib def _get_style(self, gc, rgbFace): - return generate_css(self._get_style_dict(gc, rgbFace)) + return _generate_css(self._get_style_dict(gc, rgbFace)) - def _get_clip(self, gc): + def _get_clip_attrs(self, gc): cliprect = gc.get_clip_rectangle() clippath, clippath_trans = gc.get_clip_path() if clippath is not None: @@ -592,8 +625,7 @@ def _get_clip(self, gc): y = self.height-(y+h) dictkey = (x, y, w, h) else: - return None - + return {} clip = self._clipd.get(dictkey) if clip is None: oid = self._make_id('p', dictkey) @@ -603,7 +635,7 @@ def _get_clip(self, gc): self._clipd[dictkey] = (dictkey, oid) else: clip, oid = clip - return oid + return {'clip-path': f'url(#{oid})'} def _write_clips(self): if not len(self._clipd): @@ -621,10 +653,10 @@ def _write_clips(self): x, y, w, h = clip writer.element( 'rect', - x=short_float_fmt(x), - y=short_float_fmt(y), - width=short_float_fmt(w), - height=short_float_fmt(h)) + x=_short_float_fmt(x), + y=_short_float_fmt(y), + width=_short_float_fmt(w), + height=_short_float_fmt(h)) writer.end('clipPath') writer.end('defs') @@ -663,16 +695,10 @@ def draw_path(self, gc, path, transform, rgbFace=None): path, trans_and_flip, clip=clip, simplify=simplify, sketch=gc.get_sketch_params()) - attrib = {} - attrib['style'] = self._get_style(gc, rgbFace) - - clipid = self._get_clip(gc) - if clipid is not None: - attrib['clip-path'] = 'url(#%s)' % clipid - if gc.get_url() is not None: self.writer.start('a', {'xlink:href': gc.get_url()}) - self.writer.element('path', d=path_data, attrib=attrib) + self.writer.element('path', d=path_data, **self._get_clip_attrs(gc), + style=self._get_style(gc, rgbFace)) if gc.get_url() is not None: self.writer.end('a') @@ -689,9 +715,9 @@ def draw_markers( marker_trans + Affine2D().scale(1.0, -1.0), simplify=False) style = self._get_style_dict(gc, rgbFace) - dictkey = (path_data, generate_css(style)) + dictkey = (path_data, _generate_css(style)) oid = self._markers.get(dictkey) - style = generate_css({k: v for k, v in style.items() + style = _generate_css({k: v for k, v in style.items() if k.startswith('stroke')}) if oid is None: @@ -701,12 +727,7 @@ def draw_markers( writer.end('defs') self._markers[dictkey] = oid - attrib = {} - clipid = self._get_clip(gc) - if clipid is not None: - attrib['clip-path'] = 'url(#%s)' % clipid - writer.start('g', attrib=attrib) - + writer.start('g', **self._get_clip_attrs(gc)) trans_and_flip = self._make_flip_transform(trans) attrib = {'xlink:href': '#%s' % oid} clip = (0, 0, self.width*72, self.height*72) @@ -714,14 +735,14 @@ def draw_markers( trans_and_flip, clip=clip, simplify=False): if len(vertices): x, y = vertices[-2:] - attrib['x'] = short_float_fmt(x) - attrib['y'] = short_float_fmt(y) + attrib['x'] = _short_float_fmt(x) + attrib['y'] = _short_float_fmt(y) attrib['style'] = self._get_style(gc, rgbFace) writer.element('use', attrib=attrib) writer.end('g') def draw_path_collection(self, gc, master_transform, paths, all_transforms, - offsets, offsetTrans, facecolors, edgecolors, + offsets, offset_trans, facecolors, edgecolors, linewidths, linestyles, antialiaseds, urls, offset_position): # Is the optimization worth it? Rough calculation: @@ -737,7 +758,7 @@ def draw_path_collection(self, gc, master_transform, paths, all_transforms, if not should_do_optimization: return super().draw_path_collection( gc, master_transform, paths, all_transforms, - offsets, offsetTrans, facecolors, edgecolors, + offsets, offset_trans, facecolors, edgecolors, linewidths, linestyles, antialiaseds, urls, offset_position) @@ -755,23 +776,23 @@ def draw_path_collection(self, gc, master_transform, paths, all_transforms, writer.end('defs') for xo, yo, path_id, gc0, rgbFace in self._iter_collection( - gc, master_transform, all_transforms, path_codes, offsets, - offsetTrans, facecolors, edgecolors, linewidths, linestyles, + gc, path_codes, offsets, offset_trans, + facecolors, edgecolors, linewidths, linestyles, antialiaseds, urls, offset_position): - clipid = self._get_clip(gc0) url = gc0.get_url() if url is not None: writer.start('a', attrib={'xlink:href': url}) - if clipid is not None: - writer.start('g', attrib={'clip-path': 'url(#%s)' % clipid}) + clip_attrs = self._get_clip_attrs(gc0) + if clip_attrs: + writer.start('g', **clip_attrs) attrib = { 'xlink:href': '#%s' % path_id, - 'x': short_float_fmt(xo), - 'y': short_float_fmt(self.height - yo), + 'x': _short_float_fmt(xo), + 'y': _short_float_fmt(self.height - yo), 'style': self._get_style(gc0, rgbFace) } writer.element('use', attrib=attrib) - if clipid is not None: + if clip_attrs: writer.end('g') if url is not None: writer.end('a') @@ -849,18 +870,18 @@ def draw_gouraud_triangle(self, gc, points, colors, trans): 'linearGradient', id="GR%x_%d" % (self._n_gradients, i), gradientUnits="userSpaceOnUse", - x1=short_float_fmt(x1), y1=short_float_fmt(y1), - x2=short_float_fmt(xb), y2=short_float_fmt(yb)) + x1=_short_float_fmt(x1), y1=_short_float_fmt(y1), + x2=_short_float_fmt(xb), y2=_short_float_fmt(yb)) writer.element( 'stop', offset='1', - style=generate_css({ + style=_generate_css({ 'stop-color': rgb2hex(avg_color), - 'stop-opacity': short_float_fmt(rgba_color[-1])})) + 'stop-opacity': _short_float_fmt(rgba_color[-1])})) writer.element( 'stop', offset='0', - style=generate_css({'stop-color': rgb2hex(rgba_color), + style=_generate_css({'stop-color': rgb2hex(rgba_color), 'stop-opacity': "0"})) writer.end('linearGradient') @@ -868,9 +889,9 @@ def draw_gouraud_triangle(self, gc, points, colors, trans): writer.end('defs') # triangle formation using "path" - dpath = "M " + short_float_fmt(x1)+',' + short_float_fmt(y1) - dpath += " L " + short_float_fmt(x2) + ',' + short_float_fmt(y2) - dpath += " " + short_float_fmt(x3) + ',' + short_float_fmt(y3) + " Z" + dpath = "M " + _short_float_fmt(x1)+',' + _short_float_fmt(y1) + dpath += " L " + _short_float_fmt(x2) + ',' + _short_float_fmt(y2) + dpath += " " + _short_float_fmt(x3) + ',' + _short_float_fmt(y3) + " Z" writer.element( 'path', @@ -912,17 +933,10 @@ def draw_gouraud_triangle(self, gc, points, colors, trans): def draw_gouraud_triangles(self, gc, triangles_array, colors_array, transform): - attrib = {} - clipid = self._get_clip(gc) - if clipid is not None: - attrib['clip-path'] = 'url(#%s)' % clipid - - self.writer.start('g', attrib=attrib) - + self.writer.start('g', **self._get_clip_attrs(gc)) transform = transform.frozen() for tri, col in zip(triangles_array, colors_array): self.draw_gouraud_triangle(gc, tri, col, transform) - self.writer.end('g') def option_scale_image(self): @@ -940,18 +954,18 @@ def draw_image(self, gc, x, y, im, transform=None): if w == 0 or h == 0: return - attrib = {} - clipid = self._get_clip(gc) - if clipid is not None: - # Can't apply clip-path directly to the image because the - # image has a transformation, which would also be applied - # to the clip-path - self.writer.start('g', attrib={'clip-path': 'url(#%s)' % clipid}) + clip_attrs = self._get_clip_attrs(gc) + if clip_attrs: + # Can't apply clip-path directly to the image because the image has + # a transformation, which would also be applied to the clip-path. + self.writer.start('g', **clip_attrs) - oid = gc.get_gid() url = gc.get_url() if url is not None: self.writer.start('a', attrib={'xlink:href': url}) + + attrib = {} + oid = gc.get_gid() if mpl.rcParams['svg.image_inline']: buf = BytesIO() Image.fromarray(im).save(buf, format="png") @@ -969,7 +983,6 @@ def draw_image(self, gc, x, y, im, transform=None): Image.fromarray(im).save(filename) oid = oid or 'Im_' + self._make_id('image', filename) attrib['xlink:href'] = filename - attrib['id'] = oid if transform is None: @@ -978,16 +991,16 @@ def draw_image(self, gc, x, y, im, transform=None): self.writer.element( 'image', - transform=generate_transform([ + transform=_generate_transform([ ('scale', (1, -1)), ('translate', (0, -h))]), - x=short_float_fmt(x), - y=short_float_fmt(-(self.height - y - h)), - width=short_float_fmt(w), height=short_float_fmt(h), + x=_short_float_fmt(x), + y=_short_float_fmt(-(self.height - y - h)), + width=_short_float_fmt(w), height=_short_float_fmt(h), attrib=attrib) else: alpha = gc.get_alpha() if alpha != 1.0: - attrib['opacity'] = short_float_fmt(alpha) + attrib['opacity'] = _short_float_fmt(alpha) flipped = ( Affine2D().scale(1.0 / w, 1.0 / h) + @@ -997,19 +1010,19 @@ def draw_image(self, gc, x, y, im, transform=None): .scale(1.0, -1.0) .translate(0.0, self.height)) - attrib['transform'] = generate_transform( + attrib['transform'] = _generate_transform( [('matrix', flipped.frozen())]) attrib['style'] = ( 'image-rendering:crisp-edges;' 'image-rendering:pixelated') self.writer.element( 'image', - width=short_float_fmt(w), height=short_float_fmt(h), + width=_short_float_fmt(w), height=_short_float_fmt(h), attrib=attrib) if url is not None: self.writer.end('a') - if clipid is not None: + if clip_attrs: self.writer.end('g') def _update_glyph_map_defs(self, glyph_map_new): @@ -1027,7 +1040,7 @@ def _update_glyph_map_defs(self, glyph_map_new): Path(vertices * 64, codes), simplify=False) writer.element( 'path', id=char_id, d=path_data, - transform=generate_transform([('scale', (1 / 64,))])) + transform=_generate_transform([('scale', (1 / 64,))])) writer.end('defs') self._glyph_map.update(glyph_map_new) @@ -1062,11 +1075,11 @@ def _draw_text_as_path(self, gc, x, y, s, prop, angle, ismath, mtext=None): style['fill'] = color alpha = gc.get_alpha() if gc.get_forced_alpha() else gc.get_rgb()[3] if alpha != 1: - style['opacity'] = short_float_fmt(alpha) + style['opacity'] = _short_float_fmt(alpha) font_scale = fontsize / text2path.FONT_SCALE attrib = { - 'style': generate_css(style), - 'transform': generate_transform([ + 'style': _generate_css(style), + 'transform': _generate_transform([ ('translate', (x, y)), ('rotate', (-angle,)), ('scale', (font_scale, -font_scale))]), @@ -1083,9 +1096,9 @@ def _draw_text_as_path(self, gc, x, y, s, prop, angle, ismath, mtext=None): for glyph_id, xposition, yposition, scale in glyph_info: attrib = {'xlink:href': '#%s' % glyph_id} if xposition != 0.0: - attrib['x'] = short_float_fmt(xposition) + attrib['x'] = _short_float_fmt(xposition) if yposition != 0.0: - attrib['y'] = short_float_fmt(yposition) + attrib['y'] = _short_float_fmt(yposition) writer.element('use', attrib=attrib) else: @@ -1102,7 +1115,7 @@ def _draw_text_as_path(self, gc, x, y, s, prop, angle, ismath, mtext=None): char_id = self._adjust_char_id(char_id) writer.element( 'use', - transform=generate_transform([ + transform=_generate_transform([ ('translate', (xposition, yposition)), ('scale', (scale,)), ]), @@ -1125,20 +1138,66 @@ def _draw_text_as_text(self, gc, x, y, s, prop, angle, ismath, mtext=None): alpha = gc.get_alpha() if gc.get_forced_alpha() else gc.get_rgb()[3] if alpha != 1: - style['opacity'] = short_float_fmt(alpha) + style['opacity'] = _short_float_fmt(alpha) if not ismath: - font = self._get_font(prop) - font.set_text(s, 0.0, flags=LOAD_NO_HINTING) - attrib = {} - style['font-family'] = str(font.family_name) - style['font-weight'] = str(prop.get_weight()).lower() - style['font-stretch'] = str(prop.get_stretch()).lower() - style['font-style'] = prop.get_style().lower() - # Must add "px" to workaround a Firefox bug - style['font-size'] = short_float_fmt(prop.get_size()) + 'px' - attrib['style'] = generate_css(style) + + font_parts = [] + if prop.get_style() != 'normal': + font_parts.append(prop.get_style()) + if prop.get_variant() != 'normal': + font_parts.append(prop.get_variant()) + weight = fm.weight_dict[prop.get_weight()] + if weight != 400: + font_parts.append(f'{weight}') + + def _format_font_name(fn): + normalize_names = { + 'sans': 'sans-serif', + 'sans serif': 'sans-serif' + } + # A generic font family. We need to do two things: + # 1. list all of the configured fonts with quoted names + # 2. append the generic name unquoted + if fn in fm.font_family_aliases: + # fix spelling of sans-serif + # we accept 3 ways CSS only supports 1 + fn = normalize_names.get(fn, fn) + # get all of the font names and fix spelling of sans-serif + # if it comes back + aliases = [ + normalize_names.get(_, _) for _ in + fm.FontManager._expand_aliases(fn) + ] + # make sure the generic name appears at least once + # duplicate is OK, next layer will deduplicate + aliases.append(fn) + + for a in aliases: + # generic font families must not be quoted + if a in fm.font_family_aliases: + yield a + # specific font families must be quoted + else: + yield repr(a) + # specific font families must be quoted + else: + yield repr(fn) + + def _get_all_names(prop): + for f in prop.get_family(): + yield from _format_font_name(f) + + font_parts.extend([ + f'{_short_float_fmt(prop.get_size())}px', + # ensure quoting and expansion of font names + ", ".join(dict.fromkeys(_get_all_names(prop))) + ]) + style['font'] = ' '.join(font_parts) + if prop.get_stretch() != 'normal': + style['font-stretch'] = prop.get_stretch() + attrib['style'] = _generate_css(style) if mtext and (angle == 0 or mtext.get_rotation_mode() == "anchor"): # If text anchoring can be supported, get the original @@ -1162,20 +1221,18 @@ def _draw_text_as_text(self, gc, x, y, s, prop, angle, ismath, mtext=None): 'center': 'middle'} style['text-anchor'] = ha_mpl_to_svg[mtext.get_ha()] - attrib['x'] = short_float_fmt(ax) - attrib['y'] = short_float_fmt(ay) - attrib['style'] = generate_css(style) - attrib['transform'] = "rotate(%s, %s, %s)" % ( - short_float_fmt(-angle), - short_float_fmt(ax), - short_float_fmt(ay)) - writer.element('text', s, attrib=attrib) + attrib['x'] = _short_float_fmt(ax) + attrib['y'] = _short_float_fmt(ay) + attrib['style'] = _generate_css(style) + attrib['transform'] = _generate_transform([ + ("rotate", (-angle, ax, ay))]) + else: - attrib['transform'] = generate_transform([ + attrib['transform'] = _generate_transform([ ('translate', (x, y)), ('rotate', (-angle,))]) - writer.element('text', s, attrib=attrib) + writer.element('text', s, attrib=attrib) else: writer.comment(s) @@ -1186,8 +1243,8 @@ def _draw_text_as_text(self, gc, x, y, s, prop, angle, ismath, mtext=None): # Apply attributes to 'g', not 'text', because we likely have some # rectangles as well with the same style and transformation. writer.start('g', - style=generate_css(style), - transform=generate_transform([ + style=_generate_css(style), + transform=_generate_transform([ ('translate', (x, y)), ('rotate', (-angle,))]), ) @@ -1195,13 +1252,24 @@ def _draw_text_as_text(self, gc, x, y, s, prop, angle, ismath, mtext=None): writer.start('text') # Sort the characters by font, and output one tspan for each. - spans = OrderedDict() + spans = {} for font, fontsize, thetext, new_x, new_y in glyphs: - style = generate_css({ - 'font-size': short_float_fmt(fontsize) + 'px', - 'font-family': font.family_name, - 'font-style': font.style_name.lower(), - 'font-weight': font.style_name.lower()}) + entry = fm.ttfFontProperty(font) + font_parts = [] + if entry.style != 'normal': + font_parts.append(entry.style) + if entry.variant != 'normal': + font_parts.append(entry.variant) + if entry.weight != 400: + font_parts.append(f'{entry.weight}') + font_parts.extend([ + f'{_short_float_fmt(fontsize)}px', + f'{entry.name!r}', # ensure quoting + ]) + style = {'font': ' '.join(font_parts)} + if entry.stretch != 'normal': + style['font-stretch'] = entry.stretch + style = _generate_css(style) if thetext == 32: thetext = 0xa0 # non-breaking space spans.setdefault(style, []).append((new_x, -new_y, thetext)) @@ -1216,7 +1284,7 @@ def _draw_text_as_text(self, gc, x, y, s, prop, angle, ismath, mtext=None): attrib = { 'style': style, - 'x': ' '.join(short_float_fmt(c[0]) for c in chars), + 'x': ' '.join(_short_float_fmt(c[0]) for c in chars), 'y': ys } @@ -1230,28 +1298,26 @@ def _draw_text_as_text(self, gc, x, y, s, prop, angle, ismath, mtext=None): for x, y, width, height in rects: writer.element( 'rect', - x=short_float_fmt(x), - y=short_float_fmt(-y-1), - width=short_float_fmt(width), - height=short_float_fmt(height) + x=_short_float_fmt(x), + y=_short_float_fmt(-y-1), + width=_short_float_fmt(width), + height=_short_float_fmt(height) ) writer.end('g') - @_api.delete_parameter("3.3", "ismath") - def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!', mtext=None): + def draw_tex(self, gc, x, y, s, prop, angle, *, mtext=None): # docstring inherited self._draw_text_as_path(gc, x, y, s, prop, angle, ismath="TeX") def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): # docstring inherited - clipid = self._get_clip(gc) - if clipid is not None: + clip_attrs = self._get_clip_attrs(gc) + if clip_attrs: # Cannot apply clip-path directly to the text, because - # is has a transformation - self.writer.start( - 'g', attrib={'clip-path': 'url(#%s)' % clipid}) + # it has a transformation + self.writer.start('g', **clip_attrs) if gc.get_url() is not None: self.writer.start('a', {'xlink:href': gc.get_url()}) @@ -1264,7 +1330,7 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): if gc.get_url() is not None: self.writer.end('a') - if clipid is not None: + if clip_attrs: self.writer.end('g') def flipy(self): @@ -1286,7 +1352,9 @@ class FigureCanvasSVG(FigureCanvasBase): fixed_dpi = 72 - def print_svg(self, filename, *args, **kwargs): + @_api.delete_parameter("3.5", "args") + def print_svg(self, filename, *args, bbox_inches_restore=None, + metadata=None): """ Parameters ---------- @@ -1319,52 +1387,30 @@ def print_svg(self, filename, *args, **kwargs): __ DC_ """ with cbook.open_file_cm(filename, "w", encoding="utf-8") as fh: - - filename = getattr(fh, 'name', '') - if not isinstance(filename, str): - filename = '' - - if cbook.file_requires_unicode(fh): - detach = False - else: - fh = TextIOWrapper(fh, 'utf-8') - detach = True - - self._print_svg(filename, fh, **kwargs) - - # Detach underlying stream from wrapper so that it remains open in - # the caller. - if detach: - fh.detach() - + if not cbook.file_requires_unicode(fh): + fh = codecs.getwriter('utf-8')(fh) + dpi = self.figure.dpi + self.figure.dpi = 72 + width, height = self.figure.get_size_inches() + w, h = width * 72, height * 72 + renderer = MixedModeRenderer( + self.figure, width, height, dpi, + RendererSVG(w, h, fh, image_dpi=dpi, metadata=metadata), + bbox_inches_restore=bbox_inches_restore) + self.figure.draw(renderer) + renderer.finalize() + + @_api.delete_parameter("3.5", "args") def print_svgz(self, filename, *args, **kwargs): with cbook.open_file_cm(filename, "wb") as fh, \ gzip.GzipFile(mode='w', fileobj=fh) as gzipwriter: - return self.print_svg(gzipwriter) - - @_check_savefig_extra_args - @_api.delete_parameter("3.4", "dpi") - def _print_svg(self, filename, fh, *, dpi=None, bbox_inches_restore=None, - metadata=None): - if dpi is None: # always use this branch after deprecation elapses. - dpi = self.figure.get_dpi() - self.figure.set_dpi(72) - width, height = self.figure.get_size_inches() - w, h = width * 72, height * 72 - - renderer = MixedModeRenderer( - self.figure, width, height, dpi, - RendererSVG(w, h, fh, filename, dpi, metadata=metadata), - bbox_inches_restore=bbox_inches_restore) - - self.figure.draw(renderer) - renderer.finalize() + return self.print_svg(gzipwriter, **kwargs) def get_default_filetype(self): return 'svg' def draw(self): - _no_output_draw(self.figure) + self.figure.draw_without_rendering() return super().draw() @@ -1380,4 +1426,5 @@ def draw(self): @_Backend.export class _BackendSVG(_Backend): + backend_version = mpl.__version__ FigureCanvas = FigureCanvasSVG diff --git a/lib/matplotlib/backends/backend_template.py b/lib/matplotlib/backends/backend_template.py index 7ed017a0c6eb..6b7aba9a504b 100644 --- a/lib/matplotlib/backends/backend_template.py +++ b/lib/matplotlib/backends/backend_template.py @@ -29,6 +29,7 @@ plt.savefig("figure.xyz") """ +from matplotlib import _api from matplotlib._pylab_helpers import Gcf from matplotlib.backend_bases import ( FigureCanvasBase, FigureManagerBase, GraphicsContextBase, RendererBase) @@ -40,7 +41,7 @@ class RendererTemplate(RendererBase): The renderer handles drawing/rendering operations. This is a minimal do-nothing class that can be used to get started when - writing a new backend. Refer to `backend_bases.RendererBase` for + writing a new backend. Refer to `.backend_bases.RendererBase` for documentation of the methods. """ @@ -62,7 +63,7 @@ def draw_path(self, gc, path, transform, rgbFace=None): # relative timings by leaving it out. backend implementers concerned with # performance will probably want to implement it # def draw_path_collection(self, gc, master_transform, paths, -# all_transforms, offsets, offsetTrans, +# all_transforms, offsets, offset_trans, # facecolors, edgecolors, linewidths, linestyles, # antialiaseds): # pass @@ -100,14 +101,14 @@ def points_to_pixels(self, points): # if backend doesn't have dpi, e.g., postscript or svg return points # elif backend assumes a value for pixels_per_inch - #return points/72.0 * self.dpi.get() * pixels_per_inch/72.0 + # return points/72.0 * self.dpi.get() * pixels_per_inch/72.0 # else - #return points/72.0 * self.dpi.get() + # return points/72.0 * self.dpi.get() class GraphicsContextTemplate(GraphicsContextBase): """ - The graphics context provides the color, line styles, etc... See the cairo + The graphics context provides the color, line styles, etc. See the cairo and postscript backends for examples of mapping the graphics context attributes (cap styles, join styles, line widths, colors) to a particular backend. In cairo this is done by wrapping a cairo.Context object and @@ -130,19 +131,11 @@ class GraphicsContextTemplate(GraphicsContextBase): ######################################################################## # # The following functions and classes are for pyplot and implement -# window/figure managers, etc... +# window/figure managers, etc. # ######################################################################## -def draw_if_interactive(): - """ - For image backends - is not required. - For GUI backends - this should be overridden if drawing should be done in - interactive python mode. - """ - - def show(*, block=None): """ For image backends - is not required. @@ -155,22 +148,12 @@ def show(*, block=None): pass -def new_figure_manager(num, *args, FigureClass=Figure, **kwargs): - """Create a new figure manager instance.""" - # If a main-level app must be created, this (and - # new_figure_manager_given_figure) is the usual place to do it -- see - # backend_wx, backend_wxagg and backend_tkagg for examples. Not all GUIs - # require explicit instantiation of a main-level app (e.g., backend_gtk3) - # for pylab. - thisFig = FigureClass(*args, **kwargs) - return new_figure_manager_given_figure(num, thisFig) - +class FigureManagerTemplate(FigureManagerBase): + """ + Helper class for pyplot mode, wraps everything up into a neat bundle. -def new_figure_manager_given_figure(num, figure): - """Create a new figure manager instance for the given figure.""" - canvas = FigureCanvasTemplate(figure) - manager = FigureManagerTemplate(canvas, num) - return manager + For non-interactive backends, the base class is sufficient. + """ class FigureCanvasTemplate(FigureCanvasBase): @@ -190,6 +173,11 @@ class methods button_press_event, button_release_event, A high-level Figure instance """ + # The instantiated manager class. For further customization, + # ``FigureManager.create_with_canvas`` can also be overridden; see the + # wx-based backends for an example. + manager_class = FigureManagerTemplate + def draw(self): """ Draw the figure using the renderer. @@ -209,6 +197,7 @@ def draw(self): # you should add it to the class-scope filetypes dictionary as follows: filetypes = {**FigureCanvasBase.filetypes, 'foo': 'My magic Foo format'} + @_api.delete_parameter("3.5", "args") def print_foo(self, filename, *args, **kwargs): """ Write out format foo. @@ -225,14 +214,6 @@ def get_default_filetype(self): return 'foo' -class FigureManagerTemplate(FigureManagerBase): - """ - Helper class for pyplot mode, wraps everything up into a neat bundle. - - For non-interactive backends, the base class is sufficient. - """ - - ######################################################################## # # Now just provide the standard names that backend.__init__ is expecting diff --git a/lib/matplotlib/backends/backend_tkagg.py b/lib/matplotlib/backends/backend_tkagg.py index 31012d80eab8..f95b6011eadf 100644 --- a/lib/matplotlib/backends/backend_tkagg.py +++ b/lib/matplotlib/backends/backend_tkagg.py @@ -1,7 +1,8 @@ from . import _backend_tk from .backend_agg import FigureCanvasAgg -from ._backend_tk import ( - _BackendTk, FigureCanvasTk, FigureManagerTk, NavigationToolbar2Tk) +from ._backend_tk import _BackendTk, FigureCanvasTk +from ._backend_tk import ( # noqa: F401 # pylint: disable=W0611 + FigureManagerTk, NavigationToolbar2Tk) class FigureCanvasTkAgg(FigureCanvasAgg, FigureCanvasTk): diff --git a/lib/matplotlib/backends/backend_tkcairo.py b/lib/matplotlib/backends/backend_tkcairo.py index a81fd0d92bb8..a6951c03c65a 100644 --- a/lib/matplotlib/backends/backend_tkcairo.py +++ b/lib/matplotlib/backends/backend_tkcairo.py @@ -3,21 +3,17 @@ import numpy as np from . import _backend_tk -from .backend_cairo import cairo, FigureCanvasCairo, RendererCairo +from .backend_cairo import cairo, FigureCanvasCairo from ._backend_tk import _BackendTk, FigureCanvasTk class FigureCanvasTkCairo(FigureCanvasCairo, FigureCanvasTk): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self._renderer = RendererCairo(self.figure.dpi) - def draw(self): width = int(self.figure.bbox.width) height = int(self.figure.bbox.height) surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) - self._renderer.set_ctx_from_surface(surface) - self._renderer.set_width_height(width, height) + self._renderer.set_context(cairo.Context(surface)) + self._renderer.dpi = self.figure.dpi self.figure.draw(self._renderer) buf = np.reshape(surface.get_data(), (height, width, 4)) _backend_tk.blit( diff --git a/lib/matplotlib/backends/backend_webagg.py b/lib/matplotlib/backends/backend_webagg.py index 7f43187ac65f..9e1e4925496f 100644 --- a/lib/matplotlib/backends/backend_webagg.py +++ b/lib/matplotlib/backends/backend_webagg.py @@ -36,7 +36,8 @@ from matplotlib.backend_bases import _Backend from matplotlib._pylab_helpers import Gcf from . import backend_webagg_core as core -from .backend_webagg_core import TimerTornado +from .backend_webagg_core import ( # noqa: F401 # pylint: disable=W0611 + TimerAsyncio, TimerTornado) class ServerThread(threading.Thread): @@ -47,8 +48,12 @@ def run(self): webagg_server_thread = ServerThread() +class FigureManagerWebAgg(core.FigureManagerWebAgg): + _toolbar2_class = core.NavigationToolbar2WebAgg + + class FigureCanvasWebAgg(core.FigureCanvasWebAggCore): - pass + manager_class = FigureManagerWebAgg class WebAggApplication(tornado.web.Application): @@ -299,10 +304,10 @@ def ipython_inline_display(figure): @_Backend.export class _BackendWebAgg(_Backend): FigureCanvas = FigureCanvasWebAgg - FigureManager = core.FigureManagerWebAgg + FigureManager = FigureManagerWebAgg @staticmethod - def show(): + def show(*, block=None): WebAggApplication.initialize() url = "http://{address}:{port}{prefix}".format( diff --git a/lib/matplotlib/backends/backend_webagg_core.py b/lib/matplotlib/backends/backend_webagg_core.py index ed0d6173a6fe..ee2d73d0cf95 100644 --- a/lib/matplotlib/backends/backend_webagg_core.py +++ b/lib/matplotlib/backends/backend_webagg_core.py @@ -8,8 +8,9 @@ # way over a web socket. # # - `backend_webagg.py` contains a concrete implementation of a basic -# application, implemented with tornado. +# application, implemented with asyncio. +import asyncio import datetime from io import BytesIO, StringIO import json @@ -19,11 +20,11 @@ import numpy as np from PIL import Image -import tornado -from matplotlib import _api, backend_bases +from matplotlib import _api, backend_bases, backend_tools from matplotlib.backends import backend_agg -from matplotlib.backend_bases import _Backend +from matplotlib.backend_bases import ( + _Backend, KeyEvent, LocationEvent, MouseEvent, ResizeEvent) _log = logging.getLogger(__name__) @@ -85,6 +86,8 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def _timer_start(self): + import tornado + self._timer_stop() if self._single: ioloop = tornado.ioloop.IOLoop.instance() @@ -98,6 +101,8 @@ def _timer_start(self): self._timer.start() def _timer_stop(self): + import tornado + if self._timer is None: return elif self._single: @@ -114,8 +119,44 @@ def _timer_set_interval(self): self._timer_start() +class TimerAsyncio(backend_bases.TimerBase): + def __init__(self, *args, **kwargs): + self._task = None + super().__init__(*args, **kwargs) + + async def _timer_task(self, interval): + while True: + try: + await asyncio.sleep(interval) + self._on_timer() + + if self._single: + break + except asyncio.CancelledError: + break + + def _timer_start(self): + self._timer_stop() + + self._task = asyncio.ensure_future( + self._timer_task(max(self.interval / 1_000., 1e-6)) + ) + + def _timer_stop(self): + if self._task is not None: + self._task.cancel() + self._task = None + + def _timer_set_interval(self): + # Only stop and restart it if the timer has already been started + if self._task is not None: + self._timer_stop() + self._timer_start() + + class FigureCanvasWebAggCore(backend_agg.FigureCanvasAgg): - _timer_cls = TimerTornado + manager_class = _api.classproperty(lambda cls: FigureManagerWebAgg) + _timer_cls = TimerAsyncio # Webagg and friends having the right methods, but still # having bugs in practice. Do not advertise that it works until # we can debug this. @@ -123,24 +164,21 @@ class FigureCanvasWebAggCore(backend_agg.FigureCanvasAgg): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - # Set to True when the renderer contains data that is newer # than the PNG buffer. self._png_is_old = True - # Set to True by the `refresh` message so that the next frame # sent to the clients will be a full frame. self._force_full = True - + # The last buffer, for diff mode. + self._last_buff = np.empty((0, 0)) # Store the current image mode so that at any point, clients can # request the information. This should be changed by calling # self.set_image_mode(mode) so that the notification can be given # to the connected clients. self._current_image_mode = 'full' - - # Store the DPI ratio of the browser. This is the scaling that - # occurs automatically for all images on a HiDPI display. - self._dpi_ratio = 1 + # Track mouse events to fill in the x, y position of key events. + self._last_mouse_xy = (None, None) def show(self): # show the figure window @@ -161,6 +199,19 @@ def blit(self, bbox=None): def draw_idle(self): self.send_event("draw") + def set_cursor(self, cursor): + # docstring inherited + cursor = _api.check_getitem({ + backend_tools.Cursors.HAND: 'pointer', + backend_tools.Cursors.POINTER: 'default', + backend_tools.Cursors.SELECT_REGION: 'crosshair', + backend_tools.Cursors.MOVE: 'move', + backend_tools.Cursors.WAIT: 'wait', + backend_tools.Cursors.RESIZE_HORIZONTAL: 'ew-resize', + backend_tools.Cursors.RESIZE_VERTICAL: 'ns-resize', + }, cursor=cursor) + self.send_event('cursor', cursor=cursor) + def set_image_mode(self, mode): """ Set the image mode for any subsequent images which will be sent @@ -179,17 +230,18 @@ def get_diff_image(self): if self._png_is_old: renderer = self.get_renderer() + pixels = np.asarray(renderer.buffer_rgba()) # The buffer is created as type uint32 so that entire # pixels can be compared in one numpy call, rather than # needing to compare each plane separately. - buff = (np.frombuffer(renderer.buffer_rgba(), dtype=np.uint32) - .reshape((renderer.height, renderer.width))) - - # If any pixels have transparency, we need to force a full - # draw as we cannot overlay new on top of old. - pixels = buff.view(dtype=np.uint8).reshape(buff.shape + (4,)) - - if self._force_full or np.any(pixels[:, :, 3] != 255): + buff = pixels.view(np.uint32).squeeze(2) + + if (self._force_full + # If the buffer has changed size we need to do a full draw. + or buff.shape != self._last_buff.shape + # If any pixels have transparency, we need to force a full + # draw as we cannot overlay new on top of old. + or (pixels[:, :, 3] != 255).any()): self.set_image_mode('full') output = buff else: @@ -198,7 +250,7 @@ def get_diff_image(self): output = np.where(diff, buff, 0) # Store the current buffer so we can compute the next diff. - np.copyto(self._last_buff, buff) + self._last_buff = buff.copy() self._force_full = False self._png_is_old = False @@ -207,31 +259,6 @@ def get_diff_image(self): Image.fromarray(data).save(png, format="png") return png.getvalue() - def get_renderer(self, cleared=None): - # Mirrors super.get_renderer, but caches the old one so that we can do - # things such as produce a diff image in get_diff_image. - w, h = self.figure.bbox.size.astype(int) - key = w, h, self.figure.dpi - try: - self._lastKey, self._renderer - except AttributeError: - need_new_renderer = True - else: - need_new_renderer = (self._lastKey != key) - - if need_new_renderer: - self._renderer = backend_agg.RendererAgg( - w, h, self.figure.dpi) - self._lastKey = key - self._last_buff = np.copy(np.frombuffer( - self._renderer.buffer_rgba(), dtype=np.uint32 - ).reshape((self._renderer.height, self._renderer.width))) - - elif cleared: - self._renderer.clear() - - return self._renderer - def handle_event(self, event): e_type = event['type'] handler = getattr(self, 'handle_{0}'.format(e_type), @@ -258,40 +285,35 @@ def _handle_mouse(self, event): x = event['x'] y = event['y'] y = self.get_renderer().height - y - - # Javascript button numbers and matplotlib button numbers are - # off by 1 + self._last_mouse_xy = x, y + # JavaScript button numbers and Matplotlib button numbers are off by 1. button = event['button'] + 1 e_type = event['type'] - guiEvent = event.get('guiEvent', None) - if e_type == 'button_press': - self.button_press_event(x, y, button, guiEvent=guiEvent) + guiEvent = event.get('guiEvent') + if e_type in ['button_press', 'button_release']: + MouseEvent(e_type + '_event', self, x, y, button, + guiEvent=guiEvent)._process() elif e_type == 'dblclick': - self.button_press_event(x, y, button, dblclick=True, - guiEvent=guiEvent) - elif e_type == 'button_release': - self.button_release_event(x, y, button, guiEvent=guiEvent) - elif e_type == 'motion_notify': - self.motion_notify_event(x, y, guiEvent=guiEvent) - elif e_type == 'figure_enter': - self.enter_notify_event(xy=(x, y), guiEvent=guiEvent) - elif e_type == 'figure_leave': - self.leave_notify_event() + MouseEvent('button_press_event', self, x, y, button, dblclick=True, + guiEvent=guiEvent)._process() elif e_type == 'scroll': - self.scroll_event(x, y, event['step'], guiEvent=guiEvent) + MouseEvent('scroll_event', self, x, y, step=event['step'], + guiEvent=guiEvent)._process() + elif e_type == 'motion_notify': + MouseEvent(e_type + '_event', self, x, y, + guiEvent=guiEvent)._process() + elif e_type in ['figure_enter', 'figure_leave']: + LocationEvent(e_type + '_event', self, x, y, + guiEvent=guiEvent)._process() handle_button_press = handle_button_release = handle_dblclick = \ handle_figure_enter = handle_figure_leave = handle_motion_notify = \ handle_scroll = _handle_mouse def _handle_key(self, event): - key = _handle_key(event['key']) - e_type = event['type'] - guiEvent = event.get('guiEvent', None) - if e_type == 'key_press': - self.key_press_event(key, guiEvent=guiEvent) - elif e_type == 'key_release': - self.key_release_event(key, guiEvent=guiEvent) + KeyEvent(event['type'] + '_event', self, + _handle_key(event['key']), *self._last_mouse_xy, + guiEvent=event.get('guiEvent'))._process() handle_key_press = handle_key_release = _handle_key def handle_toolbar_button(self, event): @@ -311,8 +333,8 @@ def handle_refresh(self, event): self.draw_idle() def handle_resize(self, event): - x, y = event.get('width', 800), event.get('height', 800) - x, y = int(x) * self._dpi_ratio, int(y) * self._dpi_ratio + x = int(event.get('width', 800)) * self.device_pixel_ratio + y = int(event.get('height', 800)) * self.device_pixel_ratio fig = self.figure # An attempt at approximating the figure size in pixels. fig.set_size_inches(x / fig.dpi, y / fig.dpi, forward=False) @@ -321,20 +343,22 @@ def handle_resize(self, event): # identical or within a pixel or so). self._png_is_old = True self.manager.resize(*fig.bbox.size, forward=False) - self.resize_event() + ResizeEvent('resize_event', self)._process() + self.draw_idle() def handle_send_image_mode(self, event): # The client requests notification of what the current image mode is. self.send_event('image_mode', mode=self._current_image_mode) + def handle_set_device_pixel_ratio(self, event): + self._handle_set_device_pixel_ratio(event.get('device_pixel_ratio', 1)) + def handle_set_dpi_ratio(self, event): - dpi_ratio = event.get('dpi_ratio', 1) - if dpi_ratio != self._dpi_ratio: - # We don't want to scale up the figure dpi more than once. - if not hasattr(self.figure, '_original_dpi'): - self.figure._original_dpi = self.figure.dpi - self.figure.dpi = dpi_ratio * self.figure._original_dpi - self._dpi_ratio = dpi_ratio + # This handler is for backwards-compatibility with older ipympl. + self._handle_set_device_pixel_ratio(event.get('dpi_ratio', 1)) + + def _handle_set_device_pixel_ratio(self, device_pixel_ratio): + if self._set_device_pixel_ratio(device_pixel_ratio): self._force_full = True self.draw_idle() @@ -365,9 +389,11 @@ class NavigationToolbar2WebAgg(backend_bases.NavigationToolbar2): if name_of_method in _ALLOWED_TOOL_ITEMS ] + cursor = _api.deprecate_privatize_attribute("3.5") + def __init__(self, canvas): self.message = '' - self.cursor = 0 + self._cursor = None # Remove with deprecation. super().__init__(canvas) def set_message(self, message): @@ -375,19 +401,11 @@ def set_message(self, message): self.canvas.send_event("message", message=message) self.message = message - def set_cursor(self, cursor): - if cursor != self.cursor: - self.canvas.send_event("cursor", cursor=cursor) - self.cursor = cursor - def draw_rubberband(self, event, x0, y0, x1, y1): - self.canvas.send_event( - "rubberband", x0=x0, y0=y0, x1=x1, y1=y1) + self.canvas.send_event("rubberband", x0=x0, y0=y0, x1=x1, y1=y1) - def release_zoom(self, event): - super().release_zoom(event) - self.canvas.send_event( - "rubberband", x0=-1, y0=-1, x1=-1, y1=-1) + def remove_rubberband(self): + self.canvas.send_event("rubberband", x0=-1, y0=-1, x1=-1, y1=-1) def save_figure(self, *args): """Save the current figure""" @@ -409,24 +427,22 @@ def set_history_buttons(self): class FigureManagerWebAgg(backend_bases.FigureManagerBase): + # This must be None to not break ipympl + _toolbar2_class = None ToolbarCls = NavigationToolbar2WebAgg def __init__(self, canvas, num): self.web_sockets = set() super().__init__(canvas, num) - self.toolbar = self._get_toolbar(canvas) def show(self): pass - def _get_toolbar(self, canvas): - toolbar = self.ToolbarCls(canvas) - return toolbar - def resize(self, w, h, forward=True): self._send_event( 'resize', - size=(w / self.canvas._dpi_ratio, h / self.canvas._dpi_ratio), + size=(w / self.canvas.device_pixel_ratio, + h / self.canvas.device_pixel_ratio), forward=forward) def set_window_title(self, title): diff --git a/lib/matplotlib/backends/backend_wx.py b/lib/matplotlib/backends/backend_wx.py index 9d12357d2523..4d88547b865d 100644 --- a/lib/matplotlib/backends/backend_wx.py +++ b/lib/matplotlib/backends/backend_wx.py @@ -7,6 +7,7 @@ Copyright (C) Jeremy O'Donoghue & John Hunter, 2003-4. """ +import functools import logging import math import pathlib @@ -18,46 +19,40 @@ import matplotlib as mpl from matplotlib.backend_bases import ( - _Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase, + _Backend, FigureCanvasBase, FigureManagerBase, GraphicsContextBase, MouseButton, NavigationToolbar2, RendererBase, - StatusbarBase, TimerBase, ToolContainerBase, cursors) + TimerBase, ToolContainerBase, cursors, + CloseEvent, KeyEvent, LocationEvent, MouseEvent, ResizeEvent) from matplotlib import _api, cbook, backend_tools from matplotlib._pylab_helpers import Gcf -from matplotlib.backend_managers import ToolManager -from matplotlib.figure import Figure from matplotlib.path import Path from matplotlib.transforms import Affine2D -from matplotlib.widgets import SubplotTool import wx _log = logging.getLogger(__name__) -# Debugging settings here... -# Debug level set here. If the debug level is less than 5, information -# messages (progressively more info for lower value) are printed. In addition, -# traceback is performed, and pdb activated, for all uncaught exceptions in -# this case -_DEBUG = 5 -_DEBUG_lvls = {1: 'Low ', 2: 'Med ', 3: 'High', 4: 'Error'} - - -@_api.deprecated("3.3") -def DEBUG_MSG(string, lvl=3, o=None): - if lvl >= _DEBUG: - print(f"{_DEBUG_lvls[lvl]}- {string} in {type(o)}") - - # the True dots per inch on the screen; should be display dependent; see -# http://groups.google.com/groups?q=screen+dpi+x11&hl=en&lr=&ie=UTF-8&oe=UTF-8&safe=off&selm=7077.26e81ad5%40swift.cs.tcd.ie&rnum=5 +# http://groups.google.com/d/msg/comp.lang.postscript/-/omHAc9FEuAsJ?hl=en # for some info about screen dpi PIXELS_PER_INCH = 75 -# Delay time for idle checks -IDLE_DELAY = 5 # Documented as deprecated as of Matplotlib 3.1. + +@_api.caching_module_getattr # module-level deprecations +class __getattr__: + cursord = _api.deprecated("3.5", obj_type="")(property(lambda self: { + cursors.MOVE: wx.CURSOR_HAND, + cursors.HAND: wx.CURSOR_HAND, + cursors.POINTER: wx.CURSOR_ARROW, + cursors.SELECT_REGION: wx.CURSOR_CROSS, + cursors.WAIT: wx.CURSOR_WAIT, + cursors.RESIZE_HORIZONTAL: wx.CURSOR_SIZEWE, + cursors.RESIZE_VERTICAL: wx.CURSOR_SIZENS, + })) +@_api.deprecated("3.6") def error_msg_wx(msg, parent=None): """Signal an error condition with a popup error dialog.""" dialog = wx.MessageDialog(parent=parent, @@ -69,6 +64,15 @@ def error_msg_wx(msg, parent=None): return None +# lru_cache holds a reference to the App and prevents it from being gc'ed. +@functools.lru_cache(1) +def _create_wxapp(): + wxapp = wx.App(False) + wxapp.SetExitOnFrameDelete(True) + cbook._setup_new_guiapp() + return wxapp + + class TimerWx(TimerBase): """Subclass of `.TimerBase` using wx.Timer events.""" @@ -88,6 +92,10 @@ def _timer_set_interval(self): self._timer_start() # Restart with new interval. +@_api.deprecated( + "2.0", name="wx", obj_type="backend", removal="the future", + alternative="wxagg", + addendum="See the Matplotlib usage FAQ for more info on backends.") class RendererWx(RendererBase): """ The renderer handles all the drawing primitives using a graphics @@ -129,7 +137,7 @@ class RendererWx(RendererBase): # wxPython allows for portable font styles, choosing them appropriately for # the target platform. Map some standard font names to the portable styles. - # QUESTION: Is it be wise to agree standard fontnames across all backends? + # QUESTION: Is it wise to agree to standard fontnames across all backends? fontnames = { 'Sans': wx.FONTFAMILY_SWISS, 'Roman': wx.FONTFAMILY_ROMAN, @@ -142,10 +150,6 @@ class RendererWx(RendererBase): def __init__(self, bitmap, dpi): """Initialise a wxWindows renderer instance.""" - _api.warn_deprecated( - "2.0", name="wx", obj_type="backend", removal="the future", - alternative="wxagg", addendum="See the Matplotlib usage FAQ for " - "more info on backends.") super().__init__() _log.debug("%s - __init__()", type(self)) self.width = bitmap.GetWidth() @@ -159,6 +163,7 @@ def flipy(self): # docstring inherited return True + @_api.deprecated("3.6") def offset_text_height(self): return True @@ -280,16 +285,6 @@ def new_gc(self): self.gc.unselect() return self.gc - @_api.deprecated("3.3", alternative=".gc") - def get_gc(self): - """ - Fetch the locally cached gc. - """ - # This is a dirty hack to allow anything with access to a renderer to - # access the current graphics context - assert self.gc is not None, "gc must be defined" - return self.gc - def get_wx_font(self, s, prop): """Return a wx font. Cache font instances for efficiency.""" _log.debug("%s - get_wx_font()", type(self)) @@ -314,7 +309,7 @@ def points_to_pixels(self, points): class GraphicsContextWx(GraphicsContextBase): """ - The graphics context provides the color, line styles, etc... + The graphics context provides the color, line styles, etc. This class stores a reference to a wxMemoryDC, and a wxGraphicsContext that draws to it. Creating a wxGraphicsContext @@ -343,8 +338,7 @@ def __init__(self, bitmap, renderer): dc, gfx_ctx = self._cache.get(bitmap, (None, None)) if dc is None: - dc = wx.MemoryDC() - dc.SelectObject(bitmap) + dc = wx.MemoryDC(bitmap) gfx_ctx = wx.GraphicsContext.Create(dc) gfx_ctx._lastcliprect = None self._cache[bitmap] = dc, gfx_ctx @@ -434,6 +428,7 @@ class _FigureCanvasWxBase(FigureCanvasBase, wx.Panel): required_interactive_framework = "wx" _timer_cls = TimerWx + manager_class = _api.classproperty(lambda cls: FigureManagerWx) keyvald = { wx.WXK_CONTROL: 'control', @@ -513,27 +508,35 @@ def __init__(self, parent, id, figure=None): _log.debug("%s - __init__() - bitmap w:%d h:%d", type(self), w, h) self._isDrawn = False self._rubberband_rect = None - - self.Bind(wx.EVT_SIZE, self._onSize) - self.Bind(wx.EVT_PAINT, self._onPaint) - self.Bind(wx.EVT_CHAR_HOOK, self._onKeyDown) - self.Bind(wx.EVT_KEY_UP, self._onKeyUp) - self.Bind(wx.EVT_LEFT_DOWN, self._onMouseButton) - self.Bind(wx.EVT_LEFT_DCLICK, self._onMouseButton) - self.Bind(wx.EVT_LEFT_UP, self._onMouseButton) - self.Bind(wx.EVT_MIDDLE_DOWN, self._onMouseButton) - self.Bind(wx.EVT_MIDDLE_DCLICK, self._onMouseButton) - self.Bind(wx.EVT_MIDDLE_UP, self._onMouseButton) - self.Bind(wx.EVT_RIGHT_DOWN, self._onMouseButton) - self.Bind(wx.EVT_RIGHT_DCLICK, self._onMouseButton) - self.Bind(wx.EVT_RIGHT_UP, self._onMouseButton) - self.Bind(wx.EVT_MOUSEWHEEL, self._onMouseWheel) - self.Bind(wx.EVT_MOTION, self._onMotion) - self.Bind(wx.EVT_LEAVE_WINDOW, self._onLeave) - self.Bind(wx.EVT_ENTER_WINDOW, self._onEnter) - - self.Bind(wx.EVT_MOUSE_CAPTURE_CHANGED, self._onCaptureLost) - self.Bind(wx.EVT_MOUSE_CAPTURE_LOST, self._onCaptureLost) + self._rubberband_pen_black = wx.Pen('BLACK', 1, wx.PENSTYLE_SHORT_DASH) + self._rubberband_pen_white = wx.Pen('WHITE', 1, wx.PENSTYLE_SOLID) + + self.Bind(wx.EVT_SIZE, self._on_size) + self.Bind(wx.EVT_PAINT, self._on_paint) + self.Bind(wx.EVT_CHAR_HOOK, self._on_key_down) + self.Bind(wx.EVT_KEY_UP, self._on_key_up) + self.Bind(wx.EVT_LEFT_DOWN, self._on_mouse_button) + self.Bind(wx.EVT_LEFT_DCLICK, self._on_mouse_button) + self.Bind(wx.EVT_LEFT_UP, self._on_mouse_button) + self.Bind(wx.EVT_MIDDLE_DOWN, self._on_mouse_button) + self.Bind(wx.EVT_MIDDLE_DCLICK, self._on_mouse_button) + self.Bind(wx.EVT_MIDDLE_UP, self._on_mouse_button) + self.Bind(wx.EVT_RIGHT_DOWN, self._on_mouse_button) + self.Bind(wx.EVT_RIGHT_DCLICK, self._on_mouse_button) + self.Bind(wx.EVT_RIGHT_UP, self._on_mouse_button) + self.Bind(wx.EVT_MOUSE_AUX1_DOWN, self._on_mouse_button) + self.Bind(wx.EVT_MOUSE_AUX1_UP, self._on_mouse_button) + self.Bind(wx.EVT_MOUSE_AUX2_DOWN, self._on_mouse_button) + self.Bind(wx.EVT_MOUSE_AUX2_UP, self._on_mouse_button) + self.Bind(wx.EVT_MOUSE_AUX1_DCLICK, self._on_mouse_button) + self.Bind(wx.EVT_MOUSE_AUX2_DCLICK, self._on_mouse_button) + self.Bind(wx.EVT_MOUSEWHEEL, self._on_mouse_wheel) + self.Bind(wx.EVT_MOTION, self._on_motion) + self.Bind(wx.EVT_ENTER_WINDOW, self._on_enter) + self.Bind(wx.EVT_LEAVE_WINDOW, self._on_leave) + + self.Bind(wx.EVT_MOUSE_CAPTURE_CHANGED, self._on_capture_lost) + self.Bind(wx.EVT_MOUSE_CAPTURE_LOST, self._on_capture_lost) self.SetBackgroundStyle(wx.BG_STYLE_PAINT) # Reduce flicker. self.SetBackgroundColour(wx.WHITE) @@ -601,11 +604,10 @@ def _get_imagesave_wildcards(self): wildcards = '|'.join(wildcards) return wildcards, extensions, filter_index - @_api.delete_parameter("3.4", "origin") - def gui_repaint(self, drawDC=None, origin='WX'): + def gui_repaint(self, drawDC=None): """ - Performs update of the displayed image on the GUI canvas, using the - supplied wx.PaintDC device context. + Update the displayed image on the GUI canvas, using the supplied + wx.PaintDC device context. The 'WXAgg' backend sets origin accordingly. """ @@ -620,15 +622,16 @@ def gui_repaint(self, drawDC=None, origin='WX'): # DC (see GraphicsContextWx._cache). bmp = (self.bitmap.ConvertToImage().ConvertToBitmap() if wx.Platform == '__WXMSW__' - and isinstance(self.figure._cachedRenderer, RendererWx) + and isinstance(self.figure.canvas.get_renderer(), RendererWx) else self.bitmap) drawDC.DrawBitmap(bmp, 0, 0) if self._rubberband_rect is not None: - x0, y0, x1, y1 = self._rubberband_rect - drawDC.DrawLineList( - [(x0, y0, x1, y0), (x1, y0, x1, y1), - (x0, y0, x0, y1), (x0, y1, x1, y1)], - wx.Pen('BLACK', 1, wx.PENSTYLE_SHORT_DASH)) + # Some versions of wx+python don't support numpy.float64 here. + x0, y0, x1, y1 = map(round, self._rubberband_rect) + rect = [(x0, y0, x1, y0), (x1, y0, x1, y1), + (x0, y0, x0, y1), (x0, y1, x1, y1)] + drawDC.DrawLineList(rect, self._rubberband_pen_white) + drawDC.DrawLineList(rect, self._rubberband_pen_black) filetypes = { **FigureCanvasBase.filetypes, @@ -651,9 +654,9 @@ def print_figure(self, filename, *args, **kwargs): if self._isDrawn: self.draw() - def _onPaint(self, event): + def _on_paint(self, event): """Called when wxPaintEvt is generated.""" - _log.debug("%s - _onPaint()", type(self)) + _log.debug("%s - _on_paint()", type(self)) drawDC = wx.PaintDC(self) if not self._isDrawn: self.draw(drawDC=drawDC) @@ -661,7 +664,7 @@ def _onPaint(self, event): self.gui_repaint(drawDC=drawDC) drawDC.Destroy() - def _onSize(self, event): + def _on_size(self, event): """ Called when wxEventSize is generated. @@ -669,7 +672,7 @@ def _onSize(self, event): is better to take the performance hit and redraw the whole window. """ - _log.debug("%s - _onSize()", type(self)) + _log.debug("%s - _on_size()", type(self)) sz = self.GetParent().GetSizer() if sz: si = sz.GetItem(self) @@ -703,7 +706,8 @@ def _onSize(self, event): # so no need to do anything here except to make sure # the whole background is repainted. self.Refresh(eraseBackground=False) - FigureCanvasBase.resize_event(self) + ResizeEvent("resize_event", self)._process() + self.draw_idle() def _get_key(self, event): @@ -719,30 +723,60 @@ def _get_key(self, event): else: key = None - for meth, prefix, key_name in ( - [event.ControlDown, 'ctrl', 'control'], - [event.AltDown, 'alt', 'alt'], - [event.ShiftDown, 'shift', 'shift'],): + for meth, prefix, key_name in [ + (event.ControlDown, 'ctrl', 'control'), + (event.AltDown, 'alt', 'alt'), + (event.ShiftDown, 'shift', 'shift'), + ]: if meth() and key_name != key: if not (key_name == 'shift' and key.isupper()): key = '{0}+{1}'.format(prefix, key) return key - def _onKeyDown(self, event): + def _mpl_coords(self, pos=None): + """ + Convert a wx position, defaulting to the current cursor position, to + Matplotlib coordinates. + """ + if pos is None: + pos = wx.GetMouseState() + x, y = self.ScreenToClient(pos.X, pos.Y) + else: + x, y = pos.X, pos.Y + # flip y so y=0 is bottom of canvas + return x, self.figure.bbox.height - y + + def _on_key_down(self, event): """Capture key press.""" - key = self._get_key(event) - FigureCanvasBase.key_press_event(self, key, guiEvent=event) + KeyEvent("key_press_event", self, + self._get_key(event), *self._mpl_coords(), + guiEvent=event)._process() if self: event.Skip() - def _onKeyUp(self, event): + def _on_key_up(self, event): """Release key.""" - key = self._get_key(event) - FigureCanvasBase.key_release_event(self, key, guiEvent=event) + KeyEvent("key_release_event", self, + self._get_key(event), *self._mpl_coords(), + guiEvent=event)._process() if self: event.Skip() + def set_cursor(self, cursor): + # docstring inherited + cursor = wx.Cursor(_api.check_getitem({ + cursors.MOVE: wx.CURSOR_HAND, + cursors.HAND: wx.CURSOR_HAND, + cursors.POINTER: wx.CURSOR_ARROW, + cursors.SELECT_REGION: wx.CURSOR_CROSS, + cursors.WAIT: wx.CURSOR_WAIT, + cursors.RESIZE_HORIZONTAL: wx.CURSOR_SIZEWE, + cursors.RESIZE_VERTICAL: wx.CURSOR_SIZENS, + }, cursor=cursor)) + self.SetCursor(cursor) + self.Refresh() + def _set_capture(self, capture=True): """Control wx mouse capture.""" if self.HasCapture(): @@ -750,36 +784,37 @@ def _set_capture(self, capture=True): if capture: self.CaptureMouse() - def _onCaptureLost(self, event): + def _on_capture_lost(self, event): """Capture changed or lost""" self._set_capture(False) - def _onMouseButton(self, event): + def _on_mouse_button(self, event): """Start measuring on an axis.""" event.Skip() self._set_capture(event.ButtonDown() or event.ButtonDClick()) - x = event.X - y = self.figure.bbox.height - event.Y + x, y = self._mpl_coords(event) button_map = { wx.MOUSE_BTN_LEFT: MouseButton.LEFT, wx.MOUSE_BTN_MIDDLE: MouseButton.MIDDLE, wx.MOUSE_BTN_RIGHT: MouseButton.RIGHT, + wx.MOUSE_BTN_AUX1: MouseButton.BACK, + wx.MOUSE_BTN_AUX2: MouseButton.FORWARD, } button = event.GetButton() button = button_map.get(button, button) if event.ButtonDown(): - self.button_press_event(x, y, button, guiEvent=event) + MouseEvent("button_press_event", self, + x, y, button, guiEvent=event)._process() elif event.ButtonDClick(): - self.button_press_event(x, y, button, dblclick=True, - guiEvent=event) + MouseEvent("button_press_event", self, + x, y, button, dblclick=True, guiEvent=event)._process() elif event.ButtonUp(): - self.button_release_event(x, y, button, guiEvent=event) + MouseEvent("button_release_event", self, + x, y, button, guiEvent=event)._process() - def _onMouseWheel(self, event): + def _on_mouse_wheel(self, event): """Translate mouse wheel events into matplotlib events""" - # Determine mouse location - x = event.GetX() - y = self.figure.bbox.height - event.GetY() + x, y = self._mpl_coords(event) # Convert delta/rotation/rate into a floating point step size step = event.LinesPerAction * event.WheelRotation / event.WheelDelta # Done handling event @@ -793,26 +828,29 @@ def _onMouseWheel(self, event): return # Return without processing event else: self._skipwheelevent = True - FigureCanvasBase.scroll_event(self, x, y, step, guiEvent=event) + MouseEvent("scroll_event", self, + x, y, step=step, guiEvent=event)._process() - def _onMotion(self, event): + def _on_motion(self, event): """Start measuring on an axis.""" - x = event.GetX() - y = self.figure.bbox.height - event.GetY() event.Skip() - FigureCanvasBase.motion_notify_event(self, x, y, guiEvent=event) + MouseEvent("motion_notify_event", self, + *self._mpl_coords(event), + guiEvent=event)._process() - def _onLeave(self, event): - """Mouse has left the window.""" + def _on_enter(self, event): + """Mouse has entered the window.""" event.Skip() - FigureCanvasBase.leave_notify_event(self, guiEvent=event) + LocationEvent("figure_enter_event", self, + *self._mpl_coords(event), + guiEvent=event)._process() - def _onEnter(self, event): - """Mouse has entered the window.""" - x = event.GetX() - y = self.figure.bbox.height - event.GetY() + def _on_leave(self, event): + """Mouse has left the window.""" event.Skip() - FigureCanvasBase.enter_notify_event(self, guiEvent=event, xy=(x, y)) + LocationEvent("figure_leave_event", self, + *self._mpl_coords(event), + guiEvent=event)._process() class FigureCanvasWx(_FigureCanvasWxBase): @@ -829,67 +867,19 @@ def draw(self, drawDC=None): self._isDrawn = True self.gui_repaint(drawDC=drawDC) - def print_bmp(self, filename, *args, **kwargs): - return self._print_image(filename, wx.BITMAP_TYPE_BMP, *args, **kwargs) - - def print_jpeg(self, filename, *args, **kwargs): - return self._print_image(filename, wx.BITMAP_TYPE_JPEG, - *args, **kwargs) - print_jpg = print_jpeg - - def print_pcx(self, filename, *args, **kwargs): - return self._print_image(filename, wx.BITMAP_TYPE_PCX, *args, **kwargs) - - def print_png(self, filename, *args, **kwargs): - return self._print_image(filename, wx.BITMAP_TYPE_PNG, *args, **kwargs) - - def print_tiff(self, filename, *args, **kwargs): - return self._print_image(filename, wx.BITMAP_TYPE_TIF, *args, **kwargs) - print_tif = print_tiff - - def print_xpm(self, filename, *args, **kwargs): - return self._print_image(filename, wx.BITMAP_TYPE_XPM, *args, **kwargs) - - @_check_savefig_extra_args - def _print_image(self, filename, filetype, *, quality=None): - origBitmap = self.bitmap - - self.bitmap = wx.Bitmap(math.ceil(self.figure.bbox.width), - math.ceil(self.figure.bbox.height)) - renderer = RendererWx(self.bitmap, self.figure.dpi) - - gc = renderer.new_gc() - self.figure.draw(renderer) - - # image is the object that we call SaveFile on. - image = self.bitmap - # set the JPEG quality appropriately. Unfortunately, it is only - # possible to set the quality on a wx.Image object. So if we - # are saving a JPEG, convert the wx.Bitmap to a wx.Image, - # and set the quality. - if filetype == wx.BITMAP_TYPE_JPEG: - if quality is None: - quality = dict.__getitem__(mpl.rcParams, - 'savefig.jpeg_quality') - image = self.bitmap.ConvertToImage() - image.SetOption(wx.IMAGE_OPTION_QUALITY, str(quality)) - - # Now that we have rendered into the bitmap, save it to the appropriate - # file type and clean up. - if (cbook.is_writable_file_like(filename) and - not isinstance(image, wx.Image)): - image = image.ConvertToImage() - if not image.SaveFile(filename, filetype): + def _print_image(self, filetype, filename): + bitmap = wx.Bitmap(math.ceil(self.figure.bbox.width), + math.ceil(self.figure.bbox.height)) + self.figure.draw(RendererWx(bitmap, self.figure.dpi)) + saved_obj = (bitmap.ConvertToImage() + if cbook.is_writable_file_like(filename) + else bitmap) + if not saved_obj.SaveFile(filename, filetype): raise RuntimeError(f'Could not save figure to {filename}') - - # Restore everything to normal - self.bitmap = origBitmap - - # Note: draw is required here since bits of state about the - # last renderer are strewn about the artist draw methods. Do - # not remove the draw without first verifying that these have - # been cleaned up. The artist contains() methods will fail - # otherwise. + # draw() is required here since bits of state about the last renderer + # are strewn about the artist draw methods. Do not remove the draw + # without first verifying that these have been cleaned up. The artist + # contains() methods will fail otherwise. if self._isDrawn: self.draw() # The "if self" check avoids a "wrapped C/C++ object has been deleted" @@ -897,9 +887,22 @@ def _print_image(self, filename, filetype, *, quality=None): if self: self.Refresh() + print_bmp = functools.partialmethod( + _print_image, wx.BITMAP_TYPE_BMP) + print_jpeg = print_jpg = functools.partialmethod( + _print_image, wx.BITMAP_TYPE_JPEG) + print_pcx = functools.partialmethod( + _print_image, wx.BITMAP_TYPE_PCX) + print_png = functools.partialmethod( + _print_image, wx.BITMAP_TYPE_PNG) + print_tiff = print_tif = functools.partialmethod( + _print_image, wx.BITMAP_TYPE_TIF) + print_xpm = functools.partialmethod( + _print_image, wx.BITMAP_TYPE_XPM) + class FigureFrameWx(wx.Frame): - def __init__(self, num, fig): + def __init__(self, num, fig, *, canvas_class=None): # On non-Windows platform, explicitly set the position - fix # positioning bug on some Linux platforms if wx.Platform == '__WXMSW__': @@ -909,95 +912,74 @@ def __init__(self, num, fig): super().__init__(parent=None, id=-1, pos=pos) # Frame will be sized later by the Fit method _log.debug("%s - __init__()", type(self)) - self.num = num _set_frame_icon(self) - self.canvas = self.get_canvas(fig) - w, h = map(math.ceil, fig.bbox.size) - self.canvas.SetInitialSize(wx.Size(w, h)) - self.canvas.SetFocus() - self.sizer = wx.BoxSizer(wx.VERTICAL) - self.sizer.Add(self.canvas, 1, wx.TOP | wx.LEFT | wx.EXPAND) - # By adding toolbar in sizer, we are able to put it at the bottom - # of the frame - so appearance is closer to GTK version - - self.figmgr = FigureManagerWx(self.canvas, num, self) - - self.toolbar = self._get_toolbar() - - if self.figmgr.toolmanager: - backend_tools.add_tools_to_manager(self.figmgr.toolmanager) - if self.toolbar: - backend_tools.add_tools_to_container(self.toolbar) - - if self.toolbar is not None: - self.toolbar.Realize() - # On Windows platform, default window size is incorrect, so set - # toolbar width to figure width. - tw, th = self.toolbar.GetSize() - fw, fh = self.canvas.GetSize() - # By adding toolbar in sizer, we are able to put it at the bottom - # of the frame - so appearance is closer to GTK version. - self.toolbar.SetSize(wx.Size(fw, th)) - self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND) - self.SetSizer(self.sizer) - self.Fit() + # The parameter will become required after the deprecation elapses. + if canvas_class is not None: + self.canvas = canvas_class(self, -1, fig) + else: + _api.warn_deprecated( + "3.6", message="The canvas_class parameter will become " + "required after the deprecation period starting in Matplotlib " + "%(since)s elapses.") + self.canvas = self.get_canvas(fig) - self.canvas.SetMinSize((2, 2)) + # Auto-attaches itself to self.canvas.manager + manager = FigureManagerWx(self.canvas, num, self) - self.Bind(wx.EVT_CLOSE, self._onClose) + toolbar = self.canvas.manager.toolbar + if toolbar is not None: + self.SetToolBar(toolbar) - @property - def toolmanager(self): - return self.figmgr.toolmanager + # On Windows, canvas sizing must occur after toolbar addition; + # otherwise the toolbar further resizes the canvas. + w, h = map(math.ceil, fig.bbox.size) + self.canvas.SetInitialSize(wx.Size(w, h)) + self.canvas.SetMinSize((2, 2)) + self.canvas.SetFocus() - def _get_toolbar(self): - if mpl.rcParams['toolbar'] == 'toolbar2': - toolbar = NavigationToolbar2Wx(self.canvas) - elif mpl.rcParams['toolbar'] == 'toolmanager': - toolbar = ToolbarWx(self.toolmanager, self) - else: - toolbar = None - return toolbar + self.Fit() + self.Bind(wx.EVT_CLOSE, self._on_close) + + sizer = _api.deprecated("3.6", alternative="frame.GetSizer()")( + property(lambda self: self.GetSizer())) + figmgr = _api.deprecated("3.6", alternative="frame.canvas.manager")( + property(lambda self: self.canvas.manager)) + num = _api.deprecated("3.6", alternative="frame.canvas.manager.num")( + property(lambda self: self.canvas.manager.num)) + toolbar = _api.deprecated("3.6", alternative="frame.GetToolBar()")( + property(lambda self: self.GetToolBar())) + toolmanager = _api.deprecated( + "3.6", alternative="frame.canvas.manager.toolmanager")( + property(lambda self: self.canvas.manager.toolmanager)) + + @_api.deprecated( + "3.6", alternative="the canvas_class constructor parameter") def get_canvas(self, fig): return FigureCanvasWx(self, -1, fig) + @_api.deprecated("3.6", alternative="frame.canvas.manager") def get_figure_manager(self): _log.debug("%s - get_figure_manager()", type(self)) - return self.figmgr + return self.canvas.manager - def _onClose(self, event): - _log.debug("%s - onClose()", type(self)) - self.canvas.close_event() + def _on_close(self, event): + _log.debug("%s - on_close()", type(self)) + CloseEvent("close_event", self.canvas)._process() self.canvas.stop_event_loop() # set FigureManagerWx.frame to None to prevent repeated attempts to # close this frame from FigureManagerWx.destroy() - self.figmgr.frame = None + self.canvas.manager.frame = None # remove figure manager from Gcf.figs - Gcf.destroy(self.figmgr) + Gcf.destroy(self.canvas.manager) + try: # See issue 2941338. + self.canvas.mpl_disconnect(self.canvas.toolbar._id_drag) + except AttributeError: # If there's no toolbar. + pass # Carry on with close event propagation, frame & children destruction event.Skip() - def GetToolBar(self): - """Override wxFrame::GetToolBar as we don't have managed toolbar""" - return self.toolbar - - def Destroy(self, *args, **kwargs): - try: - self.canvas.mpl_disconnect(self.toolbar._id_drag) - # Rationale for line above: see issue 2941338. - except AttributeError: - pass # classic toolbar lacks the attribute - # The "if self" check avoids a "wrapped C/C++ object has been deleted" - # RuntimeError at exit with e.g. - # MPLBACKEND=wxagg python -c 'from pylab import *; plot()'. - if self and not self.IsBeingDeleted(): - super().Destroy(*args, **kwargs) - # self.toolbar.Destroy() should not be necessary if the close event - # is allowed to propagate. - return True - class FigureManagerWx(FigureManagerBase): """ @@ -1017,20 +999,18 @@ class FigureManagerWx(FigureManagerBase): def __init__(self, canvas, num, frame): _log.debug("%s - __init__()", type(self)) self.frame = self.window = frame - self._initializing = True super().__init__(canvas, num) - self._initializing = False - @property - def toolbar(self): - return self.frame.GetToolBar() - - @toolbar.setter - def toolbar(self, value): - # Never allow this, except that base class inits this to None before - # the frame is set up. - if not self._initializing: - raise AttributeError("can't set attribute") + @classmethod + def create_with_canvas(cls, canvas_class, figure, num): + # docstring inherited + wxapp = wx.GetApp() or _create_wxapp() + frame = FigureFrameWx(num, figure, canvas_class=canvas_class) + manager = figure.canvas.manager + if mpl.is_interactive(): + manager.frame.Show() + figure.canvas.draw_idle() + return manager def show(self): # docstring inherited @@ -1062,9 +1042,9 @@ def set_window_title(self, title): def resize(self, width, height): # docstring inherited - self.canvas.SetInitialSize( - wx.Size(math.ceil(width), math.ceil(height))) - self.window.GetSizer().Fit(self.window) + # Directly using SetClientSize doesn't handle the toolbar on Windows. + self.window.SetSize(self.window.ClientToWindowSize(wx.Size( + math.ceil(width), math.ceil(height)))) def _load_bitmap(filename): @@ -1085,18 +1065,9 @@ def _set_frame_icon(frame): frame.SetIcons(bundle) -cursord = { - cursors.MOVE: wx.CURSOR_HAND, - cursors.HAND: wx.CURSOR_HAND, - cursors.POINTER: wx.CURSOR_ARROW, - cursors.SELECT_REGION: wx.CURSOR_CROSS, - cursors.WAIT: wx.CURSOR_WAIT, -} - - class NavigationToolbar2Wx(NavigationToolbar2, wx.ToolBar): - def __init__(self, canvas, coordinates=True): - wx.ToolBar.__init__(self, canvas.GetParent(), -1) + def __init__(self, canvas, coordinates=True, *, style=wx.TB_BOTTOM): + wx.ToolBar.__init__(self, canvas.GetParent(), -1, style=style) if 'wxMac' in wx.PlatformInfo: self.SetToolBitmapSize((24, 24)) @@ -1120,28 +1091,13 @@ def __init__(self, canvas, coordinates=True): self._coordinates = coordinates if self._coordinates: self.AddStretchableSpace() - self._label_text = wx.StaticText(self) + self._label_text = wx.StaticText(self, style=wx.ALIGN_RIGHT) self.AddControl(self._label_text) self.Realize() NavigationToolbar2.__init__(self, canvas) - self._prevZoomRect = None - # for now, use alternate zoom-rectangle drawing on all - # Macs. N.B. In future versions of wx it may be possible to - # detect Retina displays with window.GetContentScaleFactor() - # and/or dc.GetContentScaleFactor() - self._retinaFix = 'wxMac' in wx.PlatformInfo - - prevZoomRect = _api.deprecate_privatize_attribute("3.3") - retinaFix = _api.deprecate_privatize_attribute("3.3") - savedRetinaImage = _api.deprecate_privatize_attribute("3.3") - wxoverlay = _api.deprecate_privatize_attribute("3.3") - zoomAxes = _api.deprecate_privatize_attribute("3.3") - zoomStartX = _api.deprecate_privatize_attribute("3.3") - zoomStartY = _api.deprecate_privatize_attribute("3.3") - @staticmethod def _icon(name): """ @@ -1167,33 +1123,33 @@ def _icon(name): return wx.Bitmap.FromBufferRGBA( image.shape[1], image.shape[0], image.tobytes()) - @_api.deprecated("3.4") - def get_canvas(self, frame, fig): - return type(self.canvas)(frame, -1, fig) + def _update_buttons_checked(self): + if "Pan" in self.wx_ids: + self.ToggleTool(self.wx_ids["Pan"], self.mode.name == "PAN") + if "Zoom" in self.wx_ids: + self.ToggleTool(self.wx_ids["Zoom"], self.mode.name == "ZOOM") def zoom(self, *args): - tool = self.wx_ids['Zoom'] - self.ToggleTool(tool, not self.GetToolState(tool)) super().zoom(*args) + self._update_buttons_checked() def pan(self, *args): - tool = self.wx_ids['Pan'] - self.ToggleTool(tool, not self.GetToolState(tool)) super().pan(*args) + self._update_buttons_checked() def save_figure(self, *args): # Fetch the required filename and file type. filetypes, exts, filter_index = self.canvas._get_imagesave_wildcards() default_file = self.canvas.get_default_filename() - dlg = wx.FileDialog( + dialog = wx.FileDialog( self.canvas.GetParent(), "Save to file", mpl.rcParams["savefig.directory"], default_file, filetypes, wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) - dlg.SetFilterIndex(filter_index) - if dlg.ShowModal() == wx.ID_OK: - path = pathlib.Path(dlg.GetPath()) + dialog.SetFilterIndex(filter_index) + if dialog.ShowModal() == wx.ID_OK: + path = pathlib.Path(dialog.GetPath()) _log.debug('%s - Save file path: %s', type(self), path) - fmt = exts[dlg.GetFilterIndex()] + fmt = exts[dialog.GetFilterIndex()] ext = path.suffix[1:] if ext in self.canvas.get_supported_filetypes() and fmt != ext: # looks like they forgot to set the image type drop @@ -1208,41 +1164,11 @@ def save_figure(self, *args): try: self.canvas.figure.savefig(str(path), format=fmt) except Exception as e: - error_msg_wx(str(e)) - - def set_cursor(self, cursor): - cursor = wx.Cursor(cursord[cursor]) - self.canvas.SetCursor(cursor) - self.canvas.Update() - - def press_zoom(self, event): - super().press_zoom(event) - if self.mode.name == 'ZOOM': - if not self._retinaFix: - self._wxoverlay = wx.Overlay() - else: - if event.inaxes is not None: - self._savedRetinaImage = self.canvas.copy_from_bbox( - event.inaxes.bbox) - self._zoomStartX = event.xdata - self._zoomStartY = event.ydata - self._zoomAxes = event.inaxes - - def release_zoom(self, event): - super().release_zoom(event) - if self.mode.name == 'ZOOM': - # When the mouse is released we reset the overlay and it - # restores the former content to the window. - if not self._retinaFix: - self._wxoverlay.Reset() - del self._wxoverlay - else: - del self._savedRetinaImage - if self._prevZoomRect: - self._prevZoomRect.pop(0).remove() - self._prevZoomRect = None - if self._zoomAxes: - self._zoomAxes = None + dialog = wx.MessageDialog( + parent=self.canvas.GetParent(), message=str(e), + caption='Matplotlib error') + dialog.ShowModal() + dialog.Destroy() def draw_rubberband(self, event, x0, y0, x1, y1): height = self.canvas.figure.bbox.height @@ -1266,29 +1192,16 @@ def set_history_buttons(self): self.EnableTool(self.wx_ids['Forward'], can_forward) -@_api.deprecated("3.3") -class StatusBarWx(wx.StatusBar): - """ - A status bar is added to _FigureFrame to allow measurements and the - previously selected scroll function to be displayed as a user convenience. - """ - - def __init__(self, parent, *args, **kwargs): - super().__init__(parent, -1) - self.SetFieldsCount(2) - - def set_function(self, string): - self.SetStatusText("%s" % string, 1) - - # tools for matplotlib.backend_managers.ToolManager: class ToolbarWx(ToolContainerBase, wx.ToolBar): - def __init__(self, toolmanager, parent, style=wx.TB_HORIZONTAL): + def __init__(self, toolmanager, parent=None, style=wx.TB_BOTTOM): + if parent is None: + parent = toolmanager.canvas.GetParent() ToolContainerBase.__init__(self, toolmanager) wx.ToolBar.__init__(self, parent, -1, style=style) self._space = self.AddStretchableSpace() - self._label_text = wx.StaticText(self) + self._label_text = wx.StaticText(self, style=wx.ALIGN_RIGHT) self.AddControl(self._label_text) self._toolitems = {} self._groups = {} # Mapping of groups to the separator after them. @@ -1368,37 +1281,27 @@ def set_message(self, s): self._label_text.SetLabel(s) -@_api.deprecated("3.3") -class StatusbarWx(StatusbarBase, wx.StatusBar): - """For use with ToolManager.""" - def __init__(self, parent, *args, **kwargs): - StatusbarBase.__init__(self, *args, **kwargs) - wx.StatusBar.__init__(self, parent, -1) - self.SetFieldsCount(1) - self.SetStatusText("") - - def set_message(self, s): - self.SetStatusText(s) - - +@backend_tools._register_tool_class(_FigureCanvasWxBase) class ConfigureSubplotsWx(backend_tools.ConfigureSubplotsBase): def trigger(self, *args): - NavigationToolbar2Wx.configure_subplots( - self._make_classic_style_pseudo_toolbar()) + NavigationToolbar2Wx.configure_subplots(self) +@backend_tools._register_tool_class(_FigureCanvasWxBase) class SaveFigureWx(backend_tools.SaveFigureBase): def trigger(self, *args): NavigationToolbar2Wx.save_figure( self._make_classic_style_pseudo_toolbar()) +@_api.deprecated("3.5", alternative="ToolSetCursor") class SetCursorWx(backend_tools.SetCursorBase): def set_cursor(self, cursor): NavigationToolbar2Wx.set_cursor( self._make_classic_style_pseudo_toolbar(), cursor) +@backend_tools._register_tool_class(_FigureCanvasWxBase) class RubberbandWx(backend_tools.RubberbandBase): def draw_rubberband(self, x0, y0, x1, y1): NavigationToolbar2Wx.draw_rubberband( @@ -1431,15 +1334,15 @@ def __init__(self, parent, help_entries): grid_sizer.Add(label, 0, 0, 0) # finalize layout, create button sizer.Add(grid_sizer, 0, wx.ALL, 6) - OK = wx.Button(self, wx.ID_OK) - sizer.Add(OK, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 8) + ok = wx.Button(self, wx.ID_OK) + sizer.Add(ok, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 8) self.SetSizer(sizer) sizer.Fit(self) self.Layout() - self.Bind(wx.EVT_CLOSE, self.OnClose) - OK.Bind(wx.EVT_BUTTON, self.OnClose) + self.Bind(wx.EVT_CLOSE, self._on_close) + ok.Bind(wx.EVT_BUTTON, self._on_close) - def OnClose(self, event): + def _on_close(self, event): _HelpDialog._instance = None # remove global reference self.DestroyLater() event.Skip() @@ -1454,12 +1357,14 @@ def show(cls, parent, help_entries): cls._instance.Show() +@backend_tools._register_tool_class(_FigureCanvasWxBase) class HelpWx(backend_tools.ToolHelpBase): def trigger(self, *args): _HelpDialog.show(self.figure.canvas.GetTopLevelParent(), self._get_help_entries()) +@backend_tools._register_tool_class(_FigureCanvasWxBase) class ToolCopyToClipboardWx(backend_tools.ToolCopyToClipboardBase): def trigger(self, *args, **kwargs): if not self.canvas._isDrawn: @@ -1472,41 +1377,14 @@ def trigger(self, *args, **kwargs): wx.TheClipboard.Close() -backend_tools.ToolSaveFigure = SaveFigureWx -backend_tools.ToolConfigureSubplots = ConfigureSubplotsWx -backend_tools.ToolSetCursor = SetCursorWx -backend_tools.ToolRubberband = RubberbandWx -backend_tools.ToolHelp = HelpWx -backend_tools.ToolCopyToClipboard = ToolCopyToClipboardWx +FigureManagerWx._toolbar2_class = NavigationToolbar2Wx +FigureManagerWx._toolmanager_toolbar_class = ToolbarWx @_Backend.export class _BackendWx(_Backend): FigureCanvas = FigureCanvasWx FigureManager = FigureManagerWx - _frame_class = FigureFrameWx - - @classmethod - def new_figure_manager(cls, num, *args, **kwargs): - # Create a wx.App instance if it has not been created so far. - wxapp = wx.GetApp() - if wxapp is None: - wxapp = wx.App(False) - wxapp.SetExitOnFrameDelete(True) - cbook._setup_new_guiapp() - # Retain a reference to the app object so that it does not get - # garbage collected. - _BackendWx._theWxApp = wxapp - return super().new_figure_manager(num, *args, **kwargs) - - @classmethod - def new_figure_manager_given_figure(cls, num, figure): - frame = cls._frame_class(num, figure) - figmgr = frame.get_figure_manager() - if mpl.is_interactive(): - figmgr.frame.Show() - figure.canvas.draw_idle() - return figmgr @staticmethod def mainloop(): diff --git a/lib/matplotlib/backends/backend_wxagg.py b/lib/matplotlib/backends/backend_wxagg.py index 106578e7e14b..ca7f91583766 100644 --- a/lib/matplotlib/backends/backend_wxagg.py +++ b/lib/matplotlib/backends/backend_wxagg.py @@ -1,11 +1,14 @@ import wx +from .. import _api from .backend_agg import FigureCanvasAgg -from .backend_wx import ( - _BackendWx, _FigureCanvasWxBase, FigureFrameWx, +from .backend_wx import _BackendWx, _FigureCanvasWxBase, FigureFrameWx +from .backend_wx import ( # noqa: F401 # pylint: disable=W0611 NavigationToolbar2Wx as NavigationToolbar2WxAgg) +@_api.deprecated( + "3.6", alternative="FigureFrameWx(..., canvas_class=FigureCanvasWxAgg)") class FigureFrameWxAgg(FigureFrameWx): def get_canvas(self, fig): return FigureCanvasWxAgg(self, -1, fig) @@ -27,66 +30,32 @@ def draw(self, drawDC=None): Render the figure using agg. """ FigureCanvasAgg.draw(self) - - self.bitmap = _convert_agg_to_wx_bitmap(self.get_renderer(), None) + self.bitmap = _rgba_to_wx_bitmap(self.get_renderer().buffer_rgba()) self._isDrawn = True self.gui_repaint(drawDC=drawDC) def blit(self, bbox=None): # docstring inherited + bitmap = _rgba_to_wx_bitmap(self.get_renderer().buffer_rgba()) if bbox is None: - self.bitmap = _convert_agg_to_wx_bitmap(self.get_renderer(), None) - self.gui_repaint() - return - - srcBmp = _convert_agg_to_wx_bitmap(self.get_renderer(), None) - srcDC = wx.MemoryDC() - srcDC.SelectObject(srcBmp) - - destDC = wx.MemoryDC() - destDC.SelectObject(self.bitmap) - - x = int(bbox.x0) - y = int(self.bitmap.GetHeight() - bbox.y1) - destDC.Blit(x, y, int(bbox.width), int(bbox.height), srcDC, x, y) - - destDC.SelectObject(wx.NullBitmap) - srcDC.SelectObject(wx.NullBitmap) + self.bitmap = bitmap + else: + srcDC = wx.MemoryDC(bitmap) + destDC = wx.MemoryDC(self.bitmap) + x = int(bbox.x0) + y = int(self.bitmap.GetHeight() - bbox.y1) + destDC.Blit(x, y, int(bbox.width), int(bbox.height), srcDC, x, y) + destDC.SelectObject(wx.NullBitmap) + srcDC.SelectObject(wx.NullBitmap) self.gui_repaint() -def _convert_agg_to_wx_bitmap(agg, bbox): - """ - Convert the region of the agg buffer bounded by bbox to a wx.Bitmap. If - bbox is None, the entire buffer is converted. - Note: agg must be a backend_agg.RendererAgg instance. - """ - if bbox is None: - # agg => rgba buffer -> bitmap - return wx.Bitmap.FromBufferRGBA(int(agg.width), int(agg.height), - agg.buffer_rgba()) - else: - # agg => rgba buffer -> bitmap => clipped bitmap - srcBmp = wx.Bitmap.FromBufferRGBA(int(agg.width), int(agg.height), - agg.buffer_rgba()) - srcDC = wx.MemoryDC() - srcDC.SelectObject(srcBmp) - - destBmp = wx.Bitmap(int(bbox.width), int(bbox.height)) - destDC = wx.MemoryDC() - destDC.SelectObject(destBmp) - - x = int(bbox.x0) - y = int(int(agg.height) - bbox.y1) - destDC.Blit(0, 0, int(bbox.width), int(bbox.height), srcDC, x, y) - - srcDC.SelectObject(wx.NullBitmap) - destDC.SelectObject(wx.NullBitmap) - - return destBmp +def _rgba_to_wx_bitmap(rgba): + """Convert an RGBA buffer to a wx.Bitmap.""" + h, w, _ = rgba.shape + return wx.Bitmap.FromBufferRGBA(w, h, rgba) @_BackendWx.export class _BackendWxAgg(_BackendWx): FigureCanvas = FigureCanvasWxAgg - _frame_class = FigureFrameWxAgg diff --git a/lib/matplotlib/backends/backend_wxcairo.py b/lib/matplotlib/backends/backend_wxcairo.py index 6cb0b9d68414..0416a187d091 100644 --- a/lib/matplotlib/backends/backend_wxcairo.py +++ b/lib/matplotlib/backends/backend_wxcairo.py @@ -1,17 +1,20 @@ import wx.lib.wxcairo as wxcairo -from .backend_cairo import cairo, FigureCanvasCairo, RendererCairo -from .backend_wx import ( - _BackendWx, _FigureCanvasWxBase, FigureFrameWx, +from .. import _api +from .backend_cairo import cairo, FigureCanvasCairo +from .backend_wx import _BackendWx, _FigureCanvasWxBase, FigureFrameWx +from .backend_wx import ( # noqa: F401 # pylint: disable=W0611 NavigationToolbar2Wx as NavigationToolbar2WxCairo) +@_api.deprecated( + "3.6", alternative="FigureFrameWx(..., canvas_class=FigureCanvasWxCairo)") class FigureFrameWxCairo(FigureFrameWx): def get_canvas(self, fig): return FigureCanvasWxCairo(self, -1, fig) -class FigureCanvasWxCairo(_FigureCanvasWxBase, FigureCanvasCairo): +class FigureCanvasWxCairo(FigureCanvasCairo, _FigureCanvasWxBase): """ The FigureCanvas contains the figure and does event handling. @@ -21,20 +24,11 @@ class FigureCanvasWxCairo(_FigureCanvasWxBase, FigureCanvasCairo): we give a hint as to our preferred minimum size. """ - def __init__(self, parent, id, figure): - # _FigureCanvasWxBase should be fixed to have the same signature as - # every other FigureCanvas and use cooperative inheritance, but in the - # meantime the following will make do. - _FigureCanvasWxBase.__init__(self, parent, id, figure) - FigureCanvasCairo.__init__(self, figure) - self._renderer = RendererCairo(self.figure.dpi) - def draw(self, drawDC=None): - width = int(self.figure.bbox.width) - height = int(self.figure.bbox.height) - surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) - self._renderer.set_ctx_from_surface(surface) - self._renderer.set_width_height(width, height) + size = self.figure.bbox.size.astype(int) + surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, *size) + self._renderer.set_context(cairo.Context(surface)) + self._renderer.dpi = self.figure.dpi self.figure.draw(self._renderer) self.bitmap = wxcairo.BitmapFromImageSurface(surface) self._isDrawn = True @@ -44,4 +38,3 @@ def draw(self, drawDC=None): @_BackendWx.export class _BackendWxCairo(_BackendWx): FigureCanvas = FigureCanvasWxCairo - _frame_class = FigureFrameWxCairo diff --git a/lib/matplotlib/backends/qt_compat.py b/lib/matplotlib/backends/qt_compat.py index 21b89125d88e..06db924feea3 100644 --- a/lib/matplotlib/backends/qt_compat.py +++ b/lib/matplotlib/backends/qt_compat.py @@ -2,173 +2,131 @@ Qt binding and backend selector. The selection logic is as follows: -- if any of PyQt5, PySide2, PyQt4 or PySide have already been imported - (checked in that order), use it; +- if any of PyQt6, PySide6, PyQt5, or PySide2 have already been + imported (checked in that order), use it; - otherwise, if the QT_API environment variable (used by Enthought) is set, use - it to determine which binding to use (but do not change the backend based on - it; i.e. if the Qt5Agg backend is requested but QT_API is set to "pyqt4", - then actually use Qt5 with PyQt5 or PySide2 (whichever can be imported); + it to determine which binding to use; - otherwise, use whatever the rcParams indicate. - -Support for PyQt4 is deprecated. """ -from distutils.version import LooseVersion +import functools +import operator import os import platform import sys +import signal +import socket +import contextlib + +from packaging.version import parse as parse_version import matplotlib as mpl from matplotlib import _api +from . import _QT_FORCE_QT5_BINDING +QT_API_PYQT6 = "PyQt6" +QT_API_PYSIDE6 = "PySide6" QT_API_PYQT5 = "PyQt5" QT_API_PYSIDE2 = "PySide2" -QT_API_PYQTv2 = "PyQt4v2" -QT_API_PYSIDE = "PySide" -QT_API_PYQT = "PyQt4" # Use the old sip v1 API (Py3 defaults to v2). QT_API_ENV = os.environ.get("QT_API") if QT_API_ENV is not None: QT_API_ENV = QT_API_ENV.lower() -# Mapping of QT_API_ENV to requested binding. ETS does not support PyQt4v1. -# (https://github.com/enthought/pyface/blob/master/pyface/qt/__init__.py) -_ETS = {"pyqt5": QT_API_PYQT5, "pyside2": QT_API_PYSIDE2, - "pyqt": QT_API_PYQTv2, "pyside": QT_API_PYSIDE, - None: None} +_ETS = { # Mapping of QT_API_ENV to requested binding. + "pyqt6": QT_API_PYQT6, "pyside6": QT_API_PYSIDE6, + "pyqt5": QT_API_PYQT5, "pyside2": QT_API_PYSIDE2, +} # First, check if anything is already imported. -if "PyQt5.QtCore" in sys.modules: +if sys.modules.get("PyQt6.QtCore"): + QT_API = QT_API_PYQT6 +elif sys.modules.get("PySide6.QtCore"): + QT_API = QT_API_PYSIDE6 +elif sys.modules.get("PyQt5.QtCore"): QT_API = QT_API_PYQT5 -elif "PySide2.QtCore" in sys.modules: +elif sys.modules.get("PySide2.QtCore"): QT_API = QT_API_PYSIDE2 -elif "PyQt4.QtCore" in sys.modules: - QT_API = QT_API_PYQTv2 -elif "PySide.QtCore" in sys.modules: - QT_API = QT_API_PYSIDE # Otherwise, check the QT_API environment variable (from Enthought). This can # only override the binding, not the backend (in other words, we check that the -# requested backend actually matches). Use dict.__getitem__ to avoid +# requested backend actually matches). Use _get_backend_or_none to avoid # triggering backend resolution (which can result in a partially but # incompletely imported backend_qt5). -elif dict.__getitem__(mpl.rcParams, "backend") in ["Qt5Agg", "Qt5Cairo"]: +elif (mpl.rcParams._get_backend_or_none() or "").lower().startswith("qt5"): if QT_API_ENV in ["pyqt5", "pyside2"]: QT_API = _ETS[QT_API_ENV] else: - QT_API = None -elif dict.__getitem__(mpl.rcParams, "backend") in ["Qt4Agg", "Qt4Cairo"]: - if QT_API_ENV in ["pyqt4", "pyside"]: - QT_API = _ETS[QT_API_ENV] - else: + _QT_FORCE_QT5_BINDING = True # noqa QT_API = None # A non-Qt backend was selected but we still got there (possible, e.g., when # fully manually embedding Matplotlib in a Qt app without using pyplot). +elif QT_API_ENV is None: + QT_API = None +elif QT_API_ENV in _ETS: + QT_API = _ETS[QT_API_ENV] else: - try: - QT_API = _ETS[QT_API_ENV] - except KeyError as err: - raise RuntimeError( - "The environment variable QT_API has the unrecognized value {!r};" - "valid values are 'pyqt5', 'pyside2', 'pyqt', and " - "'pyside'") from err + raise RuntimeError( + "The environment variable QT_API has the unrecognized value {!r}; " + "valid values are {}".format(QT_API_ENV, ", ".join(_ETS))) -def _setup_pyqt5(): - global QtCore, QtGui, QtWidgets, __version__, is_pyqt5, \ - _isdeleted, _getSaveFileName +def _setup_pyqt5plus(): + global QtCore, QtGui, QtWidgets, __version__ + global _getSaveFileName, _isdeleted, _to_int - if QT_API == QT_API_PYQT5: - from PyQt5 import QtCore, QtGui, QtWidgets - import sip + if QT_API == QT_API_PYQT6: + from PyQt6 import QtCore, QtGui, QtWidgets, sip __version__ = QtCore.PYQT_VERSION_STR QtCore.Signal = QtCore.pyqtSignal QtCore.Slot = QtCore.pyqtSlot QtCore.Property = QtCore.pyqtProperty _isdeleted = sip.isdeleted - elif QT_API == QT_API_PYSIDE2: - from PySide2 import QtCore, QtGui, QtWidgets, __version__ - import shiboken2 - def _isdeleted(obj): return not shiboken2.isValid(obj) - else: - raise ValueError("Unexpected value for the 'backend.qt5' rcparam") - _getSaveFileName = QtWidgets.QFileDialog.getSaveFileName - - @_api.deprecated("3.3", alternative="QtCore.qVersion()") - def is_pyqt5(): - return True - - -def _setup_pyqt4(): - global QtCore, QtGui, QtWidgets, __version__, is_pyqt5, \ - _isdeleted, _getSaveFileName - - def _setup_pyqt4_internal(api): - global QtCore, QtGui, QtWidgets, \ - __version__, is_pyqt5, _isdeleted, _getSaveFileName - # List of incompatible APIs: - # http://pyqt.sourceforge.net/Docs/PyQt4/incompatible_apis.html - _sip_apis = ["QDate", "QDateTime", "QString", "QTextStream", "QTime", - "QUrl", "QVariant"] - try: - import sip - except ImportError: - pass + _to_int = operator.attrgetter('value') + elif QT_API == QT_API_PYSIDE6: + from PySide6 import QtCore, QtGui, QtWidgets, __version__ + import shiboken6 + def _isdeleted(obj): return not shiboken6.isValid(obj) + if parse_version(__version__) >= parse_version('6.4'): + _to_int = operator.attrgetter('value') else: - for _sip_api in _sip_apis: - try: - sip.setapi(_sip_api, api) - except ValueError: - pass - from PyQt4 import QtCore, QtGui - import sip # Always succeeds *after* importing PyQt4. + _to_int = int + elif QT_API == QT_API_PYQT5: + from PyQt5 import QtCore, QtGui, QtWidgets + import sip __version__ = QtCore.PYQT_VERSION_STR - # PyQt 4.6 introduced getSaveFileNameAndFilter: - # https://riverbankcomputing.com/news/pyqt-46 - if __version__ < LooseVersion("4.6"): - raise ImportError("PyQt<4.6 is not supported") QtCore.Signal = QtCore.pyqtSignal QtCore.Slot = QtCore.pyqtSlot QtCore.Property = QtCore.pyqtProperty _isdeleted = sip.isdeleted - _getSaveFileName = QtGui.QFileDialog.getSaveFileNameAndFilter - - if QT_API == QT_API_PYQTv2: - _setup_pyqt4_internal(api=2) - elif QT_API == QT_API_PYSIDE: - from PySide import QtCore, QtGui, __version__, __version_info__ - import shiboken - # PySide 1.0.3 fixed the following: - # https://srinikom.github.io/pyside-bz-archive/809.html - if __version_info__ < (1, 0, 3): - raise ImportError("PySide<1.0.3 is not supported") - def _isdeleted(obj): return not shiboken.isValid(obj) - _getSaveFileName = QtGui.QFileDialog.getSaveFileName - elif QT_API == QT_API_PYQT: - _setup_pyqt4_internal(api=1) + _to_int = int + elif QT_API == QT_API_PYSIDE2: + from PySide2 import QtCore, QtGui, QtWidgets, __version__ + try: + from PySide2 import shiboken2 + except ImportError: + import shiboken2 + def _isdeleted(obj): + return not shiboken2.isValid(obj) + _to_int = int else: - raise ValueError("Unexpected value for the 'backend.qt4' rcparam") - QtWidgets = QtGui - - @_api.deprecated("3.3", alternative="QtCore.qVersion()") - def is_pyqt5(): - return False + raise AssertionError(f"Unexpected QT_API: {QT_API}") + _getSaveFileName = QtWidgets.QFileDialog.getSaveFileName -if QT_API in [QT_API_PYQT5, QT_API_PYSIDE2]: - _setup_pyqt5() -elif QT_API in [QT_API_PYQTv2, QT_API_PYSIDE, QT_API_PYQT]: - _setup_pyqt4() +if QT_API in [QT_API_PYQT6, QT_API_PYQT5, QT_API_PYSIDE6, QT_API_PYSIDE2]: + _setup_pyqt5plus() elif QT_API is None: # See above re: dict.__getitem__. - if dict.__getitem__(mpl.rcParams, "backend") == "Qt4Agg": - _candidates = [(_setup_pyqt4, QT_API_PYQTv2), - (_setup_pyqt4, QT_API_PYSIDE), - (_setup_pyqt4, QT_API_PYQT), - (_setup_pyqt5, QT_API_PYQT5), - (_setup_pyqt5, QT_API_PYSIDE2)] + if _QT_FORCE_QT5_BINDING: + _candidates = [ + (_setup_pyqt5plus, QT_API_PYQT5), + (_setup_pyqt5plus, QT_API_PYSIDE2), + ] else: - _candidates = [(_setup_pyqt5, QT_API_PYQT5), - (_setup_pyqt5, QT_API_PYSIDE2), - (_setup_pyqt4, QT_API_PYQTv2), - (_setup_pyqt4, QT_API_PYSIDE), - (_setup_pyqt4, QT_API_PYQT)] + _candidates = [ + (_setup_pyqt5plus, QT_API_PYQT6), + (_setup_pyqt5plus, QT_API_PYSIDE6), + (_setup_pyqt5plus, QT_API_PYQT5), + (_setup_pyqt5plus, QT_API_PYSIDE2), + ] + for _setup, QT_API in _candidates: try: _setup() @@ -176,28 +134,38 @@ def is_pyqt5(): continue break else: - raise ImportError("Failed to import any qt binding") + raise ImportError( + "Failed to import any of the following Qt binding modules: {}" + .format(", ".join(_ETS.values()))) else: # We should not get there. - raise AssertionError("Unexpected QT_API: {}".format(QT_API)) + raise AssertionError(f"Unexpected QT_API: {QT_API}") # Fixes issues with Big Sur # https://bugreports.qt.io/browse/QTBUG-87014, fixed in qt 5.15.2 if (sys.platform == 'darwin' and - LooseVersion(platform.mac_ver()[0]) >= LooseVersion("10.16") and - LooseVersion(QtCore.qVersion()) < LooseVersion("5.15.2") and - "QT_MAC_WANTS_LAYER" not in os.environ): - os.environ["QT_MAC_WANTS_LAYER"] = "1" + parse_version(platform.mac_ver()[0]) >= parse_version("10.16") and + QtCore.QLibraryInfo.version().segments() <= [5, 15, 2]): + os.environ.setdefault("QT_MAC_WANTS_LAYER", "1") -# These globals are only defined for backcompatibility purposes. -ETS = dict(pyqt=(QT_API_PYQTv2, 4), pyside=(QT_API_PYSIDE, 4), - pyqt5=(QT_API_PYQT5, 5), pyside2=(QT_API_PYSIDE2, 5)) +# PyQt6 enum compat helpers. -QT_RC_MAJOR_VERSION = int(QtCore.qVersion().split(".")[0]) -if QT_RC_MAJOR_VERSION == 4: - _api.warn_deprecated("3.3", name="support for Qt4") +@functools.lru_cache(None) +def _enum(name): + # foo.bar.Enum.Entry (PyQt6) <=> foo.bar.Entry (non-PyQt6). + return operator.attrgetter( + name if QT_API == 'PyQt6' else name.rpartition(".")[0] + )(sys.modules[QtCore.__package__]) + + +# Backports. + + +def _exec(obj): + # exec on PyQt6, exec_ elsewhere. + obj.exec() if hasattr(obj, "exec") else obj.exec_() def _devicePixelRatioF(obj): @@ -212,7 +180,7 @@ def _devicePixelRatioF(obj): except AttributeError: pass try: - # Not available on Qt4 or some older Qt5. + # Not available on older Qt5. # self.devicePixelRatio() returns 0 in rare cases return obj.devicePixelRatio() or 1 except AttributeError: @@ -226,5 +194,84 @@ def _setDevicePixelRatio(obj, val): This can be replaced by the direct call when we require Qt>=5.6. """ if hasattr(obj, 'setDevicePixelRatio'): - # Not available on Qt4 or some older Qt5. + # Not available on older Qt5. obj.setDevicePixelRatio(val) + + +@contextlib.contextmanager +def _maybe_allow_interrupt(qapp): + """ + This manager allows to terminate a plot by sending a SIGINT. It is + necessary because the running Qt backend prevents Python interpreter to + run and process signals (i.e., to raise KeyboardInterrupt exception). To + solve this one needs to somehow wake up the interpreter and make it close + the plot window. We do this by using the signal.set_wakeup_fd() function + which organizes a write of the signal number into a socketpair connected + to the QSocketNotifier (since it is part of the Qt backend, it can react + to that write event). Afterwards, the Qt handler empties the socketpair + by a recv() command to re-arm it (we need this if a signal different from + SIGINT was caught by set_wakeup_fd() and we shall continue waiting). If + the SIGINT was caught indeed, after exiting the on_signal() function the + interpreter reacts to the SIGINT according to the handle() function which + had been set up by a signal.signal() call: it causes the qt_object to + exit by calling its quit() method. Finally, we call the old SIGINT + handler with the same arguments that were given to our custom handle() + handler. + + We do this only if the old handler for SIGINT was not None, which means + that a non-python handler was installed, i.e. in Julia, and not SIG_IGN + which means we should ignore the interrupts. + """ + old_sigint_handler = signal.getsignal(signal.SIGINT) + handler_args = None + skip = False + if old_sigint_handler in (None, signal.SIG_IGN, signal.SIG_DFL): + skip = True + else: + wsock, rsock = socket.socketpair() + wsock.setblocking(False) + old_wakeup_fd = signal.set_wakeup_fd(wsock.fileno()) + sn = QtCore.QSocketNotifier( + 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 + def _may_clear_sock(*args): + try: + rsock.recv(1) + except BlockingIOError: + pass + + def handle(*args): + nonlocal handler_args + handler_args = args + qapp.quit() + + signal.signal(signal.SIGINT, handle) + try: + yield + finally: + if not skip: + wsock.close() + rsock.close() + sn.setEnabled(False) + signal.set_wakeup_fd(old_wakeup_fd) + signal.signal(signal.SIGINT, old_sigint_handler) + if handler_args is not None: + old_sigint_handler(*handler_args) + + +@_api.caching_module_getattr +class __getattr__: + ETS = _api.deprecated("3.5")(property(lambda self: dict( + pyqt5=(QT_API_PYQT5, 5), pyside2=(QT_API_PYSIDE2, 5)))) + QT_RC_MAJOR_VERSION = _api.deprecated("3.5")(property( + lambda self: int(QtCore.qVersion().split(".")[0]))) diff --git a/lib/matplotlib/backends/qt_editor/_formlayout.py b/lib/matplotlib/backends/qt_editor/_formlayout.py index e2b371f1652d..1306e0c02fa6 100644 --- a/lib/matplotlib/backends/qt_editor/_formlayout.py +++ b/lib/matplotlib/backends/qt_editor/_formlayout.py @@ -47,7 +47,8 @@ from numbers import Integral, Real from matplotlib import _api, colors as mcolors -from matplotlib.backends.qt_compat import QtGui, QtWidgets, QtCore +from matplotlib.backends.qt_compat import ( + QtGui, QtWidgets, QtCore, _enum, _to_int) _log = logging.getLogger(__name__) @@ -70,7 +71,7 @@ def __init__(self, parent=None): def choose_color(self): color = QtWidgets.QColorDialog.getColor( self._color, self.parentWidget(), "", - QtWidgets.QColorDialog.ShowAlphaChannel) + _enum("QtWidgets.QColorDialog.ColorDialogOption").ShowAlphaChannel) if color.isValid(): self.set_color(color) @@ -203,8 +204,7 @@ def get_font(self): def is_edit_valid(edit): text = edit.text() state = edit.validator().validate(text, 0)[0] - - return state == QtGui.QDoubleValidator.Acceptable + return state == _enum("QtGui.QDoubleValidator.State").Acceptable class FormWidget(QtWidgets.QWidget): @@ -291,10 +291,7 @@ def setup(self): field.setCurrentIndex(selindex) elif isinstance(value, bool): field = QtWidgets.QCheckBox(self) - if value: - field.setCheckState(QtCore.Qt.Checked) - else: - field.setCheckState(QtCore.Qt.Unchecked) + field.setChecked(value) elif isinstance(value, Integral): field = QtWidgets.QSpinBox(self) field.setRange(-10**9, 10**9) @@ -336,15 +333,23 @@ def get(self): else: value = value[index] elif isinstance(value, bool): - value = field.checkState() == QtCore.Qt.Checked + value = field.isChecked() elif isinstance(value, Integral): value = int(field.value()) elif isinstance(value, Real): value = float(str(field.text())) elif isinstance(value, datetime.datetime): - value = field.dateTime().toPyDateTime() + datetime_ = field.dateTime() + if hasattr(datetime_, "toPyDateTime"): + value = datetime_.toPyDateTime() + else: + value = datetime_.toPython() elif isinstance(value, datetime.date): - value = field.date().toPyDate() + date_ = field.date() + if hasattr(date_, "toPyDate"): + value = date_.toPyDate() + else: + value = date_.toPython() else: value = eval(str(field.text())) valuelist.append(value) @@ -436,10 +441,16 @@ def __init__(self, data, title="", comment="", # Button box self.bbox = bbox = QtWidgets.QDialogButtonBox( - QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel) + QtWidgets.QDialogButtonBox.StandardButton( + _to_int( + _enum("QtWidgets.QDialogButtonBox.StandardButton").Ok) | + _to_int( + _enum("QtWidgets.QDialogButtonBox.StandardButton").Cancel) + )) self.formwidget.update_buttons.connect(self.update_buttons) if self.apply_callback is not None: - apply_btn = bbox.addButton(QtWidgets.QDialogButtonBox.Apply) + apply_btn = bbox.addButton( + _enum("QtWidgets.QDialogButtonBox.StandardButton").Apply) apply_btn.clicked.connect(self.apply) bbox.accepted.connect(self.accept) @@ -462,14 +473,16 @@ def update_buttons(self): for field in self.float_fields: if not is_edit_valid(field): valid = False - for btn_type in (QtWidgets.QDialogButtonBox.Ok, - QtWidgets.QDialogButtonBox.Apply): - btn = self.bbox.button(btn_type) + for btn_type in ["Ok", "Apply"]: + btn = self.bbox.button( + getattr(_enum("QtWidgets.QDialogButtonBox.StandardButton"), + btn_type)) if btn is not None: btn.setEnabled(valid) def accept(self): self.data = self.formwidget.get() + self.apply_callback(self.data) super().accept() def reject(self): @@ -486,8 +499,7 @@ def get(self): def fedit(data, title="", comment="", icon=None, parent=None, apply=None): """ - Create form dialog and return result - (if Cancel button is pressed, return None) + Create form dialog data: datalist, datagroup title: str @@ -505,7 +517,7 @@ def fedit(data, title="", comment="", icon=None, parent=None, apply=None): box) for each member of a datagroup inside a datagroup Supported types for field_value: - - int, float, str, unicode, bool + - int, float, str, bool - colors: in Qt-compatible text form, i.e. in hex format or name (red, ...) (automatically detected from a string) - list/tuple: @@ -518,12 +530,19 @@ def fedit(data, title="", comment="", icon=None, parent=None, apply=None): if QtWidgets.QApplication.startingUp(): _app = QtWidgets.QApplication([]) dialog = FormDialog(data, title, comment, icon, parent, apply) - if dialog.exec_(): - return dialog.get() + + if parent is not None: + if hasattr(parent, "_fedit_dialog"): + parent._fedit_dialog.close() + parent._fedit_dialog = dialog + + dialog.show() if __name__ == "__main__": + _app = QtWidgets.QApplication([]) + def create_datalist_example(): return [('str', 'this is a string'), ('list', [0, '1', '3', '4']), @@ -546,23 +565,29 @@ def create_datagroup_example(): (datalist, "Category 2", "Category 2 comment"), (datalist, "Category 3", "Category 3 comment")) - #--------- datalist example + # --------- datalist example datalist = create_datalist_example() def apply_test(data): print("data:", data) - print("result:", fedit(datalist, title="Example", - comment="This is just an example.", - apply=apply_test)) + fedit(datalist, title="Example", + comment="This is just an example.", + apply=apply_test) + + _app.exec() - #--------- datagroup example + # --------- datagroup example datagroup = create_datagroup_example() - print("result:", fedit(datagroup, "Global title")) + fedit(datagroup, "Global title", + apply=apply_test) + _app.exec() - #--------- datagroup inside a datagroup example + # --------- datagroup inside a datagroup example datalist = create_datalist_example() datagroup = create_datagroup_example() - print("result:", fedit(((datagroup, "Title 1", "Tab 1 comment"), - (datalist, "Title 2", "Tab 2 comment"), - (datalist, "Title 3", "Tab 3 comment")), - "Global title")) + fedit(((datagroup, "Title 1", "Tab 1 comment"), + (datalist, "Title 2", "Tab 2 comment"), + (datalist, "Title 3", "Tab 3 comment")), + "Global title", + apply=apply_test) + _app.exec() diff --git a/lib/matplotlib/backends/qt_editor/_formsubplottool.py b/lib/matplotlib/backends/qt_editor/_formsubplottool.py deleted file mode 100644 index d8d0af0107b4..000000000000 --- a/lib/matplotlib/backends/qt_editor/_formsubplottool.py +++ /dev/null @@ -1,40 +0,0 @@ -from matplotlib.backends.qt_compat import QtWidgets - - -class UiSubplotTool(QtWidgets.QDialog): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.setObjectName("SubplotTool") - self._widgets = {} - - main_layout = QtWidgets.QHBoxLayout() - self.setLayout(main_layout) - - for group, spinboxes, buttons in [ - ("Borders", - ["top", "bottom", "left", "right"], ["Export values"]), - ("Spacings", - ["hspace", "wspace"], ["Tight layout", "Reset", "Close"]), - ]: - layout = QtWidgets.QVBoxLayout() - main_layout.addLayout(layout) - box = QtWidgets.QGroupBox(group) - layout.addWidget(box) - inner = QtWidgets.QFormLayout(box) - for name in spinboxes: - self._widgets[name] = widget = QtWidgets.QDoubleSpinBox() - widget.setMinimum(0) - widget.setMaximum(1) - widget.setDecimals(3) - widget.setSingleStep(0.005) - widget.setKeyboardTracking(False) - inner.addRow(name, widget) - layout.addStretch(1) - for name in buttons: - self._widgets[name] = widget = QtWidgets.QPushButton(name) - # Don't trigger on , which is used to input values. - widget.setAutoDefault(False) - layout.addWidget(widget) - - self._widgets["Close"].setFocus() diff --git a/lib/matplotlib/backends/qt_editor/figureoptions.py b/lib/matplotlib/backends/qt_editor/figureoptions.py index 98cf3c610773..67e00006910f 100644 --- a/lib/matplotlib/backends/qt_editor/figureoptions.py +++ b/lib/matplotlib/backends/qt_editor/figureoptions.py @@ -5,12 +5,10 @@ """Module that provides a GUI-based editor for Matplotlib's figure options.""" -import re - from matplotlib import cbook, cm, colors as mcolors, markers, image as mimage from matplotlib.backends.qt_compat import QtGui from matplotlib.backends.qt_editor import _formlayout - +from matplotlib.dates import DateConverter, num2date LINESTYLES = {'-': 'Solid', '--': 'Dashed', @@ -33,45 +31,45 @@ def figure_edit(axes, parent=None): sep = (None, None) # separator # Get / General - # Cast to builtin floats as they have nicer reprs. - xmin, xmax = map(float, axes.get_xlim()) - ymin, ymax = map(float, axes.get_ylim()) + def convert_limits(lim, converter): + """Convert axis limits for correct input editors.""" + if isinstance(converter, DateConverter): + return map(num2date, lim) + # Cast to builtin floats as they have nicer reprs. + return map(float, lim) + + xconverter = axes.xaxis.converter + xmin, xmax = convert_limits(axes.get_xlim(), xconverter) + yconverter = axes.yaxis.converter + ymin, ymax = convert_limits(axes.get_ylim(), yconverter) general = [('Title', axes.get_title()), sep, (None, "X-Axis"), ('Left', xmin), ('Right', xmax), ('Label', axes.get_xlabel()), - ('Scale', [axes.get_xscale(), 'linear', 'log', 'logit']), + ('Scale', [axes.get_xscale(), + 'linear', 'log', 'symlog', 'logit']), sep, (None, "Y-Axis"), ('Bottom', ymin), ('Top', ymax), ('Label', axes.get_ylabel()), - ('Scale', [axes.get_yscale(), 'linear', 'log', 'logit']), + ('Scale', [axes.get_yscale(), + 'linear', 'log', 'symlog', 'logit']), sep, ('(Re-)Generate automatic legend', False), ] # Save the unit data - xconverter = axes.xaxis.converter - yconverter = axes.yaxis.converter xunits = axes.xaxis.get_units() yunits = axes.yaxis.get_units() - # Sorting for default labels (_lineXXX, _imageXXX). - def cmp_key(label): - match = re.match(r"(_line|_image)(\d+)", label) - if match: - return match.group(1), int(match.group(2)) - else: - return label, 0 - # Get / Curves - linedict = {} + labeled_lines = [] for line in axes.get_lines(): label = line.get_label() if label == '_nolegend_': continue - linedict[label] = line + labeled_lines.append((label, line)) curves = [] def prepare_data(d, init): @@ -101,9 +99,7 @@ def prepare_data(d, init): sorted(short2name.items(), key=lambda short_and_name: short_and_name[1])) - curvelabels = sorted(linedict, key=cmp_key) - for label in curvelabels: - line = linedict[label] + for label, line in labeled_lines: color = mcolors.to_hex( mcolors.to_rgba(line.get_color(), line.get_alpha()), keep_alpha=True) @@ -132,19 +128,17 @@ def prepare_data(d, init): has_curve = bool(curves) # Get ScalarMappables. - mappabledict = {} + labeled_mappables = [] for mappable in [*axes.images, *axes.collections]: label = mappable.get_label() if label == '_nolegend_' or mappable.get_array() is None: continue - mappabledict[label] = mappable - mappablelabels = sorted(mappabledict, key=cmp_key) + labeled_mappables.append((label, mappable)) mappables = [] - cmaps = [(cmap, name) for name, cmap in sorted(cm._cmap_registry.items())] - for label in mappablelabels: - mappable = mappabledict[label] + cmaps = [(cmap, name) for name, cmap in sorted(cm._colormaps.items())] + for label, mappable in labeled_mappables: cmap = mappable.get_cmap() - if cmap not in cm._cmap_registry.values(): + if cmap not in cm._colormaps.values(): cmaps = [(cmap, cmap.name), *cmaps] low, high = mappable.get_clim() mappabledata = [ @@ -205,7 +199,7 @@ def apply_callback(data): # Set / Curves for index, curve in enumerate(curves): - line = linedict[curvelabels[index]] + line = labeled_lines[index][1] (label, linestyle, drawstyle, linewidth, color, marker, markersize, markerfacecolor, markeredgecolor) = curve line.set_label(label) @@ -223,7 +217,7 @@ def apply_callback(data): # Set ScalarMappables. for index, mappable_settings in enumerate(mappables): - mappable = mappabledict[mappablelabels[index]] + mappable = labeled_mappables[index][1] if len(mappable_settings) == 5: label, cmap, low, high, interpolation = mappable_settings mappable.set_interpolation(interpolation) @@ -236,12 +230,12 @@ def apply_callback(data): # re-generate legend, if checkbox is checked if generate_legend: draggable = None - ncol = 1 + ncols = 1 if axes.legend_ is not None: old_legend = axes.get_legend() draggable = old_legend._draggable is not None - ncol = old_legend._ncol - new_legend = axes.legend(ncol=ncol) + ncols = old_legend._ncols + new_legend = axes.legend(ncols=ncols) if new_legend: new_legend.set_draggable(draggable) @@ -251,10 +245,8 @@ def apply_callback(data): if not (axes.get_xlim() == orig_xlim and axes.get_ylim() == orig_ylim): figure.canvas.toolbar.push_current() - data = _formlayout.fedit( + _formlayout.fedit( datalist, title="Figure options", parent=parent, icon=QtGui.QIcon( str(cbook._get_data_path('images', 'qt4_editor_options.svg'))), apply=apply_callback) - if data is not None: - apply_callback(data) diff --git a/lib/matplotlib/backends/qt_editor/formsubplottool.py b/lib/matplotlib/backends/qt_editor/formsubplottool.py deleted file mode 100644 index db79a3a054e0..000000000000 --- a/lib/matplotlib/backends/qt_editor/formsubplottool.py +++ /dev/null @@ -1,8 +0,0 @@ -from matplotlib import _api -from ._formsubplottool import UiSubplotTool - - -_api.warn_deprecated( - "3.3", obj_type="module", name=__name__, - alternative="matplotlib.backends.backend_qt5.SubplotToolQt") -__all__ = ["UiSubplotTool"] diff --git a/lib/matplotlib/backends/web_backend/css/fbm.css b/lib/matplotlib/backends/web_backend/css/fbm.css index 0e21d19ae801..ce35d99a5e64 100644 --- a/lib/matplotlib/backends/web_backend/css/fbm.css +++ b/lib/matplotlib/backends/web_backend/css/fbm.css @@ -1,95 +1,95 @@ /* Flexible box model classes */ -/* Taken from Alex Russell http://infrequently.org/2009/08/css-3-progress/ */ - +/* Taken from Alex Russell https://infrequently.org/2009/08/css-3-progress/ */ + .hbox { display: -webkit-box; -webkit-box-orient: horizontal; -webkit-box-align: stretch; - + display: -moz-box; -moz-box-orient: horizontal; -moz-box-align: stretch; - + display: box; box-orient: horizontal; box-align: stretch; } - + .hbox > * { -webkit-box-flex: 0; -moz-box-flex: 0; box-flex: 0; } - + .vbox { display: -webkit-box; -webkit-box-orient: vertical; -webkit-box-align: stretch; - + display: -moz-box; -moz-box-orient: vertical; -moz-box-align: stretch; - + display: box; box-orient: vertical; box-align: stretch; } - + .vbox > * { -webkit-box-flex: 0; -moz-box-flex: 0; box-flex: 0; } - + .reverse { -webkit-box-direction: reverse; -moz-box-direction: reverse; box-direction: reverse; } - + .box-flex0 { -webkit-box-flex: 0; -moz-box-flex: 0; box-flex: 0; } - + .box-flex1, .box-flex { -webkit-box-flex: 1; -moz-box-flex: 1; box-flex: 1; } - + .box-flex2 { -webkit-box-flex: 2; -moz-box-flex: 2; box-flex: 2; } - + .box-group1 { -webkit-box-flex-group: 1; -moz-box-flex-group: 1; box-flex-group: 1; } - + .box-group2 { -webkit-box-flex-group: 2; -moz-box-flex-group: 2; box-flex-group: 2; } - + .start { -webkit-box-pack: start; -moz-box-pack: start; box-pack: start; } - + .end { -webkit-box-pack: end; -moz-box-pack: end; box-pack: end; } - + .center { -webkit-box-pack: center; -moz-box-pack: center; diff --git a/lib/matplotlib/backends/web_backend/css/page.css b/lib/matplotlib/backends/web_backend/css/page.css index c380ef0a3ffc..ded0d9220379 100644 --- a/lib/matplotlib/backends/web_backend/css/page.css +++ b/lib/matplotlib/backends/web_backend/css/page.css @@ -80,4 +80,3 @@ span#login_widget { margin: 10px; vertical-align: top; } - diff --git a/lib/matplotlib/backends/web_backend/ipython_inline_figure.html b/lib/matplotlib/backends/web_backend/ipython_inline_figure.html index 9cc6aa9020e2..b941d352a7d6 100644 --- a/lib/matplotlib/backends/web_backend/ipython_inline_figure.html +++ b/lib/matplotlib/backends/web_backend/ipython_inline_figure.html @@ -2,7 +2,7 @@ websocket server, so we have to get in client-side and fetch our resources that way. -->