信号与槽的高级玩法
高级自定义信号与槽
所谓高级自定义信号与槽,指的是我们可以自己喜欢的方法定义信号与槽函数,并传递参数。自定义信号的一般流程如下:
- 定义信号
- 定义槽函数
- 连接信号与槽函数
- 发射信号
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
class CustSignal(QObject):
# 声明无参数的信号
signal1 = pyqtSignal()
# 声明带一个int类型参数的信号
signal2 = pyqtSignal(int)
# 声明带int和str类型参数的信号
signal3 = pyqtSignal(int,str)
# 声明带一个列表参数的信号
signal4 = pyqtSignal(list)
# 声明带一个字典类型参数的信号
signal5 = pyqtSignal(dict)
# 声明一个多重载版本的信号,包括带int和str类型参数的信号和带str类型参数的信号
signal6 = pyqtSignal([int,str],[str])
def __init__(self, parent=None):
super(CustSignal,self).__init__(parent)
# 将信号连接到指定槽函数
self.signal1.connect(self.signalCall1)
self.signal2.connect(self.signalCall2)
self.signal3.connect(self.signalCall3)
self.signal4.connect(self.signalCall4)
self.signal5.connect(self.signalCall5)
self.signal6[int,str].connect(self.signalCall6)
self.signal6[str].connect(self.signalCall6OverLoad)
# 发射信号
self.signal1.emit()
self.signal2.emit(1)
self.signal3.emit(1,"text")
self.signal4.emit([1,2,3,4])
self.signal5.emit({"name":"Juechen","age":"18"})
self.signal6[int,str].emit(1,"text")
self.signal6[str].emit("text")
def signalCall1(self):
print("signal1 emit")
def signalCall2(self,val):
print("signal2 emit, value:",val)
def signalCall3(self, val, text):
print("signal3 emit ,value:",val, text)
def signalCall4(self,val):
print("signal4 emit, value:",val)
def signalCall5(self,val):
print("signal5 emit, value:",val)
def signalCall6(self,val,text):
print("signal6 emit, value:", val, text)
def signalCall6OverLoad(self,val):
print("signal6 overload emit, value:",val)
if __name__ =='__main__':
custSignal = CustSignal()
signal1 emit
signal2 emit, value: 1
signal3 emit ,value: 1 text
signal4 emit, value: [1, 2, 3, 4]
signal5 emit, value: {'name': 'Juechen', 'age': '18'}
signal6 emit, value: 1 text
signal6 overload emit, value: text
使用自定义参数
我们经常需要给槽函数传递自定义参数的情况。但是clicked
信号是没有参数,但是槽函数需要参数。
使用lambda表达式
# -*- coding:utf-8 -*-
from PyQt5.QtWidgets import QMainWindow, QPushButton, QWidget, QMessageBox, QApplication, QHBoxLayout
import sys
class WinForm(QMainWindow):
def __init__(self, parent=None):
super(WinForm,self).__init__(parent)
button1 = QPushButton("Button 1")
button2 = QPushButton("Button 2")
button1.clicked.connect(lambda: self.onButtonClick(1))
button2.clicked.connect(lambda : self.onButtonClick(2))
layout = QHBoxLayout()
layout.addWidget(button1)
layout.addWidget(button2)
main_frame = QWidget()
main_frame.setLayout(layout)
self.setCentralWidget(main_frame)
def onButtonClick(self,n):
print("Button {} 被按下了".format(n))
QMessageBox.information(self, "信息提示框", 'Button {0} clicked'.format(n))
if __name__ == '__main__':
app = QApplication(sys.argv)
form = WinForm()
form.show()
sys.exit(app.exec_())
onButtonClick()
函数处理从两个按钮传来的信号。使用lambda表达式传递按钮数字给槽函数,也可以传递其他任何东西,甚至是按钮控件本身。
partial函数
使用functools
中的partial
函数。
button1.clicked.connect(partial(self.onButtonClick,1))
button2.clicked.connect(partial(self.onButtonClick,2))
装饰器信号与槽
所谓装饰器信号与槽,就是通过装饰器的方法来定义信号和槽函数。
@PyQt5.QtCore.pyqtSlot(参数)
def on_发送者对象名称_发射信号名称(self, 参数):
pass
这种方法有效的前提是下面的函数已经执行了:
QMetaObject.connectSlotsByName(QObject)
# -*- conding:utf-8 -*-
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
class CustWidget(QWidget):
def __init__(self, parent=None):
super(CustWidget, self).__init__(parent)
self.okButton = QPushButton("OK",self)
# 使用setObjectName设置对象名称
self.okButton.setObjectName("okButton")
layout = QHBoxLayout()
layout.addWidget(self.okButton)
self.setLayout(layout)
QMetaObject.connectSlotsByName(self)
@pyqtSlot()
def on_okButton_clicked(self):
print("点击了ok按钮")
if __name__ == '__main__':
app = QApplication(sys.argv)
win = CustWidget()
win.show()
sys.exit(app.exec_())
QMetaObject.connectSlotsName(QObject)
它是在PyQt5中根据信号名称自动连接到槽函数的核心代码。这行代码用来将QObject中的子孙的某些信号按照其objectName连接到相应的槽函数。
# -*- coding:utf-8 -*-
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
class CustWidget(QWidget):
def __init__(self, parent=None):
super(CustWidget, self).__init__(parent)
self.okButton = QPushButton("OK", self)
# 使用setObjectName设置对象名称
self.okButton.setObjectName("okButton")
layout = QHBoxLayout()
layout.addWidget(self.okButton)
self.setLayout(layout)
QMetaObject.connectSlotsByName(self)
self.okButton.clicked.connect(self.okButton_clikced)
def okButton_clikced(self):
print("单击了OK按钮")
if __name__ == '__main__':
app = QApplication(sys.argv)
win = CustWidget()
win.show()
sys.exit(app.exec_())
信号与槽的断开与连接
# -*- coding:utf-8 -*-
from PyQt5.QtCore import *
class SignalClass(QObject):
# 声明无参数的信号
signal1 = pyqtSignal()
# 声明带一个int类型参数的信号
signal2 = pyqtSignal(int)
def __init__(self, parent=None):
super(SignalClass, self).__init__(parent)
# 将信号signal1 连接到 sin1Call和sin2Call这两个槽函数
self.signal1.connect(self.sin1Call)
self.signal1.connect(self.sin2Call)
# 将信号2连接到signcall
self.signal2.connect(self.signal1)
# 发射信号
self.signal1.emit()
self.signal2.emit(1)
# 断开signall、signal2 信号与各槽函数的连接
self.signal1.disconnect(self.sin1Call)
self.signal1.disconnect(self.sin2Call)
self.signal2.disconnect(self.signal1)
# 将信号signal1和signal2连接到同一个槽函数sin1Call
self.signal1.connect(self.sin1Call)
self.signal2.connect(self.sin2Call)
# 再次发射信号
self.signal1.emit()
self.signal2.emit(1)
def sin1Call(self):
print("signal-1 emit")
def sin2Call(self):
print("signal-2 emit")
if __name__ == '__main__':
signal = SignalClass()
signal-1 emit
signal-2 emit
signal-1 emit
signal-2 emit
signal-1 emit
signal-2 emit
使用Qt Designer 进行页面显示与业务逻辑分类
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'MainWinSignalSlog02.ui'
#
# Created by: PyQt5 UI code generator 5.13.0
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(715, 225)
self.controlsGroup = QtWidgets.QGroupBox(Form)
self.controlsGroup.setGeometry(QtCore.QRect(10, 20, 451, 151))
self.controlsGroup.setObjectName("controlsGroup")
self.widget = QtWidgets.QWidget(self.controlsGroup)
self.widget.setGeometry(QtCore.QRect(10, 40, 411, 30))
self.widget.setObjectName("widget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.widget)
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout.setObjectName("horizontalLayout")
self.label = QtWidgets.QLabel(self.widget)
self.label.setObjectName("label")
self.horizontalLayout.addWidget(self.label)
self.numberSpinBox = QtWidgets.QSpinBox(self.widget)
self.numberSpinBox.setObjectName("numberSpinBox")
self.horizontalLayout.addWidget(self.numberSpinBox)
self.styleCombo = QtWidgets.QComboBox(self.widget)
self.styleCombo.setObjectName("styleCombo")
self.styleCombo.addItem("")
self.styleCombo.addItem("")
self.styleCombo.addItem("")
self.horizontalLayout.addWidget(self.styleCombo)
self.label_2 = QtWidgets.QLabel(self.widget)
self.label_2.setObjectName("label_2")
self.horizontalLayout.addWidget(self.label_2)
self.printButton = QtWidgets.QPushButton(self.widget)
self.printButton.setObjectName("printButton")
self.horizontalLayout.addWidget(self.printButton)
self.widget1 = QtWidgets.QWidget(self.controlsGroup)
self.widget1.setGeometry(QtCore.QRect(10, 100, 201, 30))
self.widget1.setObjectName("widget1")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.widget1)
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.previewStatus = QtWidgets.QCheckBox(self.widget1)
self.previewStatus.setObjectName("previewStatus")
self.horizontalLayout_2.addWidget(self.previewStatus)
self.previewButton = QtWidgets.QPushButton(self.widget1)
self.previewButton.setObjectName("previewButton")
self.horizontalLayout_2.addWidget(self.previewButton)
self.resultGroup = QtWidgets.QGroupBox(Form)
self.resultGroup.setGeometry(QtCore.QRect(470, 20, 231, 151))
self.resultGroup.setObjectName("resultGroup")
self.resultLabel = QtWidgets.QLabel(self.resultGroup)
self.resultLabel.setGeometry(QtCore.QRect(20, 30, 191, 101))
self.resultLabel.setObjectName("resultLabel")
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "打印控件"))
self.controlsGroup.setTitle(_translate("Form", "打印控制"))
self.label.setText(_translate("Form", "打印份数:"))
self.styleCombo.setItemText(0, _translate("Form", "A3"))
self.styleCombo.setItemText(1, _translate("Form", "A4"))
self.styleCombo.setItemText(2, _translate("Form", "A5"))
self.label_2.setText(_translate("Form", "纸张类型:"))
self.printButton.setText(_translate("Form", "打印"))
self.previewStatus.setText(_translate("Form", "全屏预览"))
self.previewButton.setText(_translate("Form", "预览"))
self.resultGroup.setTitle(_translate("Form", "操作结果"))
self.resultLabel.setText(_translate("Form", "<html><head/><body><p><br/></p></body></html>"))
import sys
from PyQt5.QtWidgets import *
from MainWinSignalSlog02 import Ui_Form
from PyQt5.QtCore import pyqtSignal, Qt
class MyMainWindow(QMainWindow,Ui_Form):
helpSignal = pyqtSignal(str)
printSignal = pyqtSignal(list)
# 声明一个多重载版本的信号,包括一个带int和str类型参数的信号,以及带str类型参数的信号
previewSignal = pyqtSignal([int,str],[str])
def __init__(self,parent=None):
super(MyMainWindow, self).__init__(parent)
self.setupUi(self)
self.initUI()
def initUI(self):
self.helpSignal.connect(self.showHelpMessage)
self.printSignal.connect(self.printPaper)
self.previewSignal[str].connect(self.previewPaper)
self.previewSignal[int,str].connect(self.previewPaperWithArgs)
self.printButton.clicked.connect(self.emitPrintSignal)
self.previewButton.clicked.connect(self.emitPreviewSignal)
# 发射预览信号
def emitPreviewSignal(self):
if self.previewStatus.isChecked() == True:
self.previewSignal[int,str].emit(1080,"Full Screen")
elif self.previewStatus.isChecked() == False:
self.previewSignal[str].emit("Preview")
# 发射打印信号
def emitPrintSignal(self):
pList = []
pList.append(self.numberSpinBox.value())
pList.append(self.styleCombo.currentText())
self.printSignal.emit(pList)
def printPaper(self,list):
self.resultLabel.setText("打印: "+"份数: " + str(list[0]) + "纸张: "+str(list[1]) )
def previewPaperWithArgs(self, style, text):
self.resultLabel.setText(str(style) + text)
def previewPaper(self,text):
self.resultLabel.setText(text)
# 重载按键事件
def keyPressEvent(self, event):
if event.key() == Qt.Key_F1:
self.helpSignal.emit("help message")
# 显示帮助信息
def showHelpMessage(self, message):
self.resultLabel.setText(message)
self.statusBar().showMessage(message)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MyMainWindow()
win.show()
sys.exit(app.exec_())
多线程中的信号与槽的使用
# -*- coding:utf-8 -*-
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import sys
class Main(QWidget):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
# 创建一个线程实例并设置名称、变量、信号与槽
self.thread = MyThread()
self.thread.setIdentity("thread1")
self.thread.sinOut.connect(self.outText)
self.thread.setVal(6)
def outText(self,text):
print(text)
class MyThread(QThread):
sinOut = pyqtSignal(str)
def __init__(self, parent = None):
super(MyThread, self).__init__(parent)
self.identity = None
def setIdentity(self, text):
self.identity = text
def setVal(self, val):
self.times = int(val)
# 执行线程的run方法
self.start()
def run(self):
while self.times > 0 and self.identity:
# 发射信号
self.sinOut.emit(self.identity + "==>" + str(self.times))
self.times -=1
if __name__ == '__main__':
app = QApplication(sys.argv)
main = Main()
main.show()
sys.exit(app.exec_())
thread1==>6
thread1==>5
thread1==>4
thread1==>3
thread1==>2
thread1==>1