十五、PyQt5 之 信号与槽
#!/usr/bin/python # -*- coding:utf-8 -*- """ 信号(Signal)与槽(Slot) """ from PyQt5.QtWidgets import * import sys class SigalSlotDemo(QWidget): def __init__(self): super(SigalSlotDemo, self).__init__() self.initUI() def onClick(self): self.btn.setText("信号已经发出") self.btn.setStyleSheet("QPushButton(max-width:200px;min-width:200px") def initUI(self): self.setGeometry(300, 300, 500, 500) self.setWindowTitle("信号(Signal)与槽(Slot)") self.btn = QPushButton("我的按钮", self) self.btn.clicked.connect(self.onClick) if __name__ == '__main__': app = QApplication(sys.argv) gui = SigalSlotDemo() gui.show() sys.exit(app.exec_())
#!/usr/bin/python # -*- coding:utf-8 -*- """ 自定义信号 pyqtSignal() """ from PyQt5.QtCore import * class MyTypeSignal(QObject): # 定义一个信号, object 一定要有,不知道为什么 sendMsg = pyqtSignal(object) # 发送三个参数的信号 sendMsg1 = pyqtSignal(str, int, int) def run(self): # 触犯槽函数 self.sendMsg.emit("Hello PyQt5") def run1(self): # 触犯槽函数 self.sendMsg1.emit("Hello WY", 3, 4) # 定义一个槽类 class MySlot(QObject): def get(self, msg): print("信息: " + msg) def get1(self, msg, a, b): print(msg) print(a + b) if __name__ == '__main__': send = MyTypeSignal() slot = MySlot() # 将 slot.get的方法与信号绑定,一旦调用了 send.run() 方法,就会将 emit 发送的数据 # 传递给 slot.get() 的msg send.sendMsg.connect(slot.get) send.run() # 信息: Hello PyQt5 send.sendMsg1.connect(slot.get1) send.run1() # 调用断开信号之后,再调用 run 就会不起作用了 send.sendMsg.disconnect() send.run()
#!/usr/bin/python # -*- coding:utf-8 -*- """ 为了添加多个信号 (信号的重载) """ from PyQt5.QtCore import * class MultiSignal(QObject): signal1 = pyqtSignal() signal2 = pyqtSignal(int) signal3 = pyqtSignal(int, str) signal4 = pyqtSignal(list) signal5 = pyqtSignal(dict) # 声明一个重载版本的信号, 也就是槽函数参数可以是 int和str类型,也可以只有一个 str类型的参数 signal6 = pyqtSignal([int, str], [str]) def __init__(self): super(MultiSignal, self).__init__() 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.connect(self.signalCall6) # 想要只重载 [str] 这一个参数,使用下面的方法 self.signal6[str].connect(self.signalCall6Overload) self.signal1.emit() self.signal2.emit(10) self.signal3.emit(10, "Hello World") self.signal4.emit([1, 2, 3, 4, 5]) self.signal5.emit({"name": "wangyong"}) self.signal6[int, str].emit(20, "test") self.signal6[str].emit("TEST") 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__': multiSignal = MultiSignal()
#!/usr/bin/python # -*- coding:utf-8 -*- """ 信号与槽的 N 对 N 连接与断开连接 """ from PyQt5.QtCore import * class NNSignal(QObject): signal1 = pyqtSignal() signal2 = pyqtSignal(int) signal3 = pyqtSignal() def __init__(self): super(NNSignal, self).__init__() # 同时绑定两个槽函数 self.signal1.connect(self.call1) self.signal1.connect(self.call11) self.signal1.emit() # 可以绑定到信号上 self.signal2.connect(self.signal1) self.signal2.emit(2) # 本质上还是触发 self.signal1, 所以这个 2 没有任何作用,只是作为定义时候需要的参数 # 解除关联 self.signal1.disconnect(self.call1) self.signal1.disconnect(self.call11) self.signal2.disconnect(self.signal1) # 直接这样应该也是可以的 # self.signal1.disconnect() # self.signal2.disconnect() # 重新关联 self.signal1.connect(self.call1) self.signal2.connect(self.call2) self.signal1.emit() self.signal2.emit(2) self.signal3.connect(self.signal1) self.signal1.emit() # call1 emit self.signal3.emit() # call1 emit 这个和上一步绑定最后都是同一个槽函数 def call1(self): print("call1 emit") def call11(self): print("call11 emit") def call2(self, val): print("call2 emit:", val) if __name__ == '__main__': nnSignal = NNSignal()
#!/usr/bin/python # -*- coding:utf-8 -*- """ 为窗口类添加信号 """ from PyQt5.QtWidgets import * from PyQt5.QtCore import * import sys class WinSignal(QWidget): button_clicked_signal = pyqtSignal() def __init__(self): super(WinSignal, self).__init__() self.setWindowTitle("为窗口类添加信号") self.resize(300, 100) self.btn = QPushButton("关闭窗口", self) self.btn.clicked.connect(self.btn_clicked) self.button_clicked_signal.connect(self.btn_close) def btn_clicked(self): self.button_clicked_signal.emit() def btn_close(self): self.close() if __name__ == '__main__': app = QApplication(sys.argv) example = WinSignal() example.show() sys.exit(app.exec_())
#!/usr/bin/python # -*- coding:utf-8 -*- """ 多线程更新UI数据(在两个线程中传递数据) """ from PyQt5.QtCore import QThread, pyqtSignal, QDateTime from PyQt5.QtWidgets import QApplication, QDialog, QLineEdit import time import sys class BackendThread(QThread): update_date = pyqtSignal(str) def run(self): while True: data = QDateTime.currentDateTime() currentTime = data.toString("yyyy-MM-dd hh:mm:ss") self.update_date.emit(str(currentTime)) time.sleep(1) class ThreadUpdateUI(QDialog): def __init__(self): super(QDialog, self).__init__() self.setWindowTitle("多线程更新UI数据") self.resize(400, 100) self.input = QLineEdit(self) self.input.resize(400, 100) self.initUI() def initUI(self): self.backend = BackendThread() self.backend.update_date.connect(self.handleDisplay) self.backend.start() def handleDisplay(self, data): self.input.setText(data) if __name__ == '__main__': app = QApplication(sys.argv) example = ThreadUpdateUI() example.show() sys.exit(app.exec_())
#!/usr/bin/python # -*- coding:utf-8 -*- """ 信号与槽自动连接 setObjectName(signalname) on_objectname_signalname --- on_okButton_clicked 槽函数名称命名规则: on_发送者对象名称_发射信号名称(self, 参数) """ from PyQt5 import QtCore from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QPushButton import sys class AutoSignalSlot(QWidget): def __init__(self): super(AutoSignalSlot, self).__init__() self.okButton = QPushButton("ok", self) # 给按钮设置一个名字,名称随意, 但是槽函数需要根据名称来 self.okButton.setObjectName("okButton") self.cancelButton = QPushButton("cancel", self) self.cancelButton.setObjectName("cancelButton") layout = QHBoxLayout() layout.addWidget(self.okButton) layout.addWidget(self.cancelButton) self.setLayout(layout) # 根据名称统一自动调用参函数 QtCore.QMetaObject.connectSlotsByName(self) # 直接绑定槽函数 # self.okButton.clicked.connect(self.on_okButton_clicked) @QtCore.pyqtSlot() # 采用装饰器的方式,表示该函数只能用槽函数的方式绑定, 不能用以前的click来事件绑定 def on_okButton_clicked(self): print("点击了OK 按钮") @QtCore.pyqtSlot() def on_cancelButton_clicked(self): print("点击了 Cancel 按钮") if __name__ == '__main__': app = QApplication(sys.argv) example = AutoSignalSlot() example.show() sys.exit(app.exec_())
#!/usr/bin/python # -*- coding:utf-8 -*- """ 使用Lambda表达式为槽函数传递参数 Lambda表达式:匿名函数,也就是没有名字的函数 fun = lambda :print("hello world") fun() fun1 = lambda x,y:print(x,y) fun1("a","b") """ from PyQt5.QtWidgets import * import sys class LambdaSlotArg(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("使用Lambda表达式为槽函数传递参数") button1 = QPushButton("按钮1") button2 = QPushButton("按钮2") ok = 100 button1.clicked.connect(lambda: self.onButtonClick(10, ok)) button2.clicked.connect(lambda: self.onButtonClick(ok, -20)) button1.clicked.connect(lambda: QMessageBox.information(self, "结果", "单击了button1")) layout = QHBoxLayout() layout.addWidget(button1) layout.addWidget(button2) mainFrame = QWidget() mainFrame.setLayout(layout) self.setCentralWidget(mainFrame) def onButtonClick(self, m, n): print("m + n =", m + n) QMessageBox.information(self, "结果", str(m + n)) if __name__ == "__main__": app = QApplication(sys.argv) form = LambdaSlotArg() form.show() sys.exit(app.exec_())
#!/usr/bin/python # -*- coding:utf-8 -*- """ 使用Partial对象为槽函数传递参数 """ from PyQt5.QtWidgets import * import sys from functools import partial class PartialSlotArg(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("使用Partial表达式为槽函数传递参数") button1 = QPushButton("按钮1") button2 = QPushButton("按钮2") x = 20 y = -123 button1.clicked.connect(partial(self.onButtonClick, 10, 20)) button2.clicked.connect(partial(self.onButtonClick, x, y)) layout = QHBoxLayout() layout.addWidget(button1) layout.addWidget(button2) mainFrame = QWidget() mainFrame.setLayout(layout) self.setCentralWidget(mainFrame) def onButtonClick(self, m, n): print("m + n =", m + n) QMessageBox.information(self, "结果", str(m + n)) if __name__ == "__main__": app = QApplication(sys.argv) form = PartialSlotArg() form.show() sys.exit(app.exec_())
#!/usr/bin/python # -*- coding:utf-8 -*- """ Override(覆盖)槽函数, 也就是函数重写 """ from PyQt5.QtWidgets import * from PyQt5.QtCore import * import sys class OverrideSlot(QWidget): def __init__(self): super().__init__() self.setWindowTitle("Override(覆盖)槽函数") def keyPressEvent(self, e): if e.key() == Qt.Key_Escape: # 按下 esc 按键, 退出 self.close() elif e.key() == Qt.Key_Alt: self.setWindowTitle("按下Alt键") # 按下 Alt 键, 修改标题 if __name__ == "__main__": app = QApplication(sys.argv) form = OverrideSlot() form.show() sys.exit(app.exec_())
# DateDialog.py from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * class DateDialog(QDialog): def __init__(self, parent=None): # parent 参数一定要要有,看源码应该是原码有问题 super(DateDialog, self).__init__(parent) self.setWindowTitle("DateDialog") layout = QVBoxLayout(self) self.datetime = QDateTimeEdit(self) self.datetime.setCalendarPopup(True) self.datetime.setDateTime(QDateTime.currentDateTime()) layout.addWidget(self.datetime) buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) buttons.accepted.connect(self.accept) buttons.rejected.connect(self.reject) layout.addWidget(buttons) # self.setLayout(layout) def dateTime(self): return self.datetime.dateTime() @staticmethod def getDateTime(parent=None): dialog = DateDialog(parent) result = dialog.exec() date = dialog.dateTime() return (date.date(), date.time(), result == QDialog.Accepted)#!/usr/bin/python # -*- coding:utf-8 -*- """ 多窗口交互(1):不使用信号与槽 """ import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * from SignalSlot.DateDialog import DateDialog class MultiWindow1(QWidget): def __init__(self): super(MultiWindow1, self).__init__() self.setWindowTitle("多窗口交互(1):不使用信号与槽") self.lineEdit = QLineEdit(self) self.button1 = QPushButton('弹出对话框1') self.button1.clicked.connect(self.onButton1Click) self.button2 = QPushButton('弹出对话框2') self.button2.clicked.connect(self.onButton2Click) gridLayout = QGridLayout() gridLayout.addWidget(self.lineEdit) gridLayout.addWidget(self.button1) gridLayout.addWidget(self.button2) self.setLayout(gridLayout) def onButton1Click(self): dialog = DateDialog(self) result = dialog.exec() date = dialog.dateTime() self.lineEdit.setText(date.date().toString()) dialog.destroy() def onButton2Click(self): date, time, result = DateDialog.getDateTime() self.lineEdit.setText(date.toString()) if result == QDialog.Accepted: print("点击确定按钮") else: print("单击取消按钮") if __name__ == "__main__": app = QApplication(sys.argv) form = MultiWindow1() form.show() sys.exit(app.exec_())
#!/usr/bin/python # -*- coding:utf-8 -*- """ 多窗口交互(2):使用信号与槽 如果一个窗口A与另一个窗口B交互,那么A尽量不要直接访问B窗口中的控件, 应该访问B窗口中的信号,并指定与信号绑定的槽函数 例:如果A直接访问B窗口的控件,一旦B窗口控件发生改变,那么A和B的代码都需要变化 如果A访问的是B中的信号,那么B中的控件发生了改变,只需要修改B中的代码即可 """ import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * from SignalSlot.NewDateDialog import NewDateDialog class MultiWindow2(QWidget): def __init__(self, parent=None): super(MultiWindow2, self).__init__(parent) self.resize(400, 90) self.setWindowTitle("多窗口交互(2):使用信号与槽") self.open_btn = QPushButton("获取时间") self.lineEdit_inner = QLineEdit(self) self.lineEdit_emit = QLineEdit(self) self.open_btn.clicked.connect(self.openDialog) self.lineEdit_inner.setText('接收子窗口内置信号的时间') self.lineEdit_emit.setText('接收子窗口自定义信号的时间') grid = QGridLayout() grid.addWidget(self.lineEdit_inner) grid.addWidget(self.lineEdit_emit) grid.addWidget(self.open_btn) self.setLayout(grid) def openDialog(self): dialog = NewDateDialog(self) # 连接子窗口的内置信号与主窗口的槽函数 dialog.datetime_inner.dateTimeChanged.connect(self.deal_inner_slot) # 连接子窗口的自定义信号与主窗口的槽函数 dialog.Signal_OneParameter.connect(self.deal_emit_slot) dialog.show() def deal_inner_slot(self, date): self.lineEdit_inner.setText(date.toString()) def deal_emit_slot(self,dateStr): self.lineEdit_emit.setText(dateStr) if __name__ == "__main__": app = QApplication(sys.argv) form = MultiWindow2() form.show() sys.exit(app.exec_())