Skip to content

Support for PyQt6/PySide6. #19255

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Aug 5, 2021
46 changes: 30 additions & 16 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ jobs:
texlive-luatex \
texlive-xetex \
ttf-wqy-zenhei
if [[ "${{ matrix.os }}" = ubuntu-20.04 ]]; then
sudo apt install -yy libopengl0
fi
;;
macOS)
brew install ccache
Expand All @@ -106,25 +109,25 @@ jobs:
if: startsWith(runner.os, 'Linux')
with:
path: ~/.cache/pip
key: ${{ runner.os }}-py${{ matrix.python-version }}-pip-${{ hashFiles('requirements/*/*.txt') }}
key: ${{ matrix.os }}-py${{ matrix.python-version }}-pip-${{ hashFiles('requirements/*/*.txt') }}
restore-keys: |
${{ runner.os }}-py${{ matrix.python-version }}-pip-
${{ matrix.os }}-py${{ matrix.python-version }}-pip-
- name: Cache pip
uses: actions/cache@v2
if: startsWith(runner.os, 'macOS')
with:
path: ~/Library/Caches/pip
key: ${{ runner.os }}-py${{ matrix.python-version }}-pip-${{ hashFiles('requirements/*/*.txt') }}
key: ${{ matrix.os }}-py${{ matrix.python-version }}-pip-${{ hashFiles('requirements/*/*.txt') }}
restore-keys: |
${{ runner.os }}-py${{ matrix.python-version }}-pip-
${{ matrix.os }}-py${{ matrix.python-version }}-pip-
- name: Cache ccache
uses: actions/cache@v2
with:
path: |
~/.ccache
key: ${{ runner.os }}-py${{ matrix.python-version }}-ccache-${{ hashFiles('src/*') }}
key: ${{ matrix.os }}-py${{ matrix.python-version }}-ccache-${{ hashFiles('src/*') }}
restore-keys: |
${{ runner.os }}-py${{ matrix.python-version }}-ccache-
${{ matrix.os }}-py${{ matrix.python-version }}-ccache-
- name: Cache Matplotlib
uses: actions/cache@v2
with:
Expand Down Expand Up @@ -171,17 +174,28 @@ jobs:
# Sept 2020) for either pyqt5 (there are only wheels for 10.13+) or
# pyside2 (the latest version (5.13.2) with 10.12 wheels has a
# fatal to us bug, it was fixed in 5.14.0 which has 10.13 wheels)
python -m pip install --upgrade pyqt5${{ matrix.pyqt5-ver }} &&
python -c 'import PyQt5.QtCore' &&
echo 'PyQt5 is available' ||
echo 'PyQt5 is not available'
python -m pip install --upgrade pyside2 &&
python -c 'import PySide2.QtCore' &&
echo 'PySide2 is available' ||
echo 'PySide2 is not available'
python -mpip install --upgrade pyqt5${{ matrix.pyqt5-ver }} &&
python -c 'import PyQt5.QtCore' &&
echo 'PyQt5 is available' ||
echo 'PyQt5 is not available'
python -mpip install --upgrade pyside2 &&
python -c 'import PySide2.QtCore' &&
echo 'PySide2 is available' ||
echo 'PySide2 is not available'
# Qt6 crashes on Github's ubuntu 18.04 runner.
if [[ "${{ matrix.os }}" = ubuntu-20.04 ]]; then
python -mpip install --upgrade pyqt6 &&
python -c 'import PyQt6.QtCore' &&
echo 'PyQt6 is available' ||
echo 'PyQt6 is not available'
python -mpip install --upgrade pyside6 &&
python -c 'import PySide6.QtCore' &&
echo 'PySide6 is available' ||
echo 'PySide6 is not available'
fi
fi
python -m pip install --upgrade \
-f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-$(lsb_release -r -s) \
python -mpip install --upgrade \
-f "https://extras.wxpython.org/wxPython4/extras/linux/gtk3/${{ matrix.os }}" \
wxPython &&
python -c 'import wx' &&
echo 'wxPython is available' ||
Expand Down
4 changes: 3 additions & 1 deletion doc/devel/dependencies.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Matplotlib figures can be rendered to various user interfaces. See
and the capabilities they provide.

* Tk_ (>= 8.3, != 8.6.0 or 8.6.1) [#]_: for the Tk-based backends.
* PyQt5_ or PySide2_: for the Qt5-based backends.
* PyQt6_ (>= 6.1), PySide6_, PyQt5_, or PySide2_: for the Qt-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
Expand All @@ -51,6 +51,8 @@ and the capabilities they provide.
.. _Tk: https://docs.python.org/3/library/tk.html
.. _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/
Expand Down
4 changes: 2 additions & 2 deletions doc/users/interactive.rst
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,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 <controlling-interactive>` use the
``%matplotlib`` magic:

Expand All @@ -72,7 +72,7 @@ and enable :ref:`interactive mode <controlling-interactive>` use the
::

In [1]: %matplotlib
Using matplotlib backend: Qt5Agg
Using matplotlib backend: QtAgg

In [2]: import matplotlib.pyplot as plt

Expand Down
20 changes: 11 additions & 9 deletions examples/user_interfaces/embedding_in_qt_sgskip.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@
===============

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
import time

import numpy as np

from matplotlib.backends.qt_compat import QtCore, QtWidgets
from matplotlib.backends.backend_qt5agg import (
from matplotlib.backends.qt_compat import QtWidgets
from matplotlib.backends.backend_qtagg import (
FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure

Expand All @@ -28,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)
Expand Down Expand Up @@ -66,4 +68,4 @@ def _update_canvas(self):
app.show()
app.activateWindow()
app.raise_()
qapp.exec_()
qapp.exec()
5 changes: 2 additions & 3 deletions lib/matplotlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1100,9 +1100,8 @@ def use(backend, *, force=True):
backend names, which are case-insensitive:

- interactive backends:
GTK3Agg, GTK3Cairo, MacOSX, nbAgg,
Qt5Agg, Qt5Cairo,
TkAgg, TkCairo, WebAgg, WX, WXAgg, WXCairo
GTK3Agg, GTK3Cairo, MacOSX, nbAgg, QtAgg, QtCairo,
TkAgg, TkCairo, WebAgg, WX, WXAgg, WXCairo, Qt5Agg, Qt5Cairo

- non-interactive backends:
agg, cairo, pdf, pgf, ps, svg, template
Expand Down
18 changes: 10 additions & 8 deletions lib/matplotlib/backend_bases.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,14 @@ 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',
'gtk3': 'gtk3agg',
'wx': 'wxagg',
'tk': 'tkagg',
'macosx': 'macosx',
'headless': 'agg'}
backend_mapping = {
'qt': 'qtagg',
'gtk3': 'gtk3agg',
'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.
Expand Down Expand Up @@ -1662,7 +1664,7 @@ class FigureCanvasBase:
A high-level figure instance.
"""

# Set to one of {"qt5", "gtk3", "wx", "tk", "macosx"} if an
# Set to one of {"qt", "gtk3", "wx", "tk", "macosx"} if an
# interactive framework is required, or None otherwise.
required_interactive_framework = None

Expand Down Expand Up @@ -1738,7 +1740,7 @@ def _fix_ipython_backend2gui(cls):
# don't break on our side.
return
rif = getattr(cls, "required_interactive_framework", None)
backend2gui_rif = {"qt5": "qt", "gtk3": "gtk3",
backend2gui_rif = {"qt": "qt", "gtk3": "gtk3",
"wx": "wx", "macosx": "osx"}.get(rif)
if backend2gui_rif:
if _is_non_interactive_terminal_ipython(ip):
Expand Down
Loading