PySide6在非UI线程更新UI界面实例

第一种:直接使用QThread类更新UI界面

        写一个SonThread类继承于QThread,直接在run函数中写我们要执行的动作(更新UI界面)。优点比较简单易懂,缺点只能被一种事件使用,不能重复利用。

# -*- coding: utf-8 -*-

import sys
import time
from PySide6.QtCore import *
from PySide6.QtWidgets import QApplication, QWidget, QTextBrowser, QPushButton, QHBoxLayout

# 创建QApplication对象
if not QApplication.instance():
    app = QApplication([])
else:
    app = QApplication.instance()


class SonThread(QThread):
    """子线程"""

    update_date = Signal(str)  # 定义信号

    def __init__(self, data):
        """子线程初始化"""
        super().__init__()  # 初始化父级
        self.result = data  # 把传入的参数data赋值给类变量result

    def run(self):
        """子线程执行时执行此函数"""
        for i in range(5):
            self.update_date.emit(str(f"倒计时:{5-i}"))  # 发送信号,信号内容为(倒计时:5-i)
            time.sleep(1)  # 延时1秒(不管延时多久,UI界面都不会出现假死状态)
        self.update_date.emit(str(self.result))  # 发送信号,信号内容为类变量result


class MyWindow(QWidget):
    """UI线程"""

    def __init__(self):
        """UI线程初始化"""
        super().__init__()  # 初始化父级
        self.son_thread = None  # 创建子线程变量
        self.setWindowTitle('子线程更新UI线程实例')  # 设置窗口标题
        self.text = QTextBrowser()  # 定义文本浏览框
        self.button = QPushButton("发送信号")  # 定义按钮
        self.main_layout = QHBoxLayout(self)  # 定义水平布局容器
        self.main_layout.addWidget(self.text)  # 布局文本浏览框
        self.main_layout.addWidget(self.button)  # 布局按钮
        self.button.clicked.connect(self.send_sign)  # 按钮点击事件绑定(send_sign)函数

    def send_sign(self):
        """点击按钮时执行此函数"""
        self.son_thread = SonThread("hello everyone")  # 定义子线程并传入参数(hello everyone)
        self.son_thread.update_date.connect(self.add_text)  # 给信号绑定(add_text)函数
        self.son_thread.start()  # 执行子线程

    def add_text(self, data):
        """
        收到信号时执行此函数
        :param data: 信号内容
        :return:
        """
        self.text.append(data)  # 在文本浏览框中追加内容(data)


def main():
    widget = MyWindow()  # 实例化UI线程
    widget.resize(300, 200)  # 设置窗口大小
    widget.show()  # 展示窗口
    sys.exit(app.exec())  # 持续刷新窗口


if __name__ == '__main__':
    main()

第二种:在其他线程中使用QThread类更新UI界面

        使用threading来创建新线程,在新线程中使用SonThread来更新UI界面。只用写一个更新UI界面的类,可以被很多的事件使用。

# -*- coding: utf-8 -*-

import sys
import threading
import time
from PySide6.QtCore import *
from PySide6.QtWidgets import QApplication, QWidget, QTextBrowser, QPushButton, QHBoxLayout, QVBoxLayout

# 创建QApplication对象
if not QApplication.instance():
    app = QApplication([])
else:
    app = QApplication.instance()


def thread_ui(func, *args):
    """
    开启一个新线程任务
    :param func: 要执行的线程函数;
    :param args: 函数中需要传入的参数 Any
    :return:
    """
    t = threading.Thread(target=func, args=args)  # 定义新线程
    t.setDaemon(True)  # 开启线程守护
    t.start()  # 执行线程


class SonThread(QThread):
    """子线程"""

    update_date = Signal(str)  # 定义信号

    def __init__(self, data):
        """子线程初始化"""
        super().__init__()  # 初始化父级
        self.result = data  # 把传入的参数data赋值给类变量result

    def run(self):
        """子线程执行时执行此函数"""
        self.update_date.emit(str(self.result))  # 发送信号,信号内容为类变量result


class MyWindow(QWidget):
    """UI线程"""

    def __init__(self):
        """UI线程初始化"""
        super().__init__()  # 初始化父级
        self.son_thread = None  # 创建子线程变量
        self.setWindowTitle('子线程更新UI线程实例')  # 设置窗口标题
        self.text = QTextBrowser()  # 定义文本浏览框
        self.button = QPushButton("发送信号")  # 定义发送信号按钮
        self.clear_button = QPushButton("清除文本")  # 定义清除文本按钮
        self.main_layout = QHBoxLayout(self)  # 定义水平布局容器
        self.son_layout = QVBoxLayout()  # 定义垂直布局容器
        self.main_layout.addWidget(self.text)  # 布局文本浏览框
        self.main_layout.addLayout(self.son_layout)  # 布局垂直布局容器
        self.son_layout.addWidget(self.button)  # 布局发送信号按钮
        self.son_layout.addWidget(self.clear_button)  # 布局清除文本按钮
        self.button.clicked.connect(self.button_click)  # 发送信号按钮点击事件绑定(button_click)函数
        self.clear_button.clicked.connect(self.clear_click)  # 清除文本按钮点击事件绑定(clear_click)函数

    def button_click(self):
        """点击发送信号按钮时执行此函数"""
        def button_thread():
            for i in range(5):
                self.send_sign(f"倒计时:{5-i}")  # 发送信号(倒计时:{5-i})
                time.sleep(1)  # 延时1秒(不管延时多久,UI界面都不会出现假死状态)
            self.send_sign("你好啊")  # 发送信号(你好啊)
        return thread_ui(button_thread)  # 开启新线程来执行(button_thread)函数

    def clear_click(self):
        """点击清除文本按钮时执行此函数"""
        def clear_text():
            for i in range(5):
                self.send_sign(f"倒计时:{5 - i}")  # 发送信号(倒计时:{5-i})
                time.sleep(1)  # 延时1秒(不管延时多久,UI界面都不会出现假死状态)
            self.send_sign("clear")  # 发送信号(clear)
        return thread_ui(clear_text)  # 开启新线程来执行(clear_text)函数

    def send_sign(self, data):
        """
        使用SonThread发送信号
        :param data: 发送的内容
        :return:
        """
        self.son_thread = SonThread(data)  # 定义子线程并传入参数(data)
        self.son_thread.update_date.connect(self.add_text)  # 给信号绑定(add_text)函数
        self.son_thread.start()  # 执行子线程

    def add_text(self, data):
        """
        收到信号时执行此函数
        :param data: 接收的内容
        :return:
        """
        if data == 'clear':  # 判断接收的内容是否为clear
            self.text.clear()  # 是clear就清空文本框内容
        else:  # 不是clear
            self.text.append(data)  # 在文本浏览框中追加内容(data)


def main():
    widget = MyWindow()  # 实例化UI线程
    widget.resize(300, 200)  # 设置窗口大小
    widget.show()  # 展示窗口
    sys.exit(app.exec())  # 持续刷新窗口


if __name__ == '__main__':
    main()

 第三种:创建信号线程(最优)

        鉴于前面两种方式,都有一些问题,后来我想到了第三种方式。这种方式可以使用一个信号类实例化出多个信号对象,每个信号都绑定一个的方法,每个信号都是独立存在的。我们同时可以发送多种信号去影响一个UI元素,而且每个信号之间都不会发生干扰。

# -*- coding: utf-8 -*-

import sys
import threading
import time
from PySide6.QtCore import *
from PySide6.QtWidgets import QApplication, QWidget, QTextBrowser, QPushButton, QHBoxLayout, QVBoxLayout, QMessageBox

# 创建QApplication对象
if not QApplication.instance():
    app = QApplication([])
else:
    app = QApplication.instance()


def thread_ui(func, *args):
    """
    开启一个新线程任务\n
    :param func: 要执行的线程函数;
    :param args: 函数中需要传入的参数 Any
    :return:
    """
    t = threading.Thread(target=func, args=args)  # 定义新线程
    t.setDaemon(True)  # 开启线程守护
    t.start()  # 执行线程


class SignThread(QThread):
    """信号线程"""

    def __new__(cls, parent: QWidget, func, *types: type):
        cls.update_date = Signal(*types)  # 定义信号(*types)一个信号中可以有一个或多个类型的数据(int,str,list,...)
        return super().__new__(cls)  # 使用父类__new__方法创建SignThread实例对象

    def __init__(self, parent: QWidget, func, *types: type):
        """
        信号线程初始化\n
        :param parent: 界面UI控件
        :param func: 信号要绑定的方法
        :param types: 信号类型,可以是一个或多个(type,...)
        """
        super().__init__(parent)  # 初始化父类
        self.sign = None
        self.update_date.connect(func)  # 绑定信号与方法

    def send_sign(self, *args):
        """
        使用SonThread发送信号\n
        :param args: 信号的内容
        :return:
        """
        self.sign = args  # 信号元组(type,...)
        self.start()

    def run(self):
        """信号线程执行时执行此函数"""
        self.update_date.emit(*self.sign)  # 发送信号元组(type,...)


class MyWindow(QWidget):
    """UI界面"""

    def __init__(self):
        """UI界面初始化"""
        super().__init__()  # 初始化父级
        self.setWindowTitle('子线程更新UI线程实例')  # 设置窗口标题
        self.text = QTextBrowser()  # 定义文本浏览框
        self.button = QPushButton("添加文本")  # 定义发送信号按钮
        self.clear_button = QPushButton("清除文本")  # 定义清除文本按钮
        self.main_layout = QHBoxLayout(self)  # 定义水平布局容器
        self.son_layout = QVBoxLayout()  # 定义垂直布局容器
        self.main_layout.addWidget(self.text)  # 布局文本浏览框
        self.main_layout.addLayout(self.son_layout)  # 布局垂直布局容器
        self.son_layout.addWidget(self.button)  # 布局发送信号按钮
        self.son_layout.addWidget(self.clear_button)  # 布局清除文本按钮
        self.button.clicked.connect(self.button_click)  # 发送信号按钮点击事件绑定(button_click)函数
        self.clear_button.clicked.connect(self.clear_click)  # 清除文本按钮点击事件绑定(clear_click)函数
        self.add_thread = SignThread(self, self.add_text, str, int)  # 创建信号线程,并绑定add_text函数,信号使用str和int两种数据
        self.clear_thread = SignThread(self, self.clear_text, int)  # 创建信号线程,并绑定clear_text函数,信号只使用int数据

    def button_click(self):
        """点击发送信号按钮时执行此函数"""
        def button_thread():
            for i in range(5):
                self.add_thread.send_sign(f"添加倒计时:{5-i}", i)  # 发送信号(倒计时:{5-i}, i)
                time.sleep(1)  # 延时1秒(不管延时多久,UI界面都不会出现假死状态)
            self.add_thread.send_sign('你好啊', 99)  # 发送信号(你好啊, 99)
        return thread_ui(button_thread)  # 开启新线程来执行(button_thread)函数

    def clear_click(self):
        """点击清除文本按钮时执行此函数"""
        def clear_text():
            for i in range(5):
                self.clear_thread.send_sign(0)  # 发送信号(0)
                time.sleep(1)  # 延时1秒(不管延时多久,UI界面都不会出现假死状态)
            self.clear_thread.send_sign(1)  # 发送信号(1)
        return thread_ui(clear_text)  # 开启新线程来执行(clear_text)函数

    def add_text(self, *args):
        """
        收到add_thread信号时执行此函数\n
        :param args: 接收的信号元组
        :return:
        """
        self.text.append(f'str: {args[0]} int: {args[1]}')  # 在文本浏览框中追加内容(data)

    def clear_text(self, *args):
        """
        收到clear_thread信号时执行此函数\n
        :param args: 接收的信号元组
        :return: 
        """
        self.text.clear()  # 清除文本浏览框中的内容
        if args[0] == 1:  # 当信号元组的第一个元素为整型1时
            msg = QMessageBox(self)
            msg.setText(f"清除文本结束")
            msg.exec()


def main():
    widget = MyWindow()  # 实例化UI线程
    widget.resize(300, 200)  # 设置窗口大小
    widget.show()  # 展示窗口
    sys.exit(app.exec())  # 持续刷新窗口


if __name__ == '__main__':
    main()

在这个实例中,我们可以同时点击添加文本和清除文本按钮,让文本框一边添加文本一边删除文本,两个信号差不多同时进行,互不影响。

你可能感兴趣的:(PySide6使用总结,python,ui,开发语言)