From 2fb04b754c0b11f70ae82b187bb50ba567986883 Mon Sep 17 00:00:00 2001 From: Martin Chase Date: Wed, 9 Jun 2021 13:32:24 -0700 Subject: [PATCH] Fix BusyCursor to use internal stack provided by setOverrideCursor/restoreOverrideCursor (#1827) * un-busy as many times as needed * lint * add test to prove cursor behavior * tentative change in the hopes that all supported qt versions behave properly * remove unnecessary code * use contextmanager decorator instead of class * use full path to WaitCursor * restore docstring; refactor variable for clarity * fix docstring whitespace * break up long lines * use variable to shorten instead --- pyqtgraph/widgets/BusyCursor.py | 47 ++++++++++++++------------------ tests/widgets/test_busycursor.py | 13 +++++++++ 2 files changed, 33 insertions(+), 27 deletions(-) create mode 100644 tests/widgets/test_busycursor.py diff --git a/pyqtgraph/widgets/BusyCursor.py b/pyqtgraph/widgets/BusyCursor.py index 142b8b28..4a878867 100644 --- a/pyqtgraph/widgets/BusyCursor.py +++ b/pyqtgraph/widgets/BusyCursor.py @@ -1,35 +1,28 @@ -from ..Qt import QtGui, QtCore, QT_LIB +# -*- coding: utf-8 -*- +from contextlib import contextmanager -__all__ = ['BusyCursor'] +from ..Qt import QtGui, QtCore -class BusyCursor(object): - """Class for displaying a busy mouse cursor during long operations. +__all__ = ["BusyCursor"] + + +@contextmanager +def BusyCursor(): + """ + Display a busy mouse cursor during long operations. Usage:: - with pyqtgraph.BusyCursor(): + with BusyCursor(): doLongOperation() May be nested. If called from a non-gui thread, then the cursor will not be affected. """ - active = [] - - def __enter__(self): - app = QtCore.QCoreApplication.instance() - isGuiThread = (app is not None) and (QtCore.QThread.currentThread() == app.thread()) - if isGuiThread and QtGui.QApplication.instance() is not None: - if QT_LIB == 'PySide': - # pass CursorShape rather than QCursor for PySide - # see https://bugreports.qt.io/browse/PYSIDE-243 - QtGui.QApplication.setOverrideCursor(QtCore.Qt.CursorShape.WaitCursor) - else: - QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor)) - BusyCursor.active.append(self) - self._active = True - else: - self._active = False - - def __exit__(self, *args): - if self._active: - BusyCursor.active.pop(-1) - if len(BusyCursor.active) == 0: - QtGui.QApplication.restoreOverrideCursor() + app = QtCore.QCoreApplication.instance() + in_gui_thread = (app is not None) and (QtCore.QThread.currentThread() == app.thread()) + try: + if in_gui_thread: + QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor)) + yield + finally: + if in_gui_thread: + QtGui.QApplication.restoreOverrideCursor() diff --git a/tests/widgets/test_busycursor.py b/tests/widgets/test_busycursor.py new file mode 100644 index 00000000..3bbb2624 --- /dev/null +++ b/tests/widgets/test_busycursor.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +import pyqtgraph as pg + +pg.mkQApp() + + +def test_nested_busy_cursors_clear_after_all_exit(): + with pg.BusyCursor(): + wait_cursor = pg.Qt.QtCore.Qt.CursorShape.WaitCursor + with pg.BusyCursor(): + assert pg.Qt.QtGui.QApplication.overrideCursor().shape() == wait_cursor, "Cursor should be waiting" + assert pg.Qt.QtGui.QApplication.overrideCursor().shape() == wait_cursor, "Cursor should be waiting" + assert pg.Qt.QtGui.QApplication.overrideCursor() is None, "No override cursor should be set"