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

Skip to content

Deprecate backend_qt.qApp. #22503

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 2 commits into from
Mar 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions doc/api/next_api_changes/deprecations/22503-AL.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
``backend_qt.qApp``
~~~~~~~~~~~~~~~~~~~
... is deprecated. Use ``QtWidgets.QApplication.instance()`` instead.
122 changes: 63 additions & 59 deletions lib/matplotlib/backends/backend_qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,70 +92,73 @@
}


# make place holder
qApp = None
@_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():
"""
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: # Only for Qt>=5.6, <6.
pass

# 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.setHighDpiScaleFactorRoundingPolicy(
QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
except AttributeError: # Only for Qt>=5.14.
pass
qApp = QtWidgets.QApplication(["matplotlib"])
if sys.platform == "darwin":
image = str(cbook._get_data_path('images/matplotlib.svg'))
icon = QtGui.QIcon(image)
qApp.setWindowIcon(icon)
qApp.lastWindowClosed.connect(qApp.quit)
cbook._setup_new_guiapp()
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:
qApp = app
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:
qApp.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps) # Only for Qt<6.
app.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps) # Only for Qt<6.
except AttributeError:
pass

return app


def _allow_super_init(__init__):
"""
Expand Down Expand Up @@ -419,7 +422,7 @@ def _get_key(self, event):

def flush_events(self):
# docstring inherited
qApp.processEvents()
QtWidgets.QApplication.instance().processEvents()

def start_event_loop(self, timeout=0):
# docstring inherited
Expand Down Expand Up @@ -1022,7 +1025,7 @@ def trigger(self, *args):
class ToolCopyToClipboardQT(backend_tools.ToolCopyToClipboardBase):
def trigger(self, *args, **kwargs):
pixmap = self.canvas.grab()
qApp.clipboard().setPixmap(pixmap)
QtWidgets.QApplication.instance().clipboard().setPixmap(pixmap)


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

@staticmethod
def mainloop():
with _maybe_allow_interrupt(qApp):
qt_compat._exec(qApp)
qapp = QtWidgets.QApplication.instance()
with _maybe_allow_interrupt(qapp):
qt_compat._exec(qapp)
29 changes: 25 additions & 4 deletions lib/matplotlib/tests/test_backends_interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,9 @@ def _implqt5agg():
assert 'PyQt5' in sys.modules or 'pyside2' in sys.modules

import matplotlib.backends.backend_qt5
matplotlib.backends.backend_qt5.qApp
with pytest.warns(DeprecationWarning,
match="QtWidgets.QApplication.instance"):
matplotlib.backends.backend_qt5.qApp


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

import matplotlib.backends.backend_qt5
matplotlib.backends.backend_qt5.qApp
with pytest.warns(DeprecationWarning,
match="QtWidgets.QApplication.instance"):
matplotlib.backends.backend_qt5.qApp


def _implcore():
Expand All @@ -289,7 +293,10 @@ def _implcore():
assert 'PyQt6' not in sys.modules
assert 'pyside6' not in sys.modules
assert 'PyQt5' in sys.modules or 'pyside2' in sys.modules
matplotlib.backends.backend_qt5.qApp

with pytest.warns(DeprecationWarning,
match="QtWidgets.QApplication.instance"):
matplotlib.backends.backend_qt5.qApp


def test_qt5backends_uses_qt5():
Expand Down Expand Up @@ -410,11 +417,25 @@ def _lazy_headless():


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


def _qApp_warn_impl():
import matplotlib.backends.backend_qt
import pytest

with pytest.warns(
DeprecationWarning, match="QtWidgets.QApplication.instance"):
matplotlib.backends.backend_qt.qApp


@pytest.mark.backend('QtAgg', skip_on_importerror=True)
def test_qApp_warn():
_run_helper(_qApp_warn_impl, timeout=_test_timeout)


def _test_number_of_draws_script():
import matplotlib.pyplot as plt

Expand Down