Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e6b7908

Browse files
authored
Merge pull request #22503 from anntzer/uq
Deprecate backend_qt.qApp.
2 parents b555386 + 88377a3 commit e6b7908

File tree

3 files changed

+91
-63
lines changed

3 files changed

+91
-63
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
``backend_qt.qApp``
2+
~~~~~~~~~~~~~~~~~~~
3+
... is deprecated. Use ``QtWidgets.QApplication.instance()`` instead.

lib/matplotlib/backends/backend_qt.py

Lines changed: 63 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -92,70 +92,73 @@
9292
}
9393

9494

95-
# make place holder
96-
qApp = None
95+
@_api.caching_module_getattr
96+
class __getattr__:
97+
qApp = _api.deprecated(
98+
"3.6", alternative="QtWidgets.QApplication.instance()")(
99+
property(lambda self: QtWidgets.QApplication.instance()))
97100

98101

102+
# lru_cache keeps a reference to the QApplication instance, keeping it from
103+
# being GC'd.
104+
@functools.lru_cache(1)
99105
def _create_qApp():
100-
"""
101-
Only one qApp can exist at a time, so check before creating one.
102-
"""
103-
global qApp
104-
105-
if qApp is None:
106-
app = QtWidgets.QApplication.instance()
107-
if app is None:
108-
# display_is_valid returns False only if on Linux and neither X11
109-
# nor Wayland display can be opened.
110-
if not mpl._c_internal_utils.display_is_valid():
111-
raise RuntimeError('Invalid DISPLAY variable')
112-
try:
113-
QtWidgets.QApplication.setAttribute(
114-
QtCore.Qt.AA_EnableHighDpiScaling)
115-
except AttributeError: # Only for Qt>=5.6, <6.
116-
pass
117-
118-
# Check to make sure a QApplication from a different major version
119-
# of Qt is not instantiated in the process
120-
if QT_API in {'PyQt6', 'PySide6'}:
121-
other_bindings = ('PyQt5', 'PySide2')
122-
elif QT_API in {'PyQt5', 'PySide2'}:
123-
other_bindings = ('PyQt6', 'PySide6')
124-
else:
125-
raise RuntimeError("Should never be here")
126-
127-
for binding in other_bindings:
128-
mod = sys.modules.get(f'{binding}.QtWidgets')
129-
if mod is not None and mod.QApplication.instance() is not None:
130-
other_core = sys.modules.get(f'{binding}.QtCore')
131-
_api.warn_external(
132-
f'Matplotlib is using {QT_API} which wraps '
133-
f'{QtCore.qVersion()} however an instantiated '
134-
f'QApplication from {binding} which wraps '
135-
f'{other_core.qVersion()} exists. Mixing Qt major '
136-
'versions may not work as expected.'
137-
)
138-
break
139-
try:
140-
QtWidgets.QApplication.setHighDpiScaleFactorRoundingPolicy(
141-
QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
142-
except AttributeError: # Only for Qt>=5.14.
143-
pass
144-
qApp = QtWidgets.QApplication(["matplotlib"])
145-
if sys.platform == "darwin":
146-
image = str(cbook._get_data_path('images/matplotlib.svg'))
147-
icon = QtGui.QIcon(image)
148-
qApp.setWindowIcon(icon)
149-
qApp.lastWindowClosed.connect(qApp.quit)
150-
cbook._setup_new_guiapp()
106+
app = QtWidgets.QApplication.instance()
107+
108+
# Create a new QApplication and configure if if non exists yet, as only one
109+
# QApplication can exist at a time.
110+
if app is None:
111+
# display_is_valid returns False only if on Linux and neither X11
112+
# nor Wayland display can be opened.
113+
if not mpl._c_internal_utils.display_is_valid():
114+
raise RuntimeError('Invalid DISPLAY variable')
115+
116+
# Check to make sure a QApplication from a different major version
117+
# of Qt is not instantiated in the process
118+
if QT_API in {'PyQt6', 'PySide6'}:
119+
other_bindings = ('PyQt5', 'PySide2')
120+
elif QT_API in {'PyQt5', 'PySide2'}:
121+
other_bindings = ('PyQt6', 'PySide6')
151122
else:
152-
qApp = app
123+
raise RuntimeError("Should never be here")
124+
125+
for binding in other_bindings:
126+
mod = sys.modules.get(f'{binding}.QtWidgets')
127+
if mod is not None and mod.QApplication.instance() is not None:
128+
other_core = sys.modules.get(f'{binding}.QtCore')
129+
_api.warn_external(
130+
f'Matplotlib is using {QT_API} which wraps '
131+
f'{QtCore.qVersion()} however an instantiated '
132+
f'QApplication from {binding} which wraps '
133+
f'{other_core.qVersion()} exists. Mixing Qt major '
134+
'versions may not work as expected.'
135+
)
136+
break
137+
try:
138+
QtWidgets.QApplication.setAttribute(
139+
QtCore.Qt.AA_EnableHighDpiScaling)
140+
except AttributeError: # Only for Qt>=5.6, <6.
141+
pass
142+
try:
143+
QtWidgets.QApplication.setHighDpiScaleFactorRoundingPolicy(
144+
QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
145+
except AttributeError: # Only for Qt>=5.14.
146+
pass
147+
app = QtWidgets.QApplication(["matplotlib"])
148+
if sys.platform == "darwin":
149+
image = str(cbook._get_data_path('images/matplotlib.svg'))
150+
icon = QtGui.QIcon(image)
151+
app.setWindowIcon(icon)
152+
app.lastWindowClosed.connect(app.quit)
153+
cbook._setup_new_guiapp()
153154

154155
try:
155-
qApp.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps) # Only for Qt<6.
156+
app.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps) # Only for Qt<6.
156157
except AttributeError:
157158
pass
158159

160+
return app
161+
159162

160163
def _allow_super_init(__init__):
161164
"""
@@ -419,7 +422,7 @@ def _get_key(self, event):
419422

420423
def flush_events(self):
421424
# docstring inherited
422-
qApp.processEvents()
425+
QtWidgets.QApplication.instance().processEvents()
423426

424427
def start_event_loop(self, timeout=0):
425428
# docstring inherited
@@ -1022,7 +1025,7 @@ def trigger(self, *args):
10221025
class ToolCopyToClipboardQT(backend_tools.ToolCopyToClipboardBase):
10231026
def trigger(self, *args, **kwargs):
10241027
pixmap = self.canvas.grab()
1025-
qApp.clipboard().setPixmap(pixmap)
1028+
QtWidgets.QApplication.instance().clipboard().setPixmap(pixmap)
10261029

10271030

10281031
FigureManagerQT._toolbar2_class = NavigationToolbar2QT
@@ -1036,5 +1039,6 @@ class _BackendQT(_Backend):
10361039

10371040
@staticmethod
10381041
def mainloop():
1039-
with _maybe_allow_interrupt(qApp):
1040-
qt_compat._exec(qApp)
1042+
qapp = QtWidgets.QApplication.instance()
1043+
with _maybe_allow_interrupt(qapp):
1044+
qt_compat._exec(qapp)

lib/matplotlib/tests/test_backends_interactive.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,9 @@ def _implqt5agg():
267267
assert 'PyQt5' in sys.modules or 'pyside2' in sys.modules
268268

269269
import matplotlib.backends.backend_qt5
270-
matplotlib.backends.backend_qt5.qApp
270+
with pytest.warns(DeprecationWarning,
271+
match="QtWidgets.QApplication.instance"):
272+
matplotlib.backends.backend_qt5.qApp
271273

272274

273275
def _implcairo():
@@ -279,7 +281,9 @@ def _implcairo():
279281
assert 'PyQt5' in sys.modules or 'pyside2' in sys.modules
280282

281283
import matplotlib.backends.backend_qt5
282-
matplotlib.backends.backend_qt5.qApp
284+
with pytest.warns(DeprecationWarning,
285+
match="QtWidgets.QApplication.instance"):
286+
matplotlib.backends.backend_qt5.qApp
283287

284288

285289
def _implcore():
@@ -289,7 +293,10 @@ def _implcore():
289293
assert 'PyQt6' not in sys.modules
290294
assert 'pyside6' not in sys.modules
291295
assert 'PyQt5' in sys.modules or 'pyside2' in sys.modules
292-
matplotlib.backends.backend_qt5.qApp
296+
297+
with pytest.warns(DeprecationWarning,
298+
match="QtWidgets.QApplication.instance"):
299+
matplotlib.backends.backend_qt5.qApp
293300

294301

295302
def test_qt5backends_uses_qt5():
@@ -410,11 +417,25 @@ def _lazy_headless():
410417

411418

412419
@pytest.mark.skipif(sys.platform != "linux", reason="this a linux-only test")
413-
@pytest.mark.backend('QtAgg', skip_on_importerror=True)
420+
@pytest.mark.backend('Qt5Agg', skip_on_importerror=True)
414421
def test_lazy_linux_headless():
415422
proc = _run_helper(_lazy_headless, timeout=_test_timeout, MPLBACKEND="")
416423

417424

425+
def _qApp_warn_impl():
426+
import matplotlib.backends.backend_qt
427+
import pytest
428+
429+
with pytest.warns(
430+
DeprecationWarning, match="QtWidgets.QApplication.instance"):
431+
matplotlib.backends.backend_qt.qApp
432+
433+
434+
@pytest.mark.backend('QtAgg', skip_on_importerror=True)
435+
def test_qApp_warn():
436+
_run_helper(_qApp_warn_impl, timeout=_test_timeout)
437+
438+
418439
def _test_number_of_draws_script():
419440
import matplotlib.pyplot as plt
420441

0 commit comments

Comments
 (0)