python--pyQt5 页面刷新\线程刷新\界面卡顿 - 信号与槽-- 多线程处理(线程的开始/暂停/恢复/取消)同时运行两个不同的线程 pyside6

信号与槽

PySide6/PyQT多线程之 信号与槽 / (Signal Slot)的高效利用

1.定义信号Signal (对象间通信的机制,允许对象发送和接收信号)-- 事件的发射者
2.定义槽Slot (用于响应Signal信号的方法)-- 对事件的响应
3. 将信号和槽连接:Signal().connect. Slot()

在 PySide6/PyQT 中,可以使用 connect() 方法将信号和槽相连接

信号和槽必须是相同的参数类型。在定义信号和槽时,必须确保它们的参数类型和个数是相同的,否则无法正确连接和触发信号和槽。

class BackendThread(QObject):
    # 通过类成员对象定义信号
    update_date = Signal(str) #接受一个整形参数(可以为空,也可以更换为其它类型
 ......
 
    def initUI(self):  # 读告警
        self.thread = QThread()  # 创建线程
        self.backend = BackendThread()
        self.backend.update_date.connect(self.initUI_result)  # 连接信号 update_date
        self.backend.moveToThread(self.thread)

        # 开始线程
        self.thread.started.connect(self.backend.run)
        self.thread.start()

    @Slot(int) # 接收一个字符串参数,并打印接收到的数据
    def initUI_result(self, handleDisplay_data):
        print(f"已经开始处理第{int(handleDisplay_data) + 1}个...")

这里的 Slot(int) 可以不写,但是写了可以提高代码可读性和执行效率。
首先,标明这是一个槽函数;
其次,在运行时动态地连接信号,在一定程度上提高代码的执行效率,因为在编译期间不需要生成额外的代码来连接信号和槽。

举例代码

# -*- coding: utf-8 -*-
import time
from PySide6.QtCore import (QThread, Signal, Slot, QSize)
from PySide6.QtWidgets import (QApplication, QPushButton, QLabel, QVBoxLayout, QWidget)


class MyThread(QThread):
    my_signal = Signal(int)
    finished_signal = Signal()

    def __init__(self, count):
        super().__init__()
        self.count: int = count

    def run(self):
        for idx in range(1, self.count + 1):
            time.sleep(1)
            # 任务进行时发出信号
            self.my_signal.emit(idx)
	    # 任务完成后发出信号
        self.finished_signal.emit()


class MainWindow(QWidget):

    def __init__(self, parent=None):
        super().__init__(parent=parent)
        self.setup_ui()
        #
        self.button.clicked.connect(self.setup_thread)

    def setup_ui(self):
        self.setWindowTitle('demo')
        self.resize(QSize(250, 180))
        # 创建一个垂直布局
        layout = QVBoxLayout()
        # 创建一个标签
        self.label = QLabel('This is a label => ')
        layout.addWidget(self.label)
        # 创建一个按钮
        self.button = QPushButton('Send Request')
        layout.addWidget(self.button)
        # 将布局设置为主窗口的布局
        self.setLayout(layout)
        # 显示窗口
        self.show()

    def setup_thread(self):
        self.thread_ = MyThread(count=5)
        self.thread_.my_signal.connect(self.thread_progress)
        self.thread_.finished_signal.connect(self.thread_finished)
        self.thread_.start()

    @Slot(int)
    def thread_progress(self, item):
        self.label.setText('This is a label => ' + str(item))

    @Slot()
    def thread_finished(self):
        self.label.setText('This is a label finished.')


if __name__ == '__main__':
    app = QApplication([])
    window = MainWindow()
    window.show()
    app.exec()

参考:https://blog.csdn.net/zx520113/article/details/86598658
PyQt5中使用QTimer定时刷新:当要执行可能会超过设定时间的代码

一、页面刷新

刷新界面命令:QApplication.processEvents()

import sys
from PyQt5.QtWidgets import *
import time

class WinForm(QWidget):
    def __init__(self, parent = None):
        super(WinForm, self).__init__(parent)
        self.setWindowTitle('实时刷新页面例子')
        self.listFile = QListWidget()
        self.btnStart = QPushButton('开始')
        layout = QGridLayout(self)
        layout.addWidget(self.listFile, 0, 0, 1, 2)
        layout.addWidget(self.btnStart, 1, 1)

        self.btnStart.clicked.connect(self.slotAdd)
        self.setLayout(layout)

    def slotAdd(self):
        for n in range(10):
            str_n = 'file index {0}'.format(n)
            self.listFile.addItem(str_n)
            QApplication.processEvents()
            time.sleep(1)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    form = WinForm()
    form.show()
    sys.exit(app.exec_())

对于执行很耗时的程序来说,由于PyQt需要等待程序执行完毕才能进行下一步,这个过程表现在界面上就是卡顿,而如果需要执行这个耗时程序时不断的刷新界面。那么就可以使用QApplication.processEvents(),那么就可以一边执行耗时程序,一边刷新界面的功能,给人的感觉就是程序运行很流畅,因此QApplicationEvents()的使用方法就是,在主函数执行耗时操作的地方,加入QApplication.processEvents()

QTimer

#定义QTimer 类
self. Timer = QTimer(self)
self.timer.start(1000) #单位为毫秒
self. Stop()

需要定时对数据进行刷新,以获取到最新的数据,然后对数据进行处理, 这时候可以采用PyQT5 中的QTimer 类

from PyQt5.QtWidgets import QWidget, QPushButton, QApplication, QGridLayout, QLabel
from PyQt5.QtCore import QTimer
import sys
 
 
class WinForm(QWidget):
 
    def __init__(self, parent=None):
        super(WinForm, self).__init__(parent)
 
        self.setWindowTitle("QTimer demo")
        self.label = QLabel('测试')
        self.startBtn = QPushButton('开始')
        self.endBtn = QPushButton('结束')
 
        layout = QGridLayout(self)
 
        # 初始化一个定时器
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.myFunction) #到达设定的时间后,执行槽函数代码
 
        layout.addWidget(self.label, 0, 0, 1, 2)
        layout.addWidget(self.startBtn, 1, 0)
        layout.addWidget(self.endBtn, 1, 1)
 
        # 连接按键操作和槽函数
        self.startBtn.clicked.connect(self.startTimer)
        self.endBtn.clicked.connect(self.endTimer)
 
        self.setLayout(layout)
 
    def startTimer(self):
        self.timer.start(5000) # 5000 单位是毫秒, 即 5 秒   --开始
        self.label.setText('开始执行了-----')
    
    def endTimer(self):   
        self.timer.stop()   --停止
    
    def myFunction(self):
#         for i in range(10):
#             self.label.setText(str(i) + ',')
        #如果执行该代码的时间远远超过 5 秒的话: 使用下面的方法
        self.timer.stop()
        for i in range(100000000): #此代码远远超过 5 秒
            if i % 100 == 0:
                print(i)
        self.label.setText('这是很长的代码')
        self.timer.start() #此时, start 中不要加任何的时间
        
if __name__ == "__main__":
    app = QApplication(sys.argv)
    form = WinForm()
    form.show()
    sys.exit(app.exec_())

二、多线程处理(线程刷新)-- QThread

pyqt界面刷新(表格数据刷新)、界面传值、卡顿
PyQt5 实时刷新数据
解决PySide6/PyQT的界面卡死问题(PySide6/PyQT多线程

在PySide6中,可以使用这些线程类去(QThread、QObject、QRunnable 和 QtConcurrent)创建线程多线程,并采用 信号和槽机制 来将线程中的结果回传到主线程。
本文用 QThread,它继承了 QObject

界面里函数运行多次的话,移动窗口有种卡顿的感觉,还是使用 线程刷新 比较合适

from PyQt5.QtCore import QThread, pyqtSignal, QDateTime, QObject
from PyQt5.QtWidgets import QApplication, QDialog, QLineEdit, QLabel
import time
import sys


class BackendThread(QObject):
    # 通过类成员对象定义信号
    update_date = pyqtSignal(str)

    # 处理业务逻辑
    def run(self):
        while 1:
            # 刷新1-10
            for i in range(1, 11):
            	print(f"开始处理第{i + 1}个...")
                self.update_date.emit(str(i))
                time.sleep(1)


class Window(QDialog):
    def __init__(self):
        QDialog.__init__(self)
        self.setWindowTitle('PyQt 5界面实时更新例子')
        self.resize(400, 100)
        self.input = QLabel(self)
        self.input.resize(400, 100)
        self.initUI()

    def initUI(self):
        # 创建线程
        self.thread = QThread()

        self.backend = BackendThread()
        # 连接信号
        self.backend.update_date.connect(self.handleDisplay)
        self.backend.moveToThread(self.thread)

        # 开始线程
        self.thread.started.connect(self.backend.run)
        self.thread.start()

    # 将当前时间输出到文本框
    def handleDisplay(self, data):
        self.input.setText(data)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = Window()
    win.show()
    sys.exit(app.exec_())

界面 和 运行 相分离,通过信号与槽来进行参数传递

重写的run方法,当线程的 start() 方法被调用时,就会自动执行该方法。

触发 initUI 方法,在该方法中会创建线程类 BackendThread 的实例并启动该线程。

三、多线程处理(线程的开始/暂停/恢复/取消)–QThread

from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
class Thread(QThread):
    #线程值信号
    valueChange = pyqtSignal(int)
    #构造函数
    def __init__(self):
        super(Thread, self).__init__()
        self.isPause = False
        self.isCancel=False
        self.cond = QWaitCondition()
        self.mutex = QMutex()    
    #暂停
    def pause(self):
        print("线程暂停")
        self.isPause = True
        
    #恢复
    def resume(self):
        print("线程恢复")
        self.isPause = False
        self.cond.wakeAll()
    #取消   
    def cancel(self):
        print("线程取消")
        self.isCancel=True
    #运行(入口)
    def run(self):
        for i in range(100):
            #线程锁on
            self.mutex.lock()
            if self.isPause:
                self.cond.wait(self.mutex)
            if self.isCancel:
                self.valueChange.emit(0)
                break
            #业务代码
            self.valueChange.emit(i)
            self.msleep(100)
            #线程锁off
            self.mutex.unlock()
            
class MyDialog(QDialog):
    def __init__(self):
        super().__init__()
        """控件的创建和布局"""
        self.layout=QVBoxLayout(self)
        self.progressBar=QProgressBar()
        self.btnBegin=QPushButton("开始")
        self.btnPause=QPushButton("暂停")
        self.btnPause.setEnabled(False)
        self.btnResume=QPushButton("恢复")
        self.btnResume.setEnabled(False)
        self.btnCancel=QPushButton("取消")
        self.btnCancel.setEnabled(False)
        self.layout.addWidget(self.progressBar)
        self.layout.addWidget(self.btnBegin)
        self.layout.addWidget(self.btnPause)
        self.layout.addWidget(self.btnResume)
        self.layout.addWidget(self.btnCancel)
        """信号绑定"""
        self.btnBegin.clicked.connect(self.__onClickedBtnbegin)
        self.btnPause.clicked.connect(self.__onClickedBtnpause)        
        self.btnResume.clicked.connect(self.__onClickedBtnresume)
        self.btnCancel.clicked.connect(self.__onClickedBtncancel)
        
    #开始按钮被点击的槽函数    
    def __onClickedBtnbegin(self):
        self.btnBegin.setEnabled(False)
        self.btnPause.setEnabled(True)
        self.btnResume.setEnabled(False)
        self.btnCancel.setEnabled(True)
        self.thread=Thread()#创建线程
        self.thread.valueChange.connect(self.progressBar.setValue)#线程信号和槽连接
        self.thread.start()
    #暂停按钮被点击的槽函数    
    def __onClickedBtnpause(self):
        self.btnBegin.setEnabled(False)
        self.btnPause.setEnabled(False)
        self.btnResume.setEnabled(True)
        self.btnCancel.setEnabled(True)
        self.thread.pause()
    #恢复按钮被点击的槽函数    
    def __onClickedBtnresume(self):
        self.btnBegin.setEnabled(False)
        self.btnPause.setEnabled(True)
        self.btnResume.setEnabled(False)
        self.btnCancel.setEnabled(True)
        self.thread.resume()
    #取消按钮被点击的槽函数    
    def __onClickedBtncancel(self):
        self.btnBegin.setEnabled(True)
        self.btnPause.setEnabled(False)
        self.btnResume.setEnabled(False)
        self.btnCancel.setEnabled(False)
        self.thread.cancel()
if __name__=="__main__":   
    
    #qt程序
    app=QApplication(sys.argv)  
    dialog=MyDialog()
    dialog.show()
    sys.exit(app.exec_())

在这里插入图片描述

四、线程锁

lock = threading.RLock() # 创建锁
lock. acquire()	# 锁定
lock. release()	# 释放

比如线程t1使用lock.acquire()获得了这个锁,那么线程t2就无法再获得该锁了,只会阻塞在 lock.acquire()处,直到锁被线程t1释放,即执行lock.release()。如此一来就不会出现执行了一半就暂停去执行别的线程的情况,起到保护作用,直至释放该锁,其他的线程或进程才可以成功获取该锁,然后继续执行下面的代码块。

"""
进程:正在运行的程序

线程:cpu调度的单位,一个进程可以包含多个线程

多线程: 合适IO密集型
多进程: cpu密集型任务
"""
import threading
import time

lock = threading.RLock()


def my_print(name):
    for i in range(10):
        time.sleep(1)
        lock.acquire()
        print(name, i)
        lock.release()


start = time.time()  # 返回当前的时间戳
t1 = threading.Thread(target=my_print, args=["threading1:"])  # 传个任务,和参数进来
t2 = threading.Thread(target=my_print, args=['threading2:'])
t1.start()  # 线程开始
t2.start()
t1.join()
t2.join()
end = time.time()
print(end - start)
print("结束")

五、同时运行两个不同的线程

https://geek-docs.com/pyqt/pyqt-questions/202_pyqt_how_run_two_different_threads_simultaneously_in_pyqt.html

from PyQt5.QtCore import QThread

# 创建第一个线程类
class Thread1(QThread):
    def run(self):
        for i in range(10):
            print("Thread 1: ", i)

# 创建第二个线程类
class Thread2(QThread):
    def run(self):
        for i in range(10):
            print("Thread 2: ", i)

# 创建线程对象并启动线程
thread1 = Thread1()
thread2 = Thread2()
thread1.start()
thread2.start()

创建了两个线程类Thread1和Thread2,分别重写了run方法来定义线程的具体功能。然后创建了两个线程对象,并通过调用start()方法来启动线程。通过这样的方式,就可以同时运行两个不同的线程。

线程之间的通信

from PyQt5.QtCore import QThread, pyqtSignal

# 创建第一个线程类
class Thread1(QThread):
    # 定义自定义信号
    my_signal = pyqtSignal(str)

    def run(self):
        for i in range(10):
            self.my_signal.emit("Thread 1: " + str(i))

# 创建第二个线程类
class Thread2(QThread):
    def __init__(self):
        super().__init__()
        self.my_signal.connect(self.handle_signal)

    def handle_signal(self, msg):
        print(msg)

    def run(self):
        for i in range(10):
            print("Thread 2: ", i)

# 创建线程对象并启动线程
thread1 = Thread1()
thread2 = Thread2()
thread1.start()
thread2.start()

在Thread1类中定义了一个自定义信号my_signal,并在run方法中使用emit方法发出信号。在Thread2类中, 用connect方法将自定义信号和槽函数handle_signal绑定在一起,实现信号的接收和处理。通过这样的方式, 可以在两个线程之间实现通信。

  • 10
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]:在PyQt5中,如果执行耗时的程序会导致页面卡顿。在给定的代码中,通过点击按钮来执行一个耗时的循环操作,每次循环都会使页面卡顿一秒钟。这是因为PyQt5需要等待程序执行完毕才能进行下一步操作。\[1\] 引用\[2\]:为了解决这个问题,可以使用QThread类创建线程。在重写的run()方法中,可以以每隔1秒的频率叠加输出数字,并且在数字为10时退出线程。通过使用线程,可以使页面保持响应,不会卡顿。\[2\] 引用\[3\]:另外,如果数据的读写非常耗费时间,也会导致界面卡死。在给定的代码中,通过点击按钮来执行一个耗时的循环操作,每次循环都会使界面卡死一秒钟。这是因为循环操作阻塞了主线程,导致界面无法响应。\[3\] 综上所述,为了避免界面卡顿,可以使用多线程来执行耗时的操作,保持界面的响应性。 #### 引用[.reference_title] - *1* *3* [PyQt5——多线程](https://blog.csdn.net/huayunhualuo/article/details/101446602)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [九、PyQt5多线程编程](https://blog.csdn.net/ungoing/article/details/127271810)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值