PyQt5线程GUI不起作用

时间:2017-06-28 11:08:13

标签: python multithreading python-3.x pyqt pyqt5

我正在尝试加载一些需要30多秒的数据。在此期间,我希望用户看到一个小的GUI,其中显示"正在加载。",然后"正在加载..",然后"正在加载......&#34 ;然后"加载。"我做了一些阅读,我想我必须把它放在一个单独的线程中。我发现有一个类似问题的人建议解决方案是在正确的位置:

t = threading.Thread(target=self.test)
t.daemon = True
t.start()

在文件的下半部分,我有测试功能

def test(self):
    tmp = InfoMessage()

    while True:
        print(1)

和InfoMessage函数

from PyQt5 import uic, QtCore, QtGui, QtWidgets
import sys

class InfoMessage(QtWidgets.QDialog):
    def __init__(self, msg='Loading ', parent=None):
        try:
            super(InfoMessage, self).__init__(parent)
            uic.loadUi('ui files/InfoMessage.ui',self)

            self.setWindowTitle(' ')

            self.o_msg = msg
            self.msg = msg
            self.info_label.setText(msg)
            self.val = 0

            self.timer = QtCore.QTimer()
            self.timer.setInterval(500)
            self.timer.timeout.connect(self.update_message)
            self.timer.start()

            self.show()
        except BaseException as e:
            print(str(e))

    def update_message(self):
        self.val += 1
        self.msg += '.'

        if self.val < 20:
            self.info_label.setText(self.msg)
        else:
            self.val = 0
            self.msg = self.o_msg

        QtWidgets.QApplication.processEvents()

def main():
    app = QtWidgets.QApplication(sys.argv)      # A new instance of QApplication
    form = InfoMessage('Loading ')                  # We set the form to be our MainWindow (design)
    app.exec_()                                 # and execute the app

if __name__ == '__main__':                      # if we're running file directly and not importing it
    main()                                      # run the main function

当我单独运行InfoMessage函数时,它工作正常并且每0.5秒更新一次等。但是,当我将此作为加载文件的一部分时,GUI是空白的并且显示不正确。我知道它会留在测试函数中,因为那里有print语句。

有人能指出我正确的方向吗?我想我错过了几个步骤。

1 个答案:

答案 0 :(得分:5)

首先,有两种方法可以做到这一点。一种方法是使用Python内置线程模块。另一种方法是使用与PyQT更加集成的QThread库。通常,我建议使用QThread在PyQt中进行线程化。但只有在与PyQt进行任何交互时才需要QThread。

其次,我已从processEvents()删除了InfoMessage,因为它在您的特定情况下不起任何作用。

最后,将线程设置为守护进程意味着您的线程永远不会停止。大多数功能都不是这种情况。

import sys
import threading
import time

from PyQt5 import uic, QtCore, QtWidgets
from PyQt5.QtCore import QThread


def long_task(limit=None, callback=None):
    """
    Any long running task that does not interact with the GUI.
    For instance, external libraries, opening files etc..
    """
    for i in range(limit):
        time.sleep(1)
        print(i)
    if callback is not None:
        callback.loading_stop()


class LongRunning(QThread):
    """
    This class is not required if you're using the builtin
    version of threading.
    """
    def __init__(self, limit):
        super().__init__()
        self.limit = limit

    def run(self):
        """This overrides a default run function."""
        long_task(self.limit)


class InfoMessage(QtWidgets.QDialog):
    def __init__(self, msg='Loading ', parent=None):
        super(InfoMessage, self).__init__(parent)
        uic.loadUi('loading.ui', self)

        # Initialize Values
        self.o_msg = msg
        self.msg = msg
        self.val = 0

        self.info_label.setText(msg)
        self.show()

        self.timer = QtCore.QTimer()
        self.timer.setInterval(500)
        self.timer.timeout.connect(self.update_message)
        self.timer.start()

    def update_message(self):
        self.val += 1
        self.msg += '.'

        if self.val < 20:
            self.info_label.setText(self.msg)
        else:
            self.val = 0
            self.msg = self.o_msg

    def loading_stop(self):
        self.timer.stop()
        self.info_label.setText("Done")


class MainDialog(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super(MainDialog, self).__init__(parent)

        # QThread Version - Safe to use
        self.my_thread = LongRunning(limit=10)
        self.my_thread.start()
        self.my_loader = InfoMessage('Loading ')
        self.my_thread.finished.connect(self.my_loader.loading_stop)

        # Builtin Threading - Blocking - Do not use
        # self.my_thread = threading.Thread(
        #     target=long_task,
        #     kwargs={'limit': 10}
        # )
        # self.my_thread.start()
        # self.my_loader = InfoMessage('Loading ')
        # self.my_thread.join()  # Code blocks here
        # self.my_loader.loading_stop()

        # Builtin Threading - Callback - Use with caution
        # self.my_loader = InfoMessage('Loading ')
        # self.my_thread = threading.Thread(
        #     target=long_task,
        #     kwargs={'limit': 10,
        #             'callback': self.my_loader}
        # )
        # self.my_thread.start()


def main():
    app = QtWidgets.QApplication(sys.argv)
    dialog = MainDialog()
    app.exec_()

if __name__ == '__main__':
    main()

随时询问有关此代码的任何后续问题。

祝你好运。

修改 更新以显示如何在线程完成时运行代码。请注意新参数已添加到long_task函数。