PyQt:在其他操作 运行 时显示动画 GIF

PyQt: show animated GIF while other operations are running

我有一个正在加载的 GIF,我想在特定方法完成之前将其显示为加载屏幕。我的问题是 GIF 不会保留 运行 因为还有另一个功能在工作,并且 GIF 应该保留 运行 直到该功能完成。

import sys
import time
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QMovie
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow

def wait_till_function_done():
    time.sleep(5)

class Window(QMainWindow):

    def __init__(self):
        super().__init__()


class Splash:

    def showSplash(self):
        label = QLabel()
        label.setWindowFlag(Qt.SplashScreen)

        # label for displaying GIF
        self.movie = QMovie(".../Loading.gif")
        label.setMovie(self.movie)

        self.movie.start()
        label.show()

        # -------
        # Simulate the operation that is talking about.
        # The GIF should keep working and playing till the following simulation is done.
        # -------
        # But The GIF stop running and that is because the following line block Qt event

        wait_function_is_done()  # this line simulate this sentence: "keep showing loading screen till the operation is done"


if __name__ == "__main__":
    app = QApplication(sys.argv)
    main_window = Window()
    splash_obj = Splash()
    # start the Splash screen
    splash_obj.showSplash()
    # when it's done, it will be closed and main_app showen
    main_window.show()
    app.exec_()

我尝试将 QThread 与加载 gif 一起使用,因此它会在 method/operation 为 运行 时更改 gif 帧(并且应该在该功能被关闭时关闭完毕)。但这没有成功:

  1. 框架没有改变。 (我可以使用 app.processEvents() 这样可以更改帧)。
  2. 卡在了while循环中
class GIFThread(QThread):
    signal = pyqtSignal()

    def __init__(self):
        super(GIFThread, self).__init__()

class Splash:

    def showSplash(self):

        .....

        # init thread
        thread = GIFThread()
        thread.signal.connect(self.change_gif_frame)
        thread.start()

        # start changing GIF frames
        thread.signal.emit()

        # -------
        # Simulate the operation.
        time.sleep(5)

        # this should stop the thread and break the while loop
        thread.quit()

    def change_gif_frame(self):
        while True:
            self.movie.jumpToNextFrame()

可能最好使用 QSplashScreen for this, and use QThread 到 运行 应用程序在后台初始化。如有必要,可以通过自定义信号使用进度消息更新 splash-screen。这是一个基本演示,展示了如何实现它:

import sys
from PyQt5.QtCore import pyqtSignal, Qt, QThread
from PyQt5.QtGui import QMovie
from PyQt5.QtWidgets import QApplication, QSplashScreen, QMainWindow

class Worker(QThread):
    progressChanged = pyqtSignal(int)

    def run(self):
        for count in range(6):
            self.progressChanged.emit(count)
            self.sleep(1)
        self.progressChanged.emit(-1)

class Window(QMainWindow):
    def __init__(self):
        super().__init__()

class SplashScreen(QSplashScreen):
    def __init__(self, filepath, flags=0):
        super().__init__(flags=Qt.WindowFlags(flags))
        self.movie = QMovie(filepath, parent=self)
        self.movie.frameChanged.connect(self.handleFrameChange)
        self.movie.start()

    def updateProgress(self, count=0):
        if count == 0:
            message = 'Starting...'
        elif count > 0:
            message = f'Processing... {count}'
        else:
            message = 'Finished!'
        self.showMessage(
            message, Qt.AlignHCenter | Qt.AlignBottom, Qt.white)

    def handleFrameChange(self):
        pixmap = self.movie.currentPixmap()
        self.setPixmap(pixmap)
        self.setMask(pixmap.mask())

if __name__ == "__main__":

    app = QApplication(sys.argv)
    window = Window()
    splash = SplashScreen('anim.gif', Qt.WindowStaysOnTopHint)
    worker = Worker()
    worker.progressChanged.connect(splash.updateProgress)
    worker.finished.connect(
        lambda: (splash.finish(window), window.show()))
    splash.show()
    worker.start()
    app.exec_()