PyQt5高级界面控件之QThread(十二)

女爷i 2023-05-31 07:43 76阅读 0赞

前言

QThread是Qt的线程类中最核心的底层类。由于PyQt的的跨平台特性,QThread要隐藏所有与平台相关的代码
要使用的QThread开始一个线程,可以创建它的一个子类,然后覆盖其它QThread.run()函数

  1. class ThreadQThread): def __init __self): superThreadself).__ init __() def runself): #线程相关的代码 pass

接下来创建一个新的线程

  1. thread = Thread()
  2. thread.start()

可以看出,PyQt的线程使用非常简单—-建立一个自定义的类(如thread),自我继承自QThread ,并实现其run()方法即可
在使用线程时可以直接得到Thread实例,调用其start()函数即可启动线程,线程启动之后,会自动调用其实现的run()的函数,该方法就是线程的执行函数
业务的线程任务就写在run()函数中,当run()退出之后线程就基本结束了,QThread有started和finished信号,可以为这两个信号指定槽函数,在线程启动和结束之时执行一段代码进行资源的初始化和释放操作,更灵活的使用方法是,在自定义的QThread实例中自定义信号,并将信号连接到指定的槽函数,当满足一定的业务条件时发射此信号

QThread类中的常用方法






























方法 描述
start() 启动线程
wait() 阻止线程,直到满足如下条件之一
与此QThread对象关联的线程已完成执行(即从run返回时),如果线程完成执行,此函数返回True,如果线程尚未启动,也返回True
等待时间的单位是毫秒,如果时间是ULONG_MAX(默认值·),则等待,永远不会超时(线程必须从run返回),如果等待超时,此函数将会返回False
sleep() 强制当前线程睡眠多少秒

QThread类中的常用信号


















信号 描述
started 在开始执行run函数之前,从相关线程发射此信号
finished 当程序完成业务逻辑时,从相关线程发射此信号

实例:QThread的使用

  1. import sys
  2. from PyQt5.QtWidgets import *
  3. from PyQt5.QtCore import *
  4. from PyQt5.QtGui import *
  5. class MainWidget(QWidget):
  6. def __init__(self, parent=None):
  7. super(MainWidget, self).__init__(parent)
  8. #设置标题
  9. self.setWindowTitle('QThread多线程例子')
  10. #实例化多线程对象
  11. self.thread = Worker()
  12. #实例化列表控件与按钮控件
  13. self.listFile = QListWidget()
  14. self.btnStart = QPushButton('开始')
  15. #把控件放置在栅格布局中
  16. layout = QGridLayout(self)
  17. layout.addWidget(self.listFile, 0, 0, 1, 2)
  18. layout.addWidget(self.btnStart, 1, 1)
  19. #信号与槽函数的连接
  20. self.btnStart.clicked.connect(self.slotStart)
  21. self.thread.sinOut.connect(self.slotAdd)
  22. def slotAdd(self, file_inf):
  23. #向列表控件中添加条目
  24. self.listFile.addItem(file_inf)
  25. def slotStart(self):
  26. #开始按钮不可点击,线程开始
  27. self.btnStart.setEnabled(False)
  28. self.thread.start()
  29. class Worker(QThread):
  30. sinOut = pyqtSignal(str)
  31. def __init__(self, parent=None):
  32. super(Worker, self).__init__(parent)
  33. #设置工作状态与初始num数值
  34. self.working = True
  35. self.num = 0
  36. def __del__(self):
  37. #线程状态改变与线程终止
  38. self.working = False
  39. self.wait()
  40. def run(self):
  41. while self.working == True:
  42. #获取文本
  43. file_str = 'File index{0}'.format(self.num)
  44. self.num += 1
  45. # 发射信号
  46. self.sinOut.emit(file_str)
  47. # 线程休眠2秒
  48. self.sleep(2)
  49. if __name__ == '__main__':
  50. app = QApplication(sys.argv)
  51. demo = MainWidget()
  52. demo.show()
  53. sys.exit(app.exec_())

运行效果图如下
这里写图片描述

代码分析

在这个例子中,单击开始按钮,会在后台定时读取数据,并把返回的数据显示在界面中,首先使用以下代码进行布局,把列表控件和按钮控件放在栅格布局管理器中

  1. #实例化列表控件与按钮控件
  2. self.listFile = QListWidget()
  3. self.btnStart = QPushButton('开始')
  4. #把控件放置在栅格布局中
  5. layout = QGridLayout(self)
  6. layout.addWidget(self.listFile, 0, 0, 1, 2)
  7. layout.addWidget(self.btnStart,

然后将按钮的clicked信号连接到槽函数,单击开始触发槽函数

  1. self.btnStart.clicked.connect(self.slotStart)
  2. def slotStart(self):
  3. #开始按钮不可点击,线程开始
  4. self.btnStart.setEnabledFalse
  5. self.thread.start()

比较复杂的是线程的信号,将线程的sinOut信号连接到slotAdd()槽函数,SlotAdd()函数负责在列表控件中动态添加字符串条目

  1. self.thread.sinOut.connectself.slotAdd
  2. def slotAddselffile_inf): #向列表控件中添加条目 self.listFile.addItem(file_inf)

定义一个线程类,继承自QThread,当线程启动时,执行run()函数

  1. class Worker(QThread):
  2. sinOut = pyqtSignal(str)
  3. def __init__(self, parent=None):
  4. super(Worker, self).__init__(parent)
  5. #设置工作状态与初始num数值
  6. self.working = True
  7. self.num = 0
  8. def __del__(self):
  9. #线程状态改变与线程终止
  10. self.working = False
  11. self.wait()
  12. def run(self):
  13. while self.working == True:
  14. #获取文本
  15. file_str = 'File index{0}'.format(self.num)
  16. self.num += 1
  17. # 发射信号
  18. self.sinOut.emit(file_str)
  19. # 线程休眠2秒
  20. self.sleep(2)

实例二:多线程失败案例

  1. import sys
  2. from PyQt5.QtWidgets import *
  3. from PyQt5.QtCore import *
  4. from PyQt5.QtGui import *
  5. global sec
  6. sec=0
  7. def setTime():
  8. global sec
  9. sec+=1
  10. #Led显示数字+1
  11. lcdNumber.display(sec)
  12. def work():
  13. #计时器每秒计数
  14. timer.start(1000)
  15. for i in range(200000000):
  16. pass
  17. timer.stop()
  18. if __name__ == '__main__':
  19. app=QApplication(sys.argv)
  20. top=QWidget()
  21. top.resize(300,120)
  22. #垂直布局
  23. layout=QVBoxLayout(top)
  24. #添加一个显示面板
  25. lcdNumber=QLCDNumber()
  26. layout.addWidget(lcdNumber)
  27. button=QPushButton('测试')
  28. layout.addWidget(button)
  29. timer=QTimer()
  30. #每次计时结束,触发setTime
  31. timer.timeout.connect(setTime)
  32. button.clicked.connect(work)
  33. top.show()
  34. sys.exit(app.exec_())

失败效果图如下
这里写图片描述

长时间停留在此界面,知道多线程任务完成后,此界面才会动,当耗时程序非常大时,就会造成程序运行失败的假象,实际还是在后台运行的,只是没有显示在主窗口的界面上,当然用户体验也就非常差,那么如何解决这个问题呢,下面实例三进行解答

实例三:分离UI主线程与工作线程

  1. import sys
  2. from PyQt5.QtCore import *
  3. from PyQt5.QtGui import *
  4. from PyQt5.QtWidgets import *
  5. global sec
  6. sec = 0
  7. class WorkThread(QThread):
  8. #实例化一个信号对象
  9. trigger = pyqtSignal()
  10. def __int__(self):
  11. super(WorkThread, self).__init__()
  12. def run(self):
  13. #开始进行循环
  14. for i in range(2000000000):
  15. pass
  16. # 循环完毕后发出信号
  17. self.trigger.emit()
  18. def countTime():
  19. global sec
  20. sec += 1
  21. # LED显示数字+1
  22. lcdNumber.display(sec)
  23. def work():
  24. # 计时器每秒计数
  25. timer.start(1000)
  26. # 计时开始
  27. workThread.start()
  28. # 当获得循环完毕的信号时,停止计数
  29. workThread.trigger.connect(timeStop)
  30. def timeStop():
  31. #定时器停止
  32. timer.stop()
  33. print("运行结束用时", lcdNumber.value())
  34. global sec
  35. sec = 0
  36. if __name__ == "__main__":
  37. app = QApplication(sys.argv)
  38. top = QWidget()
  39. top.resize(300, 120)
  40. # 垂直布局类QVBoxLayout
  41. layout = QVBoxLayout(top)
  42. # 加显示屏,按钮到布局中
  43. lcdNumber = QLCDNumber()
  44. layout.addWidget(lcdNumber)
  45. button = QPushButton("测试")
  46. layout.addWidget(button)
  47. #实例化定时器与多线程类
  48. timer = QTimer()
  49. workThread = WorkThread()
  50. button.clicked.connect(work)
  51. # 每次计时结束,触发 countTime
  52. timer.timeout.connect(countTime)
  53. top.show()
  54. sys.exit(app.exec_())

运行效果,程序主界面的数值会每秒增加1,直到循环结束,这里就避免了主界面长时间不动的尴尬!
这里写图片描述

实例四:事件处理

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

  1. import sys,time
  2. from PyQt5.QtWidgets import QWidget,QPushButton,QApplication,QListWidget,QGridLayout
  3. class WinForm(QWidget):
  4. def __init__(self,parent=None):
  5. super(WinForm, self).__init__(parent)
  6. #设置标题与布局方式
  7. self.setWindowTitle('实时刷新界面的例子')
  8. layout=QGridLayout()
  9. #实例化列表控件与按钮控件
  10. self.listFile=QListWidget()
  11. self.btnStart=QPushButton('开始')
  12. #添加到布局中指定位置
  13. layout.addWidget(self.listFile,0,0,1,2)
  14. layout.addWidget(self.btnStart,1,1)
  15. #按钮的点击信号触发自定义的函数
  16. self.btnStart.clicked.connect(self.slotAdd)
  17. self.setLayout(layout)
  18. def slotAdd(self):
  19. for n in range(10):
  20. #获取条目文本
  21. str_n='File index{0}'.format(n)
  22. #添加文本到列表控件中
  23. self.listFile.addItem(str_n)
  24. #实时刷新界面
  25. QApplication.processEvents()
  26. #睡眠一秒
  27. time.sleep(1)
  28. if __name__ == '__main__':
  29. app=QApplication(sys.argv)
  30. win=WinForm()
  31. win.show()
  32. sys.exit(app.exec_())

这里写图片描述

  • QThread

    • 前言
    • QThread类中的常用方法
    • QThread类中的常用信号
    • 实例:QThread的使用
    • 代码分析
    • 实例二:多线程失败案例
    • 实例三:分离UI主线程与工作线程
    • 实例四:事件处理

        • 相关文件及下载地址

相关文件及下载地址

https://download.csdn.net/download/jia666666/10609488

发表评论

表情:
评论列表 (有 0 条评论,76人围观)

还没有评论,来说两句吧...

相关阅读