用泡菜装载或倾倒时如何停止动画QCursor冻结? [英] How to stop an animated QCursor from freezing when loading or dumping with pickle?

查看:83
本文介绍了用泡菜装载或倾倒时如何停止动画QCursor冻结?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

作为对

As a follow-up question to this post, I was wondering if it is possible to extend the functionality of the cursor so that when pickle is used to dump or save data, the animation of the cursor doesn't freeze.

from PyQt5 import QtCore, QtGui, QtWidgets
import pickle
import gzip
import numpy as np


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 Progress(QtWidgets.QDialog):
    def __init__(self):
        super().__init__()
        progress = QtWidgets.QProgressBar()
        lay = QtWidgets.QVBoxLayout(self)
        lay.addWidget(progress)


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)
        dump_btn = QtWidgets.QPushButton("dump", clicked=self.dump)
        load_btn = QtWidgets.QPushButton("load", clicked=self.load)

        self.file = 'test'

        text_edit = QtWidgets.QTextEdit()
        self.popup = None

        self._manager = ManagerCursor(self)
        movie = QtGui.QMovie('../assets/comet_resized.gif')
        self._manager.setMovie(movie)
        self._manager.setWidget(self)

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

    @QtCore.pyqtSlot()
    def dump(self):
        self._manager.start()
        self.popup = Progress()
        self.popup.show()
        data = [np.full(1000, 1000) for i in range(100000)]
        with gzip.open(self.file, 'wb') as output_file:
            pickle.dump(data, output_file, pickle.HIGHEST_PROTOCOL)

    def load(self):
        self._manager.start()
        self.popup = Progress()
        self.popup.show()
        with gzip.open(self.file, 'rb') as input_file:
            data = pickle.load(input_file)

    @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_())

推荐答案

繁重的任务不应该在主线程中执行,因为它们通过冻结GUI来阻塞事件循环,必须在另一个线程中执行.

Heavy tasks should not be executed in the main thread since they block the event loop by freezing the GUI, it must be executed in another thread.

from functools import partial
import gzip
import pickle

from PyQt5 import QtCore, QtGui, QtWidgets

import numpy as np


class Worker(QtCore.QObject):
    dumpStarted = QtCore.pyqtSignal()
    dumpFinished = QtCore.pyqtSignal()

    dataChanged = QtCore.pyqtSignal(object)
    loadStarted = QtCore.pyqtSignal()
    loadFinished = QtCore.pyqtSignal()

    @QtCore.pyqtSlot(str, object)
    def dump(self, filename, data):
        self.dumpStarted.emit()
        with gzip.open(filename, "wb") as output_file:
            pickle.dump(data, output_file, pickle.HIGHEST_PROTOCOL)
        self.dumpFinished.emit()

    @QtCore.pyqtSlot(str)
    def load(self, filename):
        self.loadStarted.emit()
        with gzip.open(filename, "rb") as input_file:
            data = pickle.load(input_file)
            self.dataChanged.emit(data)
        self.loadFinished.emit()


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)
        dump_btn = QtWidgets.QPushButton("dump", clicked=self.dump)
        load_btn = QtWidgets.QPushButton("load", clicked=self.load)

        self.file = "test"

        text_edit = QtWidgets.QTextEdit()
        self.popup = None

        self._manager = ManagerCursor(self)
        movie = QtGui.QMovie("giphy.gif")
        self._manager.setMovie(movie)
        self._manager.setWidget(self)

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

        thread = QtCore.QThread(self)
        thread.start()

        self._worker = Worker()
        self._worker.moveToThread(thread)

        self._worker.dumpStarted.connect(self._manager.start)
        self._worker.dumpFinished.connect(self._manager.stop)
        self._worker.dumpStarted.connect(partial(dump_btn.setEnabled, False))
        self._worker.dumpFinished.connect(partial(dump_btn.setEnabled, True))

        self._worker.loadStarted.connect(self._manager.start)
        self._worker.loadFinished.connect(self._manager.stop)
        self._worker.loadStarted.connect(partial(load_btn.setEnabled, False))
        self._worker.loadFinished.connect(partial(load_btn.setEnabled, True))
        self._worker.dataChanged.connect(self.on_data_changed)

    @QtCore.pyqtSlot()
    def dump(self):
        data = [np.full(1000, 1000) for i in range(100000)]
        wrapper = partial(self._worker.dump, self.file, data)
        QtCore.QTimer.singleShot(0, wrapper)

    @QtCore.pyqtSlot()
    def load(self):
        wrapper = partial(self._worker.load, self.file)
        QtCore.QTimer.singleShot(0, wrapper)

    @QtCore.pyqtSlot(object)
    def on_data_changed(self, data):
        print(data)

    @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_())

这篇关于用泡菜装载或倾倒时如何停止动画QCursor冻结?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆