有没有办法创建自定义动画/gif QCursor?

3

我试图创建一个自定义动画光标,以替换常规光标,用于类似这里这里的长时间处理过程,但是,我希望我的光标是动画的,例如使用gif,就像标准的Qt.WaitCursor一样。我该如何做?我发现这个解决方案涉及到动画系统托盘图标,但我没能使其适用于光标图标。

附注:当我尝试执行第一个链接中所述的pm.setAlphaChannel(bm)时,它对我不起作用,并出现以下错误:

'AttributeError: QPixmap' object has no attribute 'setAlphaChannel'

这很奇怪,因为根据文档,QPixmap确实有一个setAlphaChannel方法。


1
你所指的动画是什么? - eyllanesc
感谢你的问题!我添加了这些信息。通过动画,我的意思是像 GIF 这样的东西。在 Windows 10 中,比如说有一个旋转的蓝色圆圈。我想把它替换掉。 - mapf
1个回答

4

一个可能的解决方案是创建一个类来处理给定 widget 的光标更新。在下面的例子中,当按下开始按钮时设置光标,当停止按钮被按下时恢复光标:

from PyQt5 import QtCore, QtGui, QtWidgets

class ManagerCursor(QtCore.QObject):
    def __init__(self, parent=None):
        super(ManagerCursor, self).__init__(parent)
        self._movie = None
        self._widget = None
        self._last_cursor = None

    def setMovie(self, movie):
        if isinstance(self._movie, QtGui.QMovie):
            if not self._movie != QtGui.QMovie.NotRunning:
                self._movie.stop()
            del self._movie
        self._movie = movie
        self._movie.frameChanged.connect(self.on_frameChanged)
        self._movie.started.connect(self.on_started)
        self._movie.finished.connect(self.restore_cursor)

    def setWidget(self, widget):
        self._widget = widget

    @QtCore.pyqtSlot()
    def on_started(self):
        if self._widget is not None:
            self._last_cursor = self._widget.cursor()

    @QtCore.pyqtSlot()
    def restore_cursor(self):
        if self._widget is not None:
            if self._last_cursor is not None:
                self._widget.setCursor(self._last_cursor)
        self._last_cursor = None

    @QtCore.pyqtSlot()
    def start(self):
        if self._movie is not None:
            self._movie.start()

    @QtCore.pyqtSlot()
    def stop(self):
        if self._movie is not None:
            self._movie.stop()
            self.restore_cursor()

    @QtCore.pyqtSlot()
    def on_frameChanged(self):
        pixmap = self._movie.currentPixmap()
        cursor = QtGui.QCursor(pixmap)
        if self._widget is not None:
            if self._last_cursor is None:
                self._last_cursor = self._widget.cursor()
            self._widget.setCursor(cursor)


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)
        start_btn = QtWidgets.QPushButton("start", clicked=self.on_start)
        stop_btn = QtWidgets.QPushButton("stop", clicked=self.on_stop)

        self._manager = ManagerCursor(self)
        movie = QtGui.QMovie("loading-icon.gif")
        self._manager.setMovie(movie)
        self._manager.setWidget(self)

        lay = QtWidgets.QVBoxLayout(self)
        lay.addWidget(start_btn)
        lay.addWidget(stop_btn)
        lay.addStretch()

    @QtCore.pyqtSlot()
    def on_start(self):
        self._manager.start()

    @QtCore.pyqtSlot()
    def on_stop(self):
        self._manager.stop()

if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.resize(640, 480)
    w.show()
    sys.exit(app.exec_())

1
@mapf 为了提高效率,Qt 不使用异常。在这种情况下,假设您已经创建了一个空的 QMovie,如果您想验证 gif 是否已加载,应该使用 isValid() 方法:print(movie.isValid())。 - eyllanesc
好的,知道了!谢谢! - mapf
抱歉如果我有点挑剔,但我有一个后续问题:当我使用这里描述的方法,即QApplication.setOverrideCursor(Qt.WaitCursor)时,光标保持不变(如保持旋转循环)并且无论您悬停在哪里都会保持更新。然而,使用您的方法则不是这种情况。例如,如果您将鼠标悬停在窗口的框架或文本字段上,则光标会更改,并且由于某种原因,当我使用pickle(第二个线程)加载数据时,光标没有更新。 - mapf
嗨,我想再次提出我的问题,因为我刚刚回来尝试并再次解决这个问题。如何确保在悬停在辅助窗口或文本字段上时,等待光标也能正常工作? - mapf
1
@mapf 如果您有另一个问题(可能类似于我回答的问题),那么您必须创建一个新的发布。 - eyllanesc
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接