學習書籍《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", "退出"))
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_())
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_())
notes: 程序myDialog.py可以當做主程序直接運行,但是建議單獨編寫一個主程序文件appMain.py,appMain.py的功能是創建應用程序和主窗體,然后顯示主窗體,並開始運行應用程序。它將myDialog.py文件的測試運行部分單獨拿出來作為一個文件。當一個應用程序有多個窗體,並且窗體之間有數據傳遞時,appMain.py負責創建應用程序的主窗體並運行起來,這樣使整個應用程序的結構更清晰。