Skip to content

Commit ef607cf

Browse files
committed
MNT: remove pyside2 support
pyside2 does not support python 3.12+
1 parent f9e8f77 commit ef607cf

13 files changed

Lines changed: 29 additions & 69 deletions

File tree

.github/workflows/cygwin.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,10 +186,6 @@ jobs:
186186
python -c 'import PyQt5.QtCore' &&
187187
echo 'PyQt5 is available' ||
188188
echo 'PyQt5 is not available'
189-
python -mpip install --upgrade pyside2 &&
190-
python -c 'import PySide2.QtCore' &&
191-
echo 'PySide2 is available' ||
192-
echo 'PySide2 is not available'
193189
python -m pip uninstall --yes wxpython || echo 'wxPython already uninstalled'
194190
195191
- name: Install Matplotlib

doc/api/backend_qt_api.rst

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,9 @@ a dependency to building the docs.
2222
Qt Bindings
2323
-----------
2424

25-
There are currently 2 actively supported Qt versions, Qt5 and Qt6, and two
26-
supported Python bindings per version -- `PyQt5
27-
<https://www.riverbankcomputing.com/static/Docs/PyQt5/>`_ and `PySide2
28-
<https://doc.qt.io/qtforpython-5/contents.html>`_ for Qt5 and `PyQt6
25+
There are currently 2 actively supported Qt versions, Qt5 and Qt6. `PyQt5
26+
<https://www.riverbankcomputing.com/static/Docs/PyQt5/>`_ is the supported
27+
Python binding for Qt5 and there are both `PyQt6
2928
<https://www.riverbankcomputing.com/static/Docs/PyQt6/>`_ and `PySide6
3029
<https://doc.qt.io/qtforpython/contents.html>`_ for Qt6 [#]_. Matplotlib's
3130
qtagg and qtcairo backends (``matplotlib.backends.backend_qtagg`` and
@@ -35,13 +34,12 @@ parts factored out in the ``matplotlib.backends.backend_qt`` module.
3534
At runtime, these backends select the actual binding used as follows:
3635

3736
1. If a binding's ``QtCore`` subpackage is already imported, that binding is
38-
selected (the order for the check is ``PyQt6``, ``PySide6``, ``PyQt5``,
39-
``PySide2``).
37+
selected (the order for the check is ``PyQt6``, ``PySide6``, ``PyQt5``).
4038
2. If the :envvar:`QT_API` environment variable is set to one of "PyQt6",
41-
"PySide6", "PyQt5", "PySide2" (case-insensitive), that binding is selected.
39+
"PySide6", "PyQt5" (case-insensitive), that binding is selected.
4240
(See also the documentation on :ref:`environment-variables`.)
4341
3. Otherwise, the first available backend in the order ``PyQt6``, ``PySide6``,
44-
``PyQt5``, ``PySide2`` is selected.
42+
``PyQt5`` is selected.
4543

4644
In the past, Matplotlib used to have separate backends for each version of Qt
4745
(e.g. qt4agg/``matplotlib.backends.backend_qt4agg`` and
@@ -62,8 +60,9 @@ change without warning [#]_.
6260

6361
.. [#] There is also `PyQt4
6462
<https://www.riverbankcomputing.com/static/Docs/PyQt4/>`_ and `PySide
65-
<https://srinikom.github.io/pyside-docs/>`_ for Qt4 but these are no
66-
longer supported by Matplotlib and upstream support for Qt4 ended
63+
<https://srinikom.github.io/pyside-docs/>`_ for Qt4 and `PySide2
64+
<https://doc.qt.io/qtforpython-5/contents.html>`_ for Qt5 but these are
65+
no longer supported by Matplotlib. Upstream support for Qt4 ended
6766
in 2015.
6867
.. [#] Despite the slight API differences, the more important distinction
6968
between the PyQt and Qt for Python series of bindings is licensing.

doc/install/dependencies.rst

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,7 @@ and the capabilities they provide.
6262
* Tk_ (>= 8.5, != 8.6.0 or 8.6.1): for the Tk-based backends. Tk is part of
6363
most standard Python installations, but it's not part of Python itself and
6464
thus may not be present in rare cases.
65-
* PyQt6_ (>= 6.1), PySide6_, PyQt5_ (>= 5.12), or PySide2_: for the Qt-based
66-
backends.
65+
* PyQt6_ (>= 6.1), PySide6_, or PyQt5_ (>= 5.12): for the Qt-based backends.
6766
* PyGObject_ and pycairo_ (>= 1.14.0): for the GTK-based backends. If using pip
6867
(but not conda or system package manager) PyGObject must be built from
6968
source; see `pygobject documentation
@@ -78,7 +77,6 @@ and the capabilities they provide.
7877

7978
.. _Tk: https://docs.python.org/3/library/tk.html
8079
.. _PyQt5: https://pypi.org/project/PyQt5/
81-
.. _PySide2: https://pypi.org/project/PySide2/
8280
.. _PyQt6: https://pypi.org/project/PyQt6/
8381
.. _PySide6: https://pypi.org/project/PySide6/
8482
.. _PyGObject: https://pygobject.readthedocs.io/en/latest/

galleries/examples/user_interfaces/embedding_in_qt_sgskip.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
===========
55
66
Simple Qt application embedding Matplotlib canvases. This program will work
7-
equally well using any Qt binding (PyQt6, PySide6, PyQt5, PySide2). The
7+
equally well using any Qt binding (PyQt6, PySide6, PyQt5). The
88
binding can be selected by setting the :envvar:`QT_API` environment variable to
99
the binding name, or by first importing it.
1010
"""

galleries/examples/user_interfaces/mplcvd.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def setup(figure):
104104
break
105105
if pkg == "gi":
106106
_setup_gtk(tb)
107-
elif pkg in ("PyQt5", "PySide2", "PyQt6", "PySide6"):
107+
elif pkg in ("PyQt5", "PyQt6", "PySide6"):
108108
_setup_qt(tb)
109109
elif pkg == "tkinter":
110110
_setup_tk(tb)

galleries/users_explain/figure/backends.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ program that can be run to test basic functionality. If this test fails, try re
321321
QtAgg, QtCairo, Qt5Agg, and Qt5Cairo
322322
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
323323

324-
Test ``PyQt6`` (if you have ``PyQt5``, ``PySide2`` or ``PySide6`` installed
324+
Test ``PyQt6`` (if you have ``PyQt5`` or ``PySide6`` installed
325325
rather than ``PyQt6``, just change the import accordingly):
326326

327327
.. code-block:: bash

lib/matplotlib/backends/backend_qt.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,9 @@ def _create_qApp():
104104
# Check to make sure a QApplication from a different major version
105105
# of Qt is not instantiated in the process
106106
if QT_API in {'PyQt6', 'PySide6'}:
107-
other_bindings = ('PyQt5', 'PySide2')
107+
other_bindings = ('PyQt5',)
108108
qt_version = 6
109-
elif QT_API in {'PyQt5', 'PySide2'}:
109+
elif QT_API == 'PyQt5':
110110
other_bindings = ('PyQt6', 'PySide6')
111111
qt_version = 5
112112
else:
@@ -195,10 +195,7 @@ def __init__(self, *args, **kwargs):
195195
super().__init__(*args, **kwargs)
196196

197197
def __del__(self):
198-
# The check for deletedness is needed to avoid an error at animation
199-
# shutdown with PySide2.
200-
if not _isdeleted(self._timer):
201-
self._timer_stop()
198+
self._timer_stop()
202199

203200
def _timer_set_single_shot(self):
204201
self._timer.setSingleShot(self._single)

lib/matplotlib/backends/backend_qtagg.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
Render to qt from agg.
33
"""
44

5-
import ctypes
65

76
from matplotlib.transforms import Bbox
87

@@ -62,11 +61,6 @@ def paintEvent(self, event):
6261
# set origin using original QT coordinates
6362
origin = QtCore.QPoint(rect.left(), rect.top())
6463
painter.drawImage(origin, qimage)
65-
# Adjust the buf reference count to work around a memory
66-
# leak bug in QImage under PySide.
67-
if QT_API == "PySide2" and QtCore.__version_info__ < (5, 12):
68-
ctypes.c_long.from_address(id(buf)).value = 1
69-
7064
self._draw_rect_callback(painter)
7165
finally:
7266
painter.end()

lib/matplotlib/backends/backend_qtcairo.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import ctypes
21

32
from .backend_cairo import cairo, FigureCanvasCairo
43
from .backend_qt import _BackendQT, FigureCanvasQT
5-
from .qt_compat import QT_API, QtCore, QtGui
4+
from .qt_compat import QT_API, QtGui
65

76

87
class FigureCanvasQTCairo(FigureCanvasCairo, FigureCanvasQT):
@@ -29,10 +28,6 @@ def paintEvent(self, event):
2928
qimage = QtGui.QImage(
3029
ptr, width, height,
3130
QtGui.QImage.Format.Format_ARGB32_Premultiplied)
32-
# Adjust the buf reference count to work around a memory leak bug in
33-
# QImage under PySide.
34-
if QT_API == "PySide2" and QtCore.__version_info__ < (5, 12):
35-
ctypes.c_long.from_address(id(buf)).value = 1
3631
qimage.setDevicePixelRatio(self.device_pixel_ratio)
3732
painter = QtGui.QPainter(self)
3833
painter.eraseRect(event.rect())

lib/matplotlib/backends/qt_compat.py

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Qt binding and backend selector.
33
44
The selection logic is as follows:
5-
- if any of PyQt6, PySide6, PyQt5, or PySide2 have already been
5+
- if any of PyQt6, PySide6, or PyQt5 have already been
66
imported (checked in that order), use it;
77
- otherwise, if the QT_API environment variable (used by Enthought) is set, use
88
it to determine which binding to use;
@@ -23,13 +23,12 @@
2323
QT_API_PYQT6 = "PyQt6"
2424
QT_API_PYSIDE6 = "PySide6"
2525
QT_API_PYQT5 = "PyQt5"
26-
QT_API_PYSIDE2 = "PySide2"
2726
QT_API_ENV = os.environ.get("QT_API")
2827
if QT_API_ENV is not None:
2928
QT_API_ENV = QT_API_ENV.lower()
3029
_ETS = { # Mapping of QT_API_ENV to requested binding.
3130
"pyqt6": QT_API_PYQT6, "pyside6": QT_API_PYSIDE6,
32-
"pyqt5": QT_API_PYQT5, "pyside2": QT_API_PYSIDE2,
31+
"pyqt5": QT_API_PYQT5,
3332
}
3433
# First, check if anything is already imported.
3534
if sys.modules.get("PyQt6.QtCore"):
@@ -38,15 +37,13 @@
3837
QT_API = QT_API_PYSIDE6
3938
elif sys.modules.get("PyQt5.QtCore"):
4039
QT_API = QT_API_PYQT5
41-
elif sys.modules.get("PySide2.QtCore"):
42-
QT_API = QT_API_PYSIDE2
4340
# Otherwise, check the QT_API environment variable (from Enthought). This can
4441
# only override the binding, not the backend (in other words, we check that the
4542
# requested backend actually matches). Use _get_backend_or_none to avoid
4643
# triggering backend resolution (which can result in a partially but
4744
# incompletely imported backend_qt5).
4845
elif (mpl.rcParams._get_backend_or_none() or "").lower().startswith("qt5"):
49-
if QT_API_ENV in ["pyqt5", "pyside2"]:
46+
if QT_API_ENV == "pyqt5":
5047
QT_API = _ETS[QT_API_ENV]
5148
else:
5249
_QT_FORCE_QT5_BINDING = True # noqa: F811
@@ -92,33 +89,22 @@ def _isdeleted(obj): return not shiboken6.isValid(obj)
9289
QtCore.Property = QtCore.pyqtProperty
9390
_isdeleted = sip.isdeleted
9491
_to_int = int
95-
elif QT_API == QT_API_PYSIDE2:
96-
from PySide2 import QtCore, QtGui, QtWidgets, QtSvg, __version__
97-
try:
98-
from PySide2 import shiboken2
99-
except ImportError:
100-
import shiboken2
101-
def _isdeleted(obj):
102-
return not shiboken2.isValid(obj)
103-
_to_int = int
10492
else:
10593
raise AssertionError(f"Unexpected QT_API: {QT_API}")
10694

10795

108-
if QT_API in [QT_API_PYQT6, QT_API_PYQT5, QT_API_PYSIDE6, QT_API_PYSIDE2]:
96+
if QT_API in [QT_API_PYQT6, QT_API_PYQT5, QT_API_PYSIDE6]:
10997
_setup_pyqt5plus()
11098
elif QT_API is None: # See above re: dict.__getitem__.
11199
if _QT_FORCE_QT5_BINDING:
112100
_candidates = [
113101
(_setup_pyqt5plus, QT_API_PYQT5),
114-
(_setup_pyqt5plus, QT_API_PYSIDE2),
115102
]
116103
else:
117104
_candidates = [
118105
(_setup_pyqt5plus, QT_API_PYQT6),
119106
(_setup_pyqt5plus, QT_API_PYSIDE6),
120107
(_setup_pyqt5plus, QT_API_PYQT5),
121-
(_setup_pyqt5plus, QT_API_PYSIDE2),
122108
]
123109
for _setup, QT_API in _candidates:
124110
try:

0 commit comments

Comments
 (0)