【问题标题】:Why is my loading screen not showing up using QThread?为什么我的加载屏幕没有使用 QThread 显示?
【发布时间】:2020-07-25 11:54:07
【问题描述】:

我正在制作一个桌面应用程序,一旦数据库被加载,我想显示一个加载屏幕。一个简单的搜索让我在 QLabel 中使用带有 QThread 对象的 gif 文件。但就我而言,QThread 不会显示任何内容。 该线程工作正常,但我的实现有问题,我无法弄清楚是什么。我的示例代码如下:

from PyQt5.QtWidgets import QWidget, QLabel, QVBoxLayout, QDialog, QApplication, QPushButton
from PyQt5.QtCore import QThread
from PyQt5.QtGui import QMovie
import sys
import time


class myThread(QThread):
    def run(self):

        test = QWidget()   # Only creating this to give parent to QDialog and QLabel objects in upcoming lines
        dialog = QDialog(test)

        vbox = QVBoxLayout()
        lbl = QLabel(test)
        self.moviee = QMovie('Loading.gif')
        lbl.setMovie(self.moviee)
        self.moviee.start()
        vbox.addWidget(lbl)
        dialog.setLayout(vbox)
        dialog.show()

    def stop(self):
        self.moviee.stop()


class Main(QWidget):
    def __init__(self):
        super().__init__()

        print('Thread is to be called here...')
        thread = myThread()
        thread.run()

        print('Thread has been called...')

        btn= QPushButton('Test button')
        vbox = QVBoxLayout()
        vbox.addWidget(btn)
        self.setLayout(vbox)

        time.sleep(5)     # sleep function used to emulate funcitons in actual program

        # thread.stop()
        self.show()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Main()
    sys.exit(app.exec_())

带有QMovie 对象的代码块在Main 循环中工作正常,所以我的QThread 实现肯定有问题。

【问题讨论】:

  • 您是否尝试调用thread.run(),但QThread 只能通过start() 启动。 doc.qt.io/qtforpython/PySide2/QtCore/…
  • 不应在主 Qt 线程之外发生 GUI 元素访问(包括创建)。不应在主 Qt 线程中发生任何阻塞函数(例如对 sleep 的调用)。 QThreads 正是那些必须被实现和用来完成“繁重工作”的线程,与 GUI 相关的所有内容都应该在主 Qt 线程中,而不是相反。
  • @VladimirBershov thread.run() 方法也是如此。没有线索。
  • @AsadUllahButt 你不能在非主线程中创建 GUI 元素。这行不通。
  • @AsadUllahButt 假设您已经更正了 Vladimir 报告的内容(该线程应该通过调用 start() 开始),由于我之前告诉过的原因,它无法完全正确工作:QMovie与 QLabel 交互,它是一个 GUI 对象,因此必须始终只能从主 Qt 线程访问。您不能从另一个线程创建/访问/修改 QWidget(包括在 QLabel 中播放 QMovie)。您必须在主线程中离开创建 QLabel播放 QMovie。这就是 Qt 的工作方式(一个,AFAIK,就像所有基于 GUI 的框架一样)。

标签: python multithreading pyqt pyqt5 qthread


【解决方案1】:

您的代码存在几个问题。正如@musicamante 所说,您不能在主线程之外创建小部件。这意味着您无法在myThread.run 中创建对话框。相反,您可以将对话框的管理移至Main,并使用槽和信号来打开和关闭对话框。

其次,像time.sleep(5) 这样的耗时任务都应该放在myThread.run 中。

考虑到这一点,你可以做这样的事情

from PyQt5.QtWidgets import QWidget, QLabel, QVBoxLayout, QDialog, QApplication, QPushButton
from PyQt5.QtCore import QThread
from PyQt5.QtGui import QMovie
import sys
import time


class myThread(QThread):
    def run(self):
        # time consuming actions
        time.sleep(5)


class Main(QWidget):

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

        print('Thread is to be called here...')
        self.load()
        print('Thread has been called...')

        btn= QPushButton('Test button')
        vbox = QVBoxLayout()
        vbox.addWidget(btn)
        self.setLayout(vbox)
        self.show()

    def load(self):
        # setup dialog
        dialog = QDialog(self)
        vbox = QVBoxLayout()
        lbl = QLabel(self)
        self.moviee = QMovie('Loading.gif')
        lbl.setMovie(self.moviee)
        self.moviee.start()
        vbox.addWidget(lbl)
        dialog.setLayout(vbox)

        # setup thread
        thread = myThread()
        thread.finished.connect(thread.deleteLater)
        thread.finished.connect(dialog.close)
        thread.finished.connect(dialog.deleteLater)
        thread.start()

        dialog.exec()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Main()
    app.exec()

【讨论】:

  • 非常感谢朋友。我的程序启动时间有点长(7-8 秒),在此期间它 (1.)Main 类中构建 GUI,(2.) 绘制一些图表,(3.) 构建了一些数据库等。所以我想将第一部分(即 GUI 的创建)移到线程中。如果这不可能,开发者社区遵循什么约定?如何在不移动线程类中的 GUI 创建的情况下让我的程序看起来加载速度非常快?
猜你喜欢
  • 2021-03-01
  • 2019-07-13
  • 2021-10-09
  • 1970-01-01
  • 1970-01-01
  • 2022-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多