写点什么

Python Qt GUI 设计:QTimer 计时器类、QThread 多线程类和事件处理类(基础篇—8)

  • 2021 年 11 月 16 日
  • 本文字数:3324 字

    阅读完需:约 11 分钟

Python Qt GUI设计:QTimer计时器类、QThread多线程类和事件处理类(基础篇—8)

一般情况下,应用程序都是单线程运行的,但是对于 GUI 程序来说,单线程有时候满足不了需求。例如,如果需要执行一个特别耗时的操作,在执行过程中整个程序就会卡顿,效果就非常不理想或者 Windows 系统也认为程序运行出错,自动关闭了程序。要解决这种问题就涉及多线程的知识。

一般来说,多线程技术涉及三种方法,其中第一种是使用计时器模块 QTimer;第二种是使用多线程模块 QThread;最后是使用事件处理的功能。

1、QTimer 计时器类

如果要在应用程序中周期性地进行某项操作,比如周期性地检测主机的 CPU 值,则需要用到 QTimer(定时器),QTimer 类提供了重复的和单次的定时器。要使用定时器,需要先创建一个 QTimer 实例,将其 timeout 信号连接到相应的槽,并调用 start()。然后,定时器会以恒定的间隔发出 timeout 信号。

当窗口控件收到 timeout 信号后,它就会停止这个定时器。这是在图形用户界面中实现复杂工作的一个典型方法,随着技术的进步,多线程在越来越多的平台上被使用,最终 QTimer 对象会被线程所替代。

QTimer 类中的常用方法如下表所示:

QTimer 类中的常用信号如下表所示:

通过一个示例,了解 QTimer 计时器类的使用方法,效果如下所示:


示例中,初始化一个定时器,把定时器的 timeout 信号与 showTime()槽函数连接起来。使用连接的槽函数显示当前时间,并在标签上显示系统现在的时间。单击“开始"按钮,启动定时器,并使"开始"按钮失效。单击“结束"按钮,停止定时器,并使“结束"按钮失效。

实现代码如下所示:

from PyQt5.QtWidgets import QWidget,  QPushButton ,  QApplication ,QListWidget,  QGridLayout , QLabelfrom PyQt5.QtCore import QTimer ,QDateTimeimport sys  class WinForm(QWidget):  		def __init__(self,parent=None): 		super(WinForm,self).__init__(parent) 		self.setWindowTitle("QTimer demo")		self.listFile= QListWidget() 		self.label = QLabel('显示当前时间')		self.startBtn = QPushButton('开始') 		self.endBtn = QPushButton('结束') 		layout = QGridLayout(self)          # 初始化一个定时器		self.timer = QTimer(self)        # showTime()方法		self.timer.timeout.connect(self.showTime)				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 showTime(self): 		# 获取系统现在的时间		time = QDateTime.currentDateTime() 		# 设置系统时间显示格式		timeDisplay = time.toString("yyyy-MM-dd hh:mm:ss dddd");		# 在标签上显示时间		self.label.setText( timeDisplay )  	def startTimer(self):         # 设置计时间隔并启动		self.timer.start(1000)		self.startBtn.setEnabled(False)		self.endBtn.setEnabled(True) 	def endTimer(self): 		self.timer.stop()		self.startBtn.setEnabled(True)		self.endBtn.setEnabled(False)		if __name__ == "__main__":  	app = QApplication(sys.argv)  	form = WinForm()  	form.show()  	sys.exit(app.exec_())
复制代码

2、QThread 多线程类

QThread 是 Qt 线程类中最核心的底层类,由于 PyQt 的跨平台特性,QThread 要隐藏所有与平台相关的代码。

在使用线程时可以直接得到 Thread 实例,调用其 start()函数即可启动线程。线程启动之后,会自动调用其实现的 run 方法,该方法就是线程的执行函数。

业务的线程任务就写在 run()函数中,当 run()退出之后线程基本就结束了。QThread 有 started 和 finished 信号,可以为这两个信号指定槽函数,在线程启动和结束时执行一段代码进行资源的初始化和释放操作。更灵活的使用方法是,在自定义的 QThread 实例中自定义信号,并将信号连接到指定的槽函数,当满足一定的业务条件后发射此信号。

QThread 类中的常用方法如下表所示:

QThread 类中的常用信号如下表所示:

通过一个示例,了解 QThread 多线程类的使用方法,效果如下所示:

示例中,在主界面中有一个用于显示时间的 LCD 数字面板,还有一个用于启动任务的按钮。用户单击"测试"按钮后,将开始一次非常耗时的计算(在程序中用一个 2000 000 000 次的循环来模拟这次非常耗时的工作,在真实的程序中可能是一个网络下载操作,从网络上下载一个很大的视频文件),同时 LCD 数字面板开始显示所用的毫秒数,并通过一个计时器进行更新。但是单击”测试“按钮后可见窗口卡死无法操作。此时在 PyQt 中所有的窗口都在 UI 主线程中(就是执行了 QApplication.exec()的线程),在这个线程中执行耗时的操作会阻塞 UI 线程,从而让窗口停止响应。如果窗口长时间没有响应,则会影响用户体验,更严重的会导致程序崩溃。所以,为了避免出现这样的问题,要使用 QThread 开启一个新的线程,在这个线程中完成耗时的操作。

实现代码如下所示:

import sysfrom PyQt5.QtCore import *from PyQt5.QtGui import *from PyQt5.QtWidgets import *    global secsec=0 class WorkThread(QThread):	trigger = pyqtSignal()	def __int__(self):		super(WorkThread,self).__init__() 	def run(self):		for i in range(2000000000):			pass				# 循环完毕后发出信号				self.trigger.emit()         def countTime():	global  sec	sec += 1	# LED显示数字+1	lcdNumber.display(sec)           def work():	# 计时器每秒计数	timer.start(1000)   	# 计时开始		workThread.start()       	# 当获得循环完毕的信号时,停止计数		workThread.trigger.connect(timeStop)   def timeStop():	timer.stop()	print("运行结束用时",lcdNumber.value())	global sec	sec=0 if __name__ == "__main__":  		app = QApplication(sys.argv) 	top = QWidget()	top.resize(300,120)    	# 垂直布局类QVBoxLayout	layout = QVBoxLayout(top)     # 加个显示屏    	lcdNumber = QLCDNumber()             	layout.addWidget(lcdNumber)	button = QPushButton("测试")	layout.addWidget(button) 	timer = QTimer()	workThread = WorkThread() 	button.clicked.connect(work)    # 每次计时结束,触发 countTime	timer.timeout.connect(countTime)       	top.show()	sys.exit(app.exec_())
复制代码

3、事件处理类

PyQt 为事件处理提供了两种机制:高级的信号与槽机制以及低级的事件处理程序。本篇文博只介绍低级的事件处理程序即:processEvents()函数的使用方法,它的作用是处理事件,简单地说,就是刷新页面。

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

因此 QApplication.processEvents()的使用方法就是,在主函数执行耗时操作的地方,加入 QApplication.processEvents()。

通过一个示例,了解事件处理类的使用方法,效果如下所示:

实现代码如下所示:

from PyQt5.QtWidgets import QWidget,  QPushButton ,  QApplication ,QListWidget,  QGridLayout import sys 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_())
复制代码


发布于: 2 小时前阅读数: 5
用户头像

【研究方向】物联网、嵌入式、AI、Python 2018.02.09 加入

【公众号】美男子玩编程,关注获取海量资源~

评论

发布
暂无评论
Python Qt GUI设计:QTimer计时器类、QThread多线程类和事件处理类(基础篇—8)