gpt4 book ai didi

python - 如果从线程启动,QMovie 不会启动

转载 作者:行者123 更新时间:2023-12-03 13:18:32 25 4
gpt4 key购买 nike

开发者小伙伴们!我有一个关于 Qt 和多线程的问题。
=== 简短版 ============================================
Qt可以做我想做的事吗?即(1)显示装载机; (2)在后台下载一个gif; (3) 下载后的gif在主窗口显示?
=== 长版 ==============================================
我有这样的想法,当我按下按钮时,它:

  • 显示装载机;
  • 激活从网络下载 gif 的线程;
  • 用下载的替换隐藏在主窗口中的默认gif并显示
  • 隐藏装载机;

  • 我遇到的问题是当下载的 gif 显示时, 它被“卡住”或只显示第一帧 .除此之外一切都很好。但是,它需要在加载器隐藏后动画化。
    提到 here

    So the Qt event loop is responsible for executing your code in response to various things that happen in your program, but while it is executing your code, it can't do anything else.


    我相信这是问题的核心。还建议

    It is recommended to use a QThread with Qt rather than a Python thread, but a Python thread will work fine if you don't ever need to communicate back to the main thread from your function.


    由于我的线程替换了默认gif的内容,我相信 它确实会返回 :(
    请在下面找到我的代码:)
    import sys
    from time import sleep

    from PyQt5.QtCore import QThread
    from PyQt5.QtGui import QMovie
    from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QLabel


    class ChangeGif(QThread):
    """
    A worker that:
    1 - waits for 1 second;
    2 - changes the content of the default gif;
    3 - shows the default gif with new contents and hide the loader.
    """

    def __init__(self, all_widgets):
    QThread.__init__(self)
    self.all = all_widgets

    def run(self):
    sleep(1)
    self.new_gif_file = QMovie("files/new.gif") # This is the "right" gif that freezes :(
    self.new_gif_file.start()
    self.all.gif_label.setMovie(self.new_gif_file)
    self.all.gif_label.show()
    self.all.loader_label.hide()


    class MainWindow(QWidget):
    """
    Main window that shows a button. If you push the button, the following happens:
    1 - a loader is shown;
    2 - a thread in background is started;
    3 - the thread changes the contents of the default gif;
    4 - when the gif is replaced, the loader disappears and the default gif with the new content is shown
    """
    def __init__(self):
    super(MainWindow, self).__init__()

    # BUTTON
    self.button = QPushButton("Push me", self)
    self.button.setFixedSize(100, 50)
    self.button.clicked.connect(lambda: self.change_gif())

    # DEFAULT GIF
    self.gif_file = QMovie("files/default.gif") # This is the "wrong" gif
    self.gif_file.start()
    self.gif_label = QLabel(self)
    self.gif_label.setMovie(self.gif_file)
    self.gif_label.move(self.button.width(), 0)
    self.gif_label.hide()

    # LOADER
    self.loader_file = QMovie("files/loader.gif")
    self.loader_file.start()
    self.loader_label = QLabel(self)
    self.loader_label.setMovie(self.loader_file)
    self.loader_label.move(self.button.width(), 0)
    self.loader_label.hide()

    # WINDOW SETTINGS
    self.setFixedSize(500, 500)
    self.show()

    def change_gif(self):
    self.loader_label.show()
    self.worker = ChangeGif(self)
    self.worker.start()


    app = QApplication(sys.argv)
    window = MainWindow()
    app.exec_()

    最佳答案

    除了不应在主 Qt 线程之外访问或创建任何 UI 元素这一事实之外,这对于使用在其他线程中创建的对象的 UI 元素也是有效的。
    在您的特定情况下,这不仅意味着您不能在单独的线程中设置电影,而且您也不能在那里创建 QMovie。
    在下面的示例中,我打开一个本地文件,并使用信号将数据发送到主线程。从那里,我创建了一个 QBuffer 来将数据存储在 QMovie 可以使用的 IO 设备中。请注意,缓冲区和电影都必须具有持久引用,否则一旦函数返回它们就会被垃圾回收。

    from PyQt5.QtCore import QThread, QByteArray, QBuffer

    class ChangeGif(QThread):
    dataLoaded = pyqtSignal(QByteArray)
    def __init__(self, all_widgets):
    QThread.__init__(self)
    self.all = all_widgets

    def run(self):
    sleep(1)
    f = QFile('new.gif')
    f.open(f.ReadOnly)
    self.dataLoaded.emit(f.readAll())
    f.close()


    class MainWindow(QWidget):
    # ...
    def change_gif(self):
    self.loader_label.show()
    self.worker = ChangeGif(self)
    self.worker.dataLoaded.connect(self.applyNewGif)
    self.worker.start()

    def applyNewGif(self, data):
    # a persistent reference must be kept for both the buffer and the movie,
    # otherwise they will be garbage collected, causing the program to
    # freeze or crash
    self.buffer = QBuffer()
    self.buffer.setData(data)
    self.newGif = QMovie()
    self.newGif.setCacheMode(self.newGif.CacheAll)
    self.newGif.setDevice(self.buffer)
    self.gif_label.setMovie(self.newGif)
    self.newGif.start()
    self.gif_label.show()
    self.loader_label.hide()
    请注意,上面的示例仅用于说明目的,因为下载过程可以使用 QtNetwork 模块完成,它们异步工作并提供简单的信号和插槽来下载远程数据:
    from PyQt5.QtCore import QUrl
    from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest

    class MainWindow(QWidget):
    def __init__(self):
    # ...
    self.downloader = QNetworkAccessManager()

    def change_gif(self):
    self.loader_label.show()
    url = QUrl('https://path.to/animation.gif')
    self.device = self.downloader.get(QNetworkRequest(url))
    self.device.finished.connect(self.applyNewGif)

    def applyNewGif(self):
    self.loader_label.hide()
    self.newGif = QMovie()
    self.newGif.setDevice(self.device)
    self.gif_label.setMovie(self.newGif)
    self.newGif.start()
    self.gif_label.show()

    关于python - 如果从线程启动,QMovie 不会启动,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63979396/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com