Как я могу изменить форму курсора с помощью PyQt?

У меня есть простое приложение, которое запускает процесс, который может длиться несколько минут до завершения. Я пытаюсь указать пользователю, что он обрабатывает запрос, например, изменение курсора на песочные часы.

Но я не могу заставить его работать правильно. Все мои попытки заканчивались либо ошибкой, либо не имели никакого эффекта. И я, кажется, неправильно называю формы курсора, так как PyQt4.Qt.WaitCursor возвращает ошибку, что модуль его не содержит.

Как правильно указать пользователю, что процесс запущен?


person TimothyAWiseman    schedule 21.11.2011    source источник


Ответы (7)


Я думаю, что QApplication.setOverrideCursor — это то, что вам нужно:

PyQt5:

from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication
...
QApplication.setOverrideCursor(Qt.WaitCursor)
# do lengthy process
QApplication.restoreOverrideCursor()

PyQt4:

from PyQt4.QtCore import Qt
from PyQt4.QtGui import QApplication
...
QApplication.setOverrideCursor(Qt.WaitCursor)
# do lengthy process
QApplication.restoreOverrideCursor()
person ekhumoro    schedule 21.11.2011
comment
Примечание. Это работает в PyQt4. Однако PySide 1.2.2 под Linux (Ubuntu 16.10) сообщает X Error: BadCursor (недопустимый параметр курсора) 6 Основной код операции: 2 (X_ChangeWindowAttributes) Идентификатор ресурса: 0xa. Подача PySide setOverrideCursor(Qt.WaitCursor) вместо setOverrideCursor(QCursor(Qt.WaitCursor)) работает, несмотря на документацию, в которой говорится, что QCursor() необходим. При некоторых обстоятельствах возникает аналогичная ошибка при восстановлении. Похоже, это известная ошибка PySide. - person Ubuntourist; 04.12.2016
comment
@Убунтурист. Спасибо - я могу подтвердить ошибку в PySide. Класс QCursor имеет конструктор копирования, который принимает значение перечисления Qt.CursorShape, поэтому на самом деле нет необходимости использовать сам QCursor. Я полагаю, что создание QCursor на стороне Qt решает проблему с PySide. - person ekhumoro; 04.12.2016
comment
По какой-то причине это решение работает для меня в PyQt5, но для восстановления обычного курсора требуется потеря фокуса (alt + tab или перемещение вокруг других виджетов, которые также меняют курсор, как любой виджет ввода). QApplication.restoreOverrideCursor() один ничего не сделает, если я не буду двигаться. - person Guimoute; 17.01.2019
comment
@Guimoute Вы должны опубликовать новый вопрос об этом и предоставить минимально воспроизводимый пример. - person ekhumoro; 17.01.2019

Хотя ответы Кэмерона и Дэвида отлично подходят для установки курсора ожидания для всей функции, я считаю, что диспетчер контекста лучше всего подходит для установки курсора ожидания для фрагментов кода:

from contextlib import contextmanager
from PyQt4 import QtCore
from PyQt4.QtGui import QApplication, QCursor

@contextmanager
def wait_cursor():
    try:
        QApplication.setOverrideCursor(QCursor(QtCore.Qt.WaitCursor))
        yield
    finally:
        QApplication.restoreOverrideCursor()

Затем поместите длинный код процесса в блок with:

with wait_cursor():
    # do lengthy process
    pass
person dbc    schedule 11.02.2016
comment
Это самый простой, самый элегантный и универсальный - person Oliver; 28.01.2017

решение эхуморо правильное. Это решение является модификацией в угоду стилю. Я использовал то, что сделал эхумор, но использовал декоратор Python.

from PyQt4.QtCore import Qt
from PyQt4.QtGui import QApplication, QCursor, QMainWidget

def waiting_effects(function):
    def new_function(self):
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        try:
            function(self)
        except Exception as e:
            raise e
            print("Error {}".format(e.args[0]))
        finally:
            QApplication.restoreOverrideCursor()
    return new_function

Я могу просто поместить декоратор на любой метод, на котором я хотел бы, чтобы счетчик был активен.

class MyWigdet(QMainWidget):

    # ...

    @waiting_effects
    def doLengthyProcess(self):
        # do lengthy process
        pass
person Cameron White    schedule 30.11.2013
comment
Декоратор отличная идея, спасибо. Я бы рекомендовал вызывать function(*args, **kwargs), возвращать возвращаемое значение function и заключать его вызов в блок try...finally для еще большего удобства. Кажется, я не могу поместить это в этот комментарий, поэтому в следующий раз я попытаюсь отредактировать ваше решение. - person Stephan A. Terre; 12.01.2015
comment
Изменение блока try на return function(*args, **kwargs) также может быть полезным, по крайней мере, в моем случае. - person Arthur Endlein; 23.01.2017

Лучше так:

def waiting_effects(function):
    def new_function(*args, **kwargs):
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        try:
            return function(*args, **kwargs)
        except Exception as e:
            raise e
            print("Error {}".format(e.args[0]))
        finally:
            QApplication.restoreOverrideCursor()
    return new_function
person David Miró    schedule 12.11.2015

По моему мнению, лучший способ добавить курсор — это использовать декораторы. Таким образом, вы можете запустить любую функцию, просто добавив курсор к этой функции в качестве декоратора.

import decorator
@decorator.decorator
def showWaitCursor(func, *args, **kwargs):
    QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)
    try:
        return func(*args, **kwargs)
    finally:
        QtWidgets.QApplication.restoreOverrideCursor()

@showWaitCursor
def youFunc():
    # Long process
person Sunil Kumar Nerella    schedule 21.11.2019

Я нахожу, что мне часто нужно делать:

QApplication.setOverrideCursor(Qt.WaitCursor)
QApplication.ProcessEvents()
...
QApplication.restoreOverrideCursor()

Однако! Во время инициализации графического интерфейса я обнаружил, что по какой-то причине необходимо внести изменения:

self.ui.show()  # Or your equivalent code to show the widget
QApplication.processEvents()
QApplication.setOverrideCursor(Qt.WaitCursor)
app.processEvents()
...
QApplication.restoreOverrideCursor()

В противном случае курсор не «восстанавливается», пока мышь не пройдет над виджетом, который его изменяет (например, QLineEdit), подобно тому, что упоминал @Guimoute

person Nat    schedule 06.04.2020

Вы можете использовать класс QgsTemporaryCursorOverride https://qgis.org/api/classQgsTemporaryCursorOverride.html

Но в Python вы должны использовать контекстный менеджер Python, который теперь включен в PyQGIS:

from qgis.PyQt.QtCore import Qt
from qgis.utils import OverrideCursor

with OverrideCursor(Qt.WaitCursor):
    do_a_slow(operation)
person etrimaille    schedule 03.06.2021