【Python_PySide2学习笔记(九)】基于QThread实现 子线程更新 主线程/UI线程

基于QThread实现 子线程更新 主线程/UI线程

前言

基于QThread实现 子线程更新 主线程/UI线程。
在最近的项目中,GUI程序中使用信号-槽的方式实现某一模块功能,当模块的执行时间较长时,主界面会卡顿,直到模块中的功能执行完成后,主界面才会恢复正常。为解决此类问题,要使用多线程的方式;而在PySide2中 使用 Python 的多线程类 threading,在另一个线程直接操作界面,可能会导致意想不到的问题,比如:输出显示不全,甚至程序崩溃,所以要用到Qt特有的线程类QThread。

正文

【Python_PySide2学习笔记(九)】基于QThread实现 子线程更新 主线程/UI线程_第1张图片

项目的GUI主要基于PySide2实现,想要实现的功能是:当点击上传数据按键时,连接数据库,弹出提示框,并在报表中记录信息。
如上图所示,当只使用QPushButton的clicked事件,在clicked的槽函数中实现功能模块时,会发现当点击按键后,主线程会被堵塞,当槽函数功能运行完后,才会继续主线程UI界面刷新。

1、QThread的基本用法

a. 创建一个子类,继承自QThread

class NewThread(QThread, parent=None):
    def __init__(self):
        super(QThread,self).__init__(parent)

b. 在新定义的线程类中,在线程类内实例化一个Signal信号对象

	signal = Signal(object) # 自定义信号, object 为信号对象承载数据的类型

c. 重写run()方法,并发射信号

在run()方法中,是要在子线程中实现的功能;当然,也可以在NewTread线程类中定义其他的函数,并在run()方法中进行调用。

    def run(self):
    	# 具体要实现的功能
    	self.signal.emit(object)  # 功能实现完成后 发射信号

d. 创建新线程类的对象,并开启线程

开启线程使用线程的start()的方法,实际是调用的我们重写的run()方法

		thread = NewThread()
		thread.start()

e. 定义更新主线程UI界面的方法

    def update(self):
    	# 具体要实现的功能

f. 绑定 从线程类发出来的信号 到 界面更新的槽函数

		self.thread.signal.connect(self.update)  # 当信号发出时,执行槽函数

2、具体示例

a. 创建一个子类,继承自QThread

class ctConnectSqlThread(QThread):
    def __init__(self, parent=None):
        super(ctConnectSqlThread, self).__init__(parent)
        self.state = None  # 初始化数据库连接状态

b. 在新定义的线程类中,在线程类内实例化一个Signal信号对象

class ctConnectSqlThread(QThread):
    connected = Signal(object)  # 初始化信号

    def __init__(self, parent=None):
        super(ctConnectSqlThread, self).__init__(parent)
        self.state = None  # 初始化数据库连接状态

c. 重写run()方法,并发射信号

    def run(self):
        config = {
            "host": "",
            "port": ,
            "database": "",
            "charset": "",
            "user": "",
            "passwd": ""
        }  # 设置连接数据库的参数
        db = tMySqlHelper.ctMySql(**config)  # 实例化时就直接传参数
        self.state = db.connectInfo  # 数据库连接状态赋值
        self.connected.emit(self.state)  # 数据库连接状态 赋值完成后 发射信号
        self.sleep(1)  # 线程休眠1秒

d. 创建新线程类的对象,并开启线程

		self.uploadingThread = tThread.ctConnectSqlThread()  # 创建上传事件 线程
        self.ui.btnUploding.clicked.connect(self.__f_btnUploadingClicked)  # 上传数据按键按下事件连接槽函数
    # 上传数据按钮按下处理函数
    def __f_btnUploadingClicked(self):
        """
        function:  上传数据按钮按下处理函数
              in:  None
             out:  None
          return:  int >0 ok, <0 some wrong
          others:  handle function of the button loading up clicked
        """
        self.ui.tabWidget.setCurrentIndex(0)  # 切换到上传数据Tab
        self.ui.pteReport.appendPlainText(
            f"{datetime.datetime.now().strftime('%Y-%m-%d-%H:%M:%S')}: 尝试连接数据库...")  # 报表多行文本框追加信息
        self.uploadingThread.start()  # 线程开启

e. 定义更新主线程UI界面的方法

# 接收子线程信号后,更新UI函数
    def upLoadingChangeUI(self, state_text):
        """
        function:  接收子线程信号后,更新UI函数
              in:  state_text:线程类中的self.state
             out:  None
          return:  None
          others:  Update the UI function after receiving the child thread signal
        """
        QMessageBox.information(self.ui, '提示', f'{state_text}', QMessageBox.Yes)
        if state_text == "数据库连接成功!":
            self.ui.pteReport.appendPlainText(
                f"{datetime.datetime.now().strftime('%Y-%m-%d-%H:%M:%S')}{state_text}")  # 报表多行文本框追加信息
        else:
            self.ui.pteReport.appendPlainText(
                f"{datetime.datetime.now().strftime('%Y-%m-%d-%H:%M:%S')}{state_text}")  # 报表多行文本框追加信息

f. 绑定 从线程类发出来的信号 到 界面更新的槽函数

		self.uploadingThread.connected.connect(self.upLoadingChangeUI)  # 上传线程 开启后 信号发射 连接槽函数

3、实现的效果

【Python_PySide2学习笔记(九)】基于QThread实现 子线程更新 主线程/UI线程_第2张图片

你可能感兴趣的:(Python学习笔记,#,PySide2学习笔记,python,pyqt,ui)