学习书籍《Python Qt GUI 与数据可视化编程》
一, 信号与槽功能概述
信号 (Signal): 就是在特定情况下被发射(emit) 的一种通告, 例如一个 PushButton 按钮最常见的信号就是鼠标单击时发射的 clicked()信号, 一个 ComboBox 最常见的信号是选择的项变化时发射的 CurrentIndexChanged()信号. GUI 程序设计的主要内容就是对界面上各组件发射的特定信号进行响应, 只需要知道什么情况下发射了哪些信号, 然后合理地去响应和处理这些信号就可以了.
槽 (Slot): 就是对信号响应的函数. 槽实质上是一个函数, 它可以被直接调用. 槽函数与一般的函数不同的是: 槽函数可以与一个信号关联, 当信号被发射时, 关联的槽函数会被自动执行. Qt 的类一般都有一些内建(build-in) 的槽函数, 例如 QWidget 有一个槽函数 close(), 其功能是关闭窗口. 如果将一个 PushButton 按钮的 clicked()信号与窗体的 close()槽函数关联, 那么点击按钮时就会关闭窗口.
二, 组件的信号与内建槽函数的关联
1. 先画一个图
属性设置表格如下:
对象名 | 类名称 | 属性设置 | 功能 |
Dialog | QDialog | windowTitle="Demo2-3 信号与槽" | 窗体的类名称是 Dialog,objectName 不要修改 |
textEdit | QPlainTextEdit | Text="PyQt5 编程指南 \ nPython 和 Qt." Font.PointSize=20 Font.bold=True | 用于显示文字,可编辑 |
chkBoxUnder | QCheckBox | Text="Underline" | 设置字体的下划线特定 |
chkBoxItalix | QCheckBox | Text="Italic" | 设置字体的斜体特性 |
chkBoxBold | QCheckBox | Text="Bold" | 设置字体的粗体特性 |
radioBlack | QRadioButton | Text="Black" | 设置字体颜色为黑色 |
radioRed | QRadioButton | Text="Red" | 设置字体颜色为红色 |
radioBlue | QRadioButton | Text="Blue" | 设置字体颜色为蓝色 |
btnClear | QPushButton | Text="清空" | 清空文本框内容 |
btnOK | QPushButton | Text="确定" | 返回确定,并关闭窗口 |
btnClose | QPushButton | Text="退出" | 退出程序 |
2. 界面组件布局管理
布局组件 | 功能 |
Vertival Layout | 垂直布局,组件自动在垂直方向上分布 |
Horizontal Layout | 水平布局,组件自动在水平方向上分布 |
Grid Layout | 网格状布局,网格状布局大小改变时,每个网格的大小都改变 |
Form Layout | 窗体布局,与网格布局类似。但是只有最右侧的一列网格会改变大小 |
Horizontal Spacer | 一个用于水平分隔的空格 |
Vertical Spacer | 一个用于垂直分隔的空格 |
使用: 先拖一个布局组件到窗体上, 例如窗体下方的 3 个按钮的布局, 先放一个 Horizontal Layout 到窗体上, 布局组件会以红色矩形框显示, 在向布局组件里拖放 3 个 PushButton 和两个 Horizontal Spacer, 就可以得到 3 个按钮的水平布局. 每个布局还有 layoutTopMargin,layoutBottomMargin,layoutLeftMargin,layoutRightMargin 这四个属性用于调整布局边框与内部组件之间的上, 下, 左, 右的边距大小.
在设计窗体的上方有一个工具栏, 用于使界面进入不同的设计状态, 以及进行布局设计, 工具栏商各按钮的功能如下:
按钮及快捷键 | 功能 |
Edit Widget(F3) | 界面设计进入编辑状态,也就是正常的设计状态 |
Edit Signals/Slots(F4) | 进入信号与槽的可视化设计状态 |
Edit Buddies | 进入伙伴关系编辑状态,可以设置一个 Lable 与一个组件成为伙伴关系 |
Edit Tab Order | 进入 Tab 顺序编辑状态,Tab 顺序是指在键盘上按 Tab 键时,输入焦点在界面各组件之间跳动的顺序 |
3. 组件的信号与内建槽函数的关联
Qt 的界面组件都是从 QWidget 继承而来的, 都支持信号与槽的功能. 每个类都有一些内建的信号和槽函数. 例如 QPushButton 按钮类常用的信号是 clicked(), 在按钮被单击时发射此信号. QDialog 是对话框类, 它有以下 3 个内建的槽函数.
accept(): 功能是关闭对话框, 表示肯定的选择, 例如 "确定".
reject(): 功能是关闭对话框, 表示否定的选择, 例如 "取消".
close(): 功能是关闭对话框
这 3 个槽函数都可以关闭对话框, 但是表示对话框的返回值不同, 我们希望将 "确定" 按钮与对话框的 accept()槽函数关联, 将 "退出" 按钮与对话框的 close()槽函数关联.
可以在 Qt Designer 里使用可视化的方式实现信号与槽函数的关联. 在 Qt Designer 里单击上方工具栏里的 "Edit Signals/Slots" 按钮, 窗体进入信号与槽函数编辑状态.
鼠标点选 "确定" 按钮, 在按住鼠标左键拖动到窗体的空白区域后释放左键, 这时出现关联设置对话框
此对话框里左边的列表框里显示了 btnOK 的信号 (上图显示的是 btnClear, 因为 btnOK 之前自己已经关联过, 就不重复关联了), 选择 clicked(), 右边的列表框里显示了 Dialog 的槽函数, 选择 accept(), 然后单击 "OK" 按钮. 同样的方法可以将 btnClose 的 clicked() 信号与 Dialog 的 close()槽函数关联, 值得注意的是, 如果没有看到 close()槽函数, 可以将下方的 "Show signals and slots inherited from QWidget" 打勾.
设置好这两个按钮的信号与槽关联后, 在窗体右下方的 Signals Slots 编辑器里就显示了这两个关联. 实际上可以直接在 Signals Slots 编辑器进行某个组件的内建信号与其他组件的内建槽函数关联.
4. 将 ui 文件转换为 py
Dialog.py
- # -*- coding: utf-8 -*-
- # Form implementation generated from reading ui file 'Dialog.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_Dialog(object):
- def setupUi(self, Dialog):
- Dialog.setObjectName("Dialog")
- Dialog.resize(400, 300)
- self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
- self.verticalLayout.setObjectName("verticalLayout")
- self.groupBox = QtWidgets.QGroupBox(Dialog)
- self.groupBox.setTitle("")
- self.groupBox.setObjectName("groupBox")
- self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.groupBox)
- self.horizontalLayout_3.setObjectName("horizontalLayout_3")
- self.chkBoxUnder = QtWidgets.QCheckBox(self.groupBox)
- self.chkBoxUnder.setObjectName("chkBoxUnder")
- self.horizontalLayout_3.addWidget(self.chkBoxUnder)
- self.chkBoxItalic = QtWidgets.QCheckBox(self.groupBox)
- self.chkBoxItalic.setObjectName("chkBoxItalic")
- self.horizontalLayout_3.addWidget(self.chkBoxItalic)
- self.chkBoxBold = QtWidgets.QCheckBox(self.groupBox)
- self.chkBoxBold.setObjectName("chkBoxBold")
- self.horizontalLayout_3.addWidget(self.chkBoxBold)
- self.verticalLayout.addWidget(self.groupBox)
- self.groupBox_2 = QtWidgets.QGroupBox(Dialog)
- self.groupBox_2.setTitle("")
- self.groupBox_2.setObjectName("groupBox_2")
- self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.groupBox_2)
- self.horizontalLayout_4.setObjectName("horizontalLayout_4")
- self.radioBlack = QtWidgets.QRadioButton(self.groupBox_2)
- self.radioBlack.setObjectName("radioBlack")
- self.horizontalLayout_4.addWidget(self.radioBlack)
- self.radioRed = QtWidgets.QRadioButton(self.groupBox_2)
- self.radioRed.setObjectName("radioRed")
- self.horizontalLayout_4.addWidget(self.radioRed)
- self.radioBlue = QtWidgets.QRadioButton(self.groupBox_2)
- self.radioBlue.setObjectName("radioBlue")
- self.horizontalLayout_4.addWidget(self.radioBlue)
- self.verticalLayout.addWidget(self.groupBox_2)
- self.plainTextEdit = QtWidgets.QPlainTextEdit(Dialog)
- font = QtGui.QFont()
- font.setPointSize(20)
- font.setBold(True)
- font.setWeight(75)
- self.plainTextEdit.setFont(font)
- self.plainTextEdit.setObjectName("plainTextEdit")
- self.verticalLayout.addWidget(self.plainTextEdit)
- self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
- self.horizontalLayout_2.setObjectName("horizontalLayout_2")
- spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
- self.horizontalLayout_2.addItem(spacerItem)
- self.btnClear = QtWidgets.QPushButton(Dialog)
- self.btnClear.setObjectName("btnClear")
- self.horizontalLayout_2.addWidget(self.btnClear)
- spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
- self.horizontalLayout_2.addItem(spacerItem1)
- self.btnOK = QtWidgets.QPushButton(Dialog)
- self.btnOK.setObjectName("btnOK")
- self.horizontalLayout_2.addWidget(self.btnOK)
- self.btnClose = QtWidgets.QPushButton(Dialog)
- self.btnClose.setObjectName("btnClose")
- self.horizontalLayout_2.addWidget(self.btnClose)
- self.verticalLayout.addLayout(self.horizontalLayout_2)
- self.retranslateUi(Dialog)
- self.btnOK.clicked.connect(Dialog.accept)
- self.btnClose.clicked.connect(Dialog.close)
- QtCore.QMetaObject.connectSlotsByName(Dialog)
- def retranslateUi(self, Dialog):
- _translate = QtCore.QCoreApplication.translate
- Dialog.setWindowTitle(_translate("Dialog", "Demo2-3 信号与槽"))
- self.chkBoxUnder.setText(_translate("Dialog", "Underline"))
- self.chkBoxItalic.setText(_translate("Dialog", "Italic"))
- self.chkBoxBold.setText(_translate("Dialog", "Bold"))
- self.radioBlack.setText(_translate("Dialog", "Black"))
- self.radioRed.setText(_translate("Dialog", "Red"))
- self.radioBlue.setText(_translate("Dialog", "Blue"))
- self.plainTextEdit.setPlainText(_translate("Dialog", "PyQt5 编程指南 \ n"
- "Python 和 Qt."))
- self.btnClear.setText(_translate("Dialog", "清空"))
- self.btnOK.setText(_translate("Dialog", "确定"))
- self.btnClose.setText(_translate("Dialog", "退出"))
- View Code
5. 窗体业务逻辑类文件 myDialog.py
- # # 与 UI 窗体类对应的业务逻辑类
- import sys
- from PyQt5.QtWidgets import QDialog, QApplication
- from Dialog import Ui_Dialog
- class QmyDialog(QDialog):
- def __init__(self, parent=None):
- super().__init__(parent) # 调用父类构造函数, 创建 QWidget 窗体
- self.ui = Ui_Dialog() # 创建 UI 对象
- self.ui.setupUi(self) # 构造 UI
- if __name__ == "__main__":
- App = QApplication(sys.argv) # 创建 App, 用 QApplication 类
- form = QmyDialog()
- form.show()
- sys.exit(App.exec_())
- View Code
6. 应用程序主程序文件 appMain.py
- # # GUI 应用程序主程序
- import sys
- from PyQt5.QtWidgets import QApplication
- from myDialog import QmyDialog
- App = QApplication(sys.argv) # 创建 GUI 应用程序
- mainform = QmyDialog() # 创建主窗体
- mainform.show() # 显示主窗体
- sys.exit(App.exec_())
- View Code
notes: 程序 myDialog.py 可以当做主程序直接运行, 但是建议单独编写一个主程序文件 appMain.py,appMain.py 的功能是创建应用程序和主窗体, 然后显示主窗体, 并开始运行应用程序. 它将 myDialog.py 文件的测试运行部分单独拿出来作为一个文件. 当一个应用程序有多个窗体, 并且窗体之间有数据传递时, appMain.py 负责创建应用程序的主窗体并运行起来, 这样使整个应用程序的结构更清晰.
来源: http://www.bubuko.com/infodetail-3494865.html