赞
踩
目录
1)界面设计——利用qt-designer设计,然后pyuic编译为py文件
二、方法二:QtWidgets.QApplication.processEvents()
按一下开关,灯亮了
在上面的描述中,“按一下开关”这个动作就是信号,而槽指的是完成这个动作的时候会发生的事情。可以理解为只有触发了信号,才能让相应的槽函数(事件)发生
在GUI中常见的信号与槽就是按钮控件和按钮绑定的回调函数。当单击\释放\双击按钮时,会根据信号运行不同的回调函数,也就是槽函数。常见的信号与槽形式如下:
此类信号与槽,信号主要是针对GUI上控件,槽函数可以是GUI自带的,也可以是自定义的槽函数
创建此类信号与槽的步骤:
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
# 自定义槽函数 def slotEvent(self): for i in range(1000000): print(i) # GUI自带槽函数——界面退出事件 def slotEvent(self): exit()
控件名.动作.connect(槽函数)
self.pushButton.clicked.connect(self.slotEvent) 信号:单击按钮 槽函数:slotEvent()函数
from PyQt5.Qt import pyqtSignal
信号名 = pyqtSignal(类型)
intSignal = pyqtSignal(int)
# 自定义槽函数
def slotEvent(self):
for i in range(1000000):
print(i)
# GUI自带槽函数——界面退出事件
def slotEvent(self):
exit()
信号名.emit(参数内容)
self.intSignal.emit(val)
注意:信号发射时的参数类型个数,必需保证和定义时的参数类型和个数一致。
信号名.connect(槽函数)
self.intSignal.connect(self.slotEvent)
开发环境:pycharm + window10 + pyqt5
功能:
1、按下开始计时按钮后,计数板开始计数,同时按钮文字修改为停止检测
2、按下停止计时按钮后,计数板停止计数,并且计时归零,同时按钮文字修改为开始计时
- # -*- coding: utf-8 -*-
-
- # Form implementation generated from reading ui file 'timerWithoutThread.ui'
- #
- # Created by: PyQt5 UI code generator 5.15.2
- #
- # WARNING: Any manual changes made to this file will be lost when pyuic5 is
- # run again. Do not edit this file unless you know what you are doing.
-
-
- from PyQt5 import QtCore, QtGui, QtWidgets
-
-
- class Ui_MainWindow(object):
- def setupUi(self, MainWindow):
- MainWindow.setObjectName("MainWindow")
- MainWindow.resize(307, 165)
- self.centralwidget = QtWidgets.QWidget(MainWindow)
- self.centralwidget.setObjectName("centralwidget")
- self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
- self.verticalLayout.setObjectName("verticalLayout")
- self.lcdNumber = QtWidgets.QLCDNumber(self.centralwidget)
- self.lcdNumber.setObjectName("lcdNumber")
- self.verticalLayout.addWidget(self.lcdNumber)
- self.pushButton = QtWidgets.QPushButton(self.centralwidget)
- self.pushButton.setObjectName("pushButton")
- self.verticalLayout.addWidget(self.pushButton)
- MainWindow.setCentralWidget(self.centralwidget)
- self.menubar = QtWidgets.QMenuBar(MainWindow)
- self.menubar.setGeometry(QtCore.QRect(0, 0, 307, 23))
- self.menubar.setObjectName("menubar")
- MainWindow.setMenuBar(self.menubar)
- self.statusbar = QtWidgets.QStatusBar(MainWindow)
- self.statusbar.setObjectName("statusbar")
- MainWindow.setStatusBar(self.statusbar)
-
- self.retranslateUi(MainWindow)
- QtCore.QMetaObject.connectSlotsByName(MainWindow)
-
- def retranslateUi(self, MainWindow):
- _translate = QtCore.QCoreApplication.translate
- MainWindow.setWindowTitle(_translate("MainWindow", "计时器——不含进程"))
- self.pushButton.setText(_translate("MainWindow", "开始计时"))
-
-
- if __name__ == "__main__":
- import sys
- app = QtWidgets.QApplication(sys.argv)
- MainWindow = QtWidgets.QMainWindow()
- ui = Ui_MainWindow()
- ui.setupUi(MainWindow)
- MainWindow.show()
- sys.exit(app.exec_())
构成:
class 自定义类名(QtWidgets.QMainWindow,Ui_MainWindow):
def __init__(self):
super(mainUi, self).__init__() # 重写类
self.setupUi(self)
self.run() # 用于绑定信号与槽def 事件1(self):
...def 事件2(self):
...def run(self):
控件名.动作.connect(事件1)
控件名.动作.connect(事件2)
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- # @Time : 2021/10/31 19:31
- # @Author : @linlianqin
- # @Site :
- # @File : timerWithoutThreadLogic.py
- # @Software: PyCharm
- # @description:不使用线程和信号槽实现计数器
-
- from PyQt5 import QtWidgets
- from pyqt_learn.signal_slot.不使用线程和信号槽实现计数器.timerWithoutThread import Ui_MainWindow
- from PyQt5.QtCore import QTimer
-
- '''
- 这里的信号和槽之间的关系不是自定义的,而是单纯通过pyqt自带的来实现,主要有两个信号与槽的对应关系:
- 1、信号:每秒计数器计数结束 槽函数:更新计数板上的信息
- 2、信号:点击计数板上的按钮 槽函数:计数器开始工作计数开始
- 3、信号:点击计数板上的按钮 槽函数:计数器停止计数并且计数归零
- 注:这里点击按钮后,其实就是开始了一个循环,在循环中不断地调用每秒计数器,然后更新计数板信息
- 步骤:
- 1、每秒计数器类实例化,即QTimer
- 2、编写每秒计数器结束时的执行事件,即更新计数板上的数字
- 3、编写按钮事件,开始计数
- 结果:
- 1、按下开始计时按钮后,计数板开始计数,同时按钮文字修改为停止检测
- 2、按下停止计时按钮后,计数板停止计数,并且计时归零,同时按钮文字修改为开始计时
- '''
-
- global sec
-
- class mainUi(QtWidgets.QMainWindow,Ui_MainWindow):
- def __init__(self):
- super(mainUi, self).__init__()
- self.setupUi(self)
- self.timer = QTimer() # 实例化每秒计数器
- global sec
- sec = 0
- self.run()
-
- # 更新计数器的数字
- def setTime(self):
- global sec
- sec += 1
- self.lcdNumber.display(sec)
-
- # 开始计数
- def startCount(self):
- # 设置计时间隔并启动,每隔1000毫秒(1秒)发送一次超时信号,循环进行,如果需要停止,可以调用timer.stop()进行停止
- self.timer.start(1000)
- # 当单击按钮开始计时后,按钮文字修改为停止计时,并且绑定的槽函数发生改变
- self.pushButton.setText("停止计时")
- self.pushButton.clicked.connect(self.stopTime)
-
- # 停止计时,计时置为0
- def stopTime(self):
- global sec
- sec = 0
- self.timer.stop()
- self.lcdNumber.display(sec)
- # 当单击按钮停止计时时,按钮文字修改为开始计时,按钮绑定槽函数改变
- self.pushButton.setText("开始计时")
- self.pushButton.clicked.connect(self.startCount)
-
- # 绑定信号与槽
- def run(self):
- # 每秒计数器计数结束后更新计数板数字
- self.timer.timeout.connect(self.setTime)
- # 单击按钮计数开始
- self.pushButton.clicked.connect(self.startCount)
-
- if __name__ == '__main__':
- import sys
- app = QtWidgets.QApplication(sys.argv)
- main = mainUi()
- main.show()
- sys.exit(app.exec_())
-
上述其实有两个信号,一个是单击按钮,一个是启动timer计时,而timer是内置的信号,
- # 更新计数器的数字
- def setTime(self):
- global sec
- sec += 1
- # 在内置信号绑定的事件中增加大循环,不会造成GUI卡死
- for i in range(1000000000):
- pass
- self.lcdNumber.display(sec)
实践证明计时器可以正常运行,内置信号的槽函数中增加大循环,不会造成GUI界面卡顿的现象,这是因为timer计数器内部使用了另一个线程来实现计数,不会影响GUI的主线程运行,因此不卡顿
- # 开始计数
- def startCount(self):
- # 设置计时间隔并启动,每隔1000毫秒(1秒)发送一次超时信号,循环进行,如果需要停止,可以调用timer.stop()进行停止
- self.timer.start(1000)
- # 在GUI控件对应的槽函数内增加耗时的大循环
- for i in range(1000000):
- print(i)
- pass
- # 当单击按钮开始计时后,按钮文字修改为停止计时,并且绑定的槽函数发生改变
- self.pushButton.setText("停止计时")
- self.pushButton.clicked.connect(self.stopTime)
按钮对应的槽函数内增加大循环,在大循环运行完前GUI会出现卡顿的情况,这是因为按钮是属于GUI控件,控件对应的槽函数是在GUI主线程中进行的,因此会 使得主线程短暂性假死状态,也就是会导致GUI卡顿,等循环结束后,GUI恢复正常
①含有复杂的运算
②含有耗时的循环
③time.sleep()
由卡顿的原因可以知道,卡顿主要是在GUI主线程运行的过程中,遇到了耗时的操作,这样导致在循环计算时,GUI产生假死卡顿状态。
因此我们只需要将耗时的操作择出来,然后另开一个新的线程去执行这些耗时的操作,而主线程则用于触发这个新线程的开始,这样就可以解决GUI卡顿。
run函数在线程调用start()函数时会自动运行
- from PyQt5.QtCore import QTimer,QThread
-
- # 新开一个线程进行循环的操作
- class newThread(QThread):
- def __init__(self):
- super(newThread, self).__init__()
-
- # 大循环
- def run(self):
- for i in range(1000000):
- print(i)
- pass
self.loopThread = newThread()
- # 开启新的线程来执行大循环
- self.loopThread.start()
另开线程后,不会出现卡顿的现象,在进行大循环的时候,计数板上的数字依旧可以实时地进行更新,这里GUI代码没有改变
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- # @Time : 2021/10/31 19:31
- # @Author : @linlianqin
- # @Site :
- # @File : timerWithoutThreadLogic.py
- # @Software: PyCharm
- # @description:
-
- from PyQt5 import QtWidgets
- from pyqt_learn.signal_slot.不使用线程和信号槽实现计数器.timerWithoutThread import Ui_MainWindow
- from PyQt5.QtCore import QTimer,QThread
- from PyQt5.Qt import pyqtSignal
-
-
- '''
- 这里的信号和槽之间的关系不是自定义的,而是单纯通过pyqt自带的来实现,主要有两个信号与槽的对应关系:
- 1、信号:每秒计数器计数结束 槽函数:更新计数板上的信息
- 2、信号:点击计数板上的按钮 槽函数:发射信号通知计数器开始计数
- 3、信号:点击计数板上的按钮 槽函数:计数器停止计数并且计数归零
- 4、信号:自定义信号 槽函数:计数器开始计数
- 注:这里点击按钮后,其实就是开始了一个循环,在循环中不断地调用每秒计数器,然后更新计数板信息
- 步骤:
- 1、每秒计数器类实例化,即QTimer
- 2、编写每秒计数器结束时的执行事件,即更新计数板上的数字
- 3、编写按钮事件,开始计数
- 结果:按钮对应的槽函数内增加大循环,在大循环运行完前GUI会出现卡顿的情况,这是因为按钮是属于GUI控件,控件对应的槽函数是在GUI主线程中进行的,因此会
- 使得主线程短暂性假死状态,也就是会导致GUI卡顿,等循环结束后,GUI恢复正常
- 方法:将循环部分择出来,然后另外开一个线程进行运行,然后单击按钮时,将线程开启,后台运行循环,而不会影响GUI主线程的运行,这样可以实现GUI不卡顿
- '''
-
- global sec
-
- # 新开一个线程进行循环的操作
- class newThread(QThread):
- def __init__(self):
- super(newThread, self).__init__()
-
- # 大循环
- def run(self):
- for i in range(1000000):
- print(i)
- pass
-
- class mainUi(QtWidgets.QMainWindow,Ui_MainWindow):
-
- def __init__(self):
- super(mainUi, self).__init__()
- self.setupUi(self)
- self.timer = QTimer() # 实例化每秒计数器
- self.loopThread = newThread()
- global sec
- sec = 0
- self.run()
-
- # 更新计数器的数字
- def setTime(self):
- global sec
- sec += 1
- self.lcdNumber.display(sec)
-
- # 开始计数
- def startCount(self):
- # 设置计时间隔并启动,每隔1000毫秒(1秒)发送一次超时信号,循环进行,如果需要停止,可以调用timer.stop()进行停止
- self.timer.start(1000)
- # 当单击按钮开始计时后,按钮文字修改为停止计时,并且绑定的槽函数发生改变
- self.pushButton.setText("停止计时")
- self.pushButton.clicked.connect(self.stopTime)
- # 开启新的线程来执行大循环
- self.loopThread.start()
-
- # 停止计时,计时置为0
- def stopTime(self):
- global sec
- sec = 0
- self.timer.stop()
- self.lcdNumber.display(sec)
- # 当单击按钮停止计时时,按钮文字修改为开始计时,按钮绑定槽函数改变
- self.pushButton.setText("开始计时")
- self.pushButton.clicked.connect(self.startCount)
-
- # 绑定信号与槽
- def run(self):
- # 每秒计数器计数结束后更新计数板数字
- self.timer.timeout.connect(self.setTime)
- # 单击按钮发射自定义信号
- # self.pushButton.clicked.connect(self.startCount)
- # self.pushButton.clicked.connect(lambda:self.signal.emit()) # 这里通过lambda将语句函数化
- self.pushButton.clicked.connect(self.startCount)
-
- if __name__ == '__main__':
- import sys
- app = QtWidgets.QApplication(sys.argv)
- main = mainUi()
- main.show()
- sys.exit(app.exec_())
-
GUI增加了一个label控件,用于实时更新显示大循环的循环次数
带参的类型根据需要传递的参数类型来确定
- # 自定义一个带整数参数的信号,用于点击按钮时使用
- intSignal = pyqtSignal(int)
这行代码一般出现在获取得到要传递的参数内容的位置,每获得一个新的参数内容,发射一次信号,因此这里在每循环一次就发射一次信号,信号带参,由信号绑定的槽函数接受参数
self.intSignal.emit(val)
注意:这里槽函数接收的参数个数和类型和信号发射的参数个数和类型是一致的。
-
- # 将自定义信号连接到修改标签事件槽函数,这里信号发射会返回参数,然后槽函数会自动接收返回的参数
- self.loopThread.intSignal.connect(self.setLabel)
- def setLabel(self,val):
- self.label.setText(str(val))
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- # @Time : 2021/10/31 19:31
- # @Author : @linlianqin
- # @Site :
- # @File : timerWithoutThreadLogic.py
- # @Software: PyCharm
- # @description:
-
- from PyQt5 import QtWidgets
- from pyqt_learn.signal_slot.利用信号槽传递参数.timerWithoutThread import Ui_MainWindow
- from PyQt5.QtCore import QTimer,QThread
- from PyQt5.Qt import pyqtSignal
-
- '''
- 这里的信号和槽之间的关系不是自定义的,而是单纯通过pyqt自带的来实现,主要有两个信号与槽的对应关系:
- 1、信号:计数器结束信号 槽函数:更新计数板上数字
- 2、信号:单击按钮 槽函数:开始计数,同时触发新线程开始运行
- 3、信号:新线程运行
- 注:这里点击按钮后,其实就是开始了一个循环,在循环中不断地调用每秒计数器,然后更新计数板信息
- 期望:单击按钮开始计数,同时GUI标签修改为循环的值
- 步骤:
- 1、创建继承QThread的类
- 2、重写类def __init__()
- 3、在类名和__init__之间自定义信号
- 4、在类的run方法中发射信号emit()
- 5、在GUI类的__init__中实例化线程
- 6、在GUI中将线程中的自定义信号绑定槽函数
- 注意:
- 1、发射信号时的参数需要和自定义信号时的参数类型匹配;
- 2、在GUI类中自定义信号绑定的槽函数的参数个数以及类型要和自定义信号定义的参数个数以及类型匹配
- '''
-
- global sec
-
- # 新开一个线程进行循环的操作
- class newThread(QThread):
- # 自定义一个带整数参数的信号,用于点击按钮时使用
- intSignal = pyqtSignal(int)
-
- def __init__(self):
- super(newThread, self).__init__()
-
- # 大循环,run函数调用线程start函数时自动启动
- def run(self):
- i = 0
- while True:
- print(i)
- i += 1
- self.emit_(i)
-
- # 发射信号
- def emit_(self,val):
- self.intSignal.emit(val)
-
- class mainUi(QtWidgets.QMainWindow,Ui_MainWindow):
-
- def __init__(self):
- super(mainUi, self).__init__()
- self.setupUi(self)
- self.timer = QTimer() # 实例化每秒计数器
- self.loopThread = newThread() # 实例化线程
- global sec
- sec = 0
- self.run()
-
- # 更新计数器的数字
- def setTime(self):
- global sec
- sec += 1
- self.lcdNumber.display(sec)
-
- # 开始计数
- def startCount(self):
- # 设置计时间隔并启动,每隔1000毫秒(1秒)发送一次超时信号,循环进行,如果需要停止,可以调用timer.stop()进行停止
- self.timer.start(1000)
- # 当单击按钮开始计时后,按钮文字修改为停止计时,并且绑定的槽函数发生改变
- self.pushButton.setText("停止计时")
- self.pushButton.clicked.connect(self.stopTime)
- # 开启新的线程来执行大循环
- self.loopThread.start()
-
-
- # 停止计时,计时置为0
- def stopTime(self):
- global sec
- sec = 0
- self.timer.stop()
- self.lcdNumber.display(sec)
- # 当单击按钮停止计时时,按钮文字修改为开始计时,按钮绑定槽函数改变
- self.pushButton.setText("开始计时")
- self.pushButton.clicked.connect(self.startCount)
-
- def setLabel(self,val):
- self.label.setText(str(val))
-
-
- # 绑定信号与槽
- def run(self):
- # 每秒计数器计数结束后更新计数板数字
- self.timer.timeout.connect(self.setTime)
- self.pushButton.clicked.connect(self.startCount)
- # 将自定义信号连接到修改标签事件槽函数,这里信号发射会返回参数,然后槽函数会自动接收返回的参数
- self.loopThread.intSignal.connect(self.setLabel)
-
- if __name__ == '__main__':
- import sys
- app = QtWidgets.QApplication(sys.argv)
- main = mainUi()
- main.show()
- sys.exit(app.exec_())
-
结果:计数板实时计数;标签实时滚动循环的次数
- 涉及到耗时计算另开线程计算避免GUI卡死
- 若需要传参,需要注意自定义信号参数类型和发射信号参数类型一致
- 发射信号参数类型和槽函数参数类型需要保持一致
参考:pyqt5-实时刷新页面(QApplication.processEvents()) - 猿码利剑 - 博客园https://www.cnblogs.com/liugp/p/10382624.html
对于执行很耗时的程序来说,由于PyQt需要等待程序执行完毕才能进行下一步,这个过程表现在界面上就是卡顿,而如果需要执行这个耗时程序时不断的刷新界面。那么就可以使用QApplication.processEvents(),那么就可以一边执行耗时程序,一边刷新界面的功能,给人的感觉就是程序运行很流畅,因此QApplicationEvents()的使用方法就是,在主函数执行耗时操作的地方,加入QApplication.processEvents()
- import sys,time
- from PyQt5.QtWidgets import QWidget,QPushButton,QApplication,QListWidget,QGridLayout
-
- class WinForm(QWidget):
- def __init__(self,parent=None):
- super(WinForm, self).__init__(parent)
- #设置标题与布局方式
- self.setWindowTitle('实时刷新界面的例子')
- layout=QGridLayout()
-
- #实例化列表控件与按钮控件
- self.listFile=QListWidget()
- self.btnStart=QPushButton('开始')
-
- #添加到布局中指定位置
- 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)
- win=WinForm()
- win.show()
- sys.exit(app.exec_())
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。