
3.4 信號與槽關聯
- 信號 signal 與 slot 是 Qt 的核心機制。
- 如何為控件發射的信號指定對應的處理槽函數?
- 在窗口的 UI 設計中添加信號和槽
- 通過代碼連接信號和槽
- 通過 Eric 的”生成對話框代碼”的功能產生信號和槽
3.4.1 簡單入門
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(400, 300)
self.closeWinBtn = QtWidgets.QPushButton(Form)
self.closeWinBtn.setGeometry(QtCore.QRect(80, 120, 93, 28))
self.closeWinBtn.setObjectName("closeWinBtn")
self.retranslateUi(Form)
self.closeWinBtn.clicked.connect(Form.close)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.closeWinBtn.setText(_translate("Form", "關閉窗口"))
import sys
from PyQt5 import QtWidgets,QtCore
from untitled import *
class myFirstSignalSlot(QtWidgets.QWidget, Ui_Form):
def __init__(self):
super().__init__()
self.setupUi(self)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
myWin = myFirstSignalSlot()
myWin.show()
sys.exit(app.exec_())
3.4.2 快速進階
- 新手的兩大困惑
- PyQt 默認有哪些信號與槽
操作使用 Qt Designer 右下角的 signal/slot 編輯 - 如何使用這些信號與槽
操作將有信號與槽的 ui 文件編譯為 py 文件后,自行查看文件
- PyQt 默認有哪些信號與槽
- 使用 Eric6 生成信號和槽
- 切換到 Eric 的窗體選項卡
- 單擊右鍵選擇編譯窗體,生成 py 文件
- 再去右鍵,選擇”生成對話框代碼”,彈出窗體代碼生成器
- 在窗體代碼生成器中,點擊新建
- 然后選擇相應的信號發射
- 進行代碼的改寫
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(400, 300)
self.closeWinBtn = QtWidgets.QPushButton(Form)
self.closeWinBtn.setGeometry(QtCore.QRect(120, 120, 93, 28))
self.closeWinBtn.setObjectName("closeWinBtn")
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.closeWinBtn.setText(_translate("Form", "關閉窗口"))
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import QWidget, QApplication
from Ui_Eric002 import Ui_Form
class Form(QWidget, Ui_Form):
"""
Class documentation goes here.
"""
def __init__(self, parent=None):
"""
Constructor
@param parent reference to the parent widget
@type QWidget
"""
super(Form, self).__init__(parent)
self.setupUi(self)
@pyqtSlot()
def on_closeWinBtn_clicked(self):
"""
Slot documentation goes here.
"""
# TODO: not implemented yet
self.close()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
myWin = Form()
myWin.show()
sys.exit(app.exec_())
3.5 菜單欄與工具欄
3.5.1 界面設計
MainWindow 即為主窗口,主要包含菜單欄。工具欄、任務欄
- 對于一級菜單 可以使用&F 來添加菜單的快捷鍵
- 子菜單可以使用 Qt Designer 右下角的動作編輯器或者屬性編輯器中的 Shortcut 來添加快捷鍵
- 右鍵窗體,可以選擇添加工具欄
- 可以在屬性編輯器中新建 addWinAction 來添加動作
- 可以在動作編輯器中建立工具欄的圖標等,然后拖入進工具欄
- 將 ui 文件轉換為 py 文件
- 測試用例
3.5.2 測試效果
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 26))
self.menubar.setObjectName("menubar")
self.menu_F = QtWidgets.QMenu(self.menubar)
self.menu_F.setObjectName("menu_F")
self.menu_E = QtWidgets.QMenu(self.menubar)
self.menu_E.setObjectName("menu_E")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.toolBar = QtWidgets.QToolBar(MainWindow)
self.toolBar.setObjectName("toolBar")
MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar)
MainWindow.insertToolBarBreak(self.toolBar)
self.actionOpen = QtWidgets.QAction(MainWindow)
self.actionOpen.setObjectName("actionOpen")
self.actionNew = QtWidgets.QAction(MainWindow)
self.actionNew.setObjectName("actionNew")
self.actionClose = QtWidgets.QAction(MainWindow)
self.actionClose.setObjectName("actionClose")
self.addWinAction = QtWidgets.QAction(MainWindow)
self.addWinAction.setObjectName("addWinAction")
self.menu_F.addAction(self.actionOpen)
self.menu_F.addAction(self.actionNew)
self.menu_F.addAction(self.actionClose)
self.menubar.addAction(self.menu_F.menuAction())
self.menubar.addAction(self.menu_E.menuAction())
self.toolBar.addAction(self.addWinAction)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.menu_F.setTitle(_translate("MainWindow", "文件(&F)"))
self.menu_E.setTitle(_translate("MainWindow", "編輯(&E)"))
self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar"))
self.actionOpen.setText(_translate("MainWindow", "Open"))
self.actionOpen.setShortcut(_translate("MainWindow", "Ctrl+O"))
self.actionNew.setText(_translate("MainWindow", "New"))
self.actionClose.setText(_translate("MainWindow", "Close"))
self.addWinAction.setText(_translate("MainWindow", "添加窗體"))
self.addWinAction.setToolTip(_translate("MainWindow", "添加窗體"))
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import QMainWindow, QApplication, QFileDialog
from Ui_main import Ui_MainWindow
class MainWindow(QMainWindow, Ui_MainWindow):
"""
Class documentation goes here.
"""
def __init__(self, parent=None):
"""
Constructor
@param parent reference to the parent widget
@type QWidget
"""
super(MainWindow, self).__init__(parent)
self.setupUi(self)
@pyqtSlot()
def on_actionOpen_triggered(self):
"""
Slot documentation goes here.
"""
# TODO: not implemented yet
file, ok = QFileDialog.getOpenFileName(self, "打開", "D:/", "All Files(*);;Text Files(*.txt)")
self.statusbar.showMessage(file)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
myWin = MainWindow()
myWin.show()
sys.exit(app.exec_())
3.5.3 應用:加載其他窗口
概念在當前窗口中嵌入另一個窗口
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(400, 300)
self.textEdit = QtWidgets.QTextEdit(Form)
self.textEdit.setGeometry(QtCore.QRect(30, 30, 231, 161))
self.textEdit.setObjectName("textEdit")
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.textEdit.setHtml(_translate("Form", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'SimSun\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">我是子窗體的內容</p>\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p></body></html>"))
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayoutWidget = QtWidgets.QWidget(self.centralwidget)
self.gridLayoutWidget.setGeometry(QtCore.QRect(109, 49, 351, 311))
self.gridLayoutWidget.setObjectName("gridLayoutWidget")
self.gridLayout = QtWidgets.QGridLayout(self.gridLayoutWidget)
self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.gridLayout.setObjectName("gridLayout")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 26))
self.menubar.setObjectName("menubar")
self.menu = QtWidgets.QMenu(self.menubar)
self.menu.setObjectName("menu")
self.menu_2 = QtWidgets.QMenu(self.menubar)
self.menu_2.setObjectName("menu_2")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.toolBar = QtWidgets.QToolBar(MainWindow)
self.toolBar.setObjectName("toolBar")
MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar)
self.AddSubAction = QtWidgets.QAction(MainWindow)
self.AddSubAction.setObjectName("AddSubAction")
self.menubar.addAction(self.menu.menuAction())
self.menubar.addAction(self.menu_2.menuAction())
self.toolBar.addAction(self.AddSubAction)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.menu.setTitle(_translate("MainWindow", "文件"))
self.menu_2.setTitle(_translate("MainWindow", "編輯"))
self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar"))
self.AddSubAction.setText(_translate("MainWindow", "添加子窗體"))
self.AddSubAction.setToolTip(_translate("MainWindow", "添加子窗體"))
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget
from Ui_MainWidget import Ui_MainWindow
from Ui_subWidget import Ui_Form
class ChildForm(QWidget, Ui_Form):
def __init__(self):
super().__init__()
self.setupUi(self)
class MainWindow(QMainWindow, Ui_MainWindow):
"""
Class documentation goes here.
"""
def __init__(self, parent=None):
"""
Constructor
@param parent reference to the parent widget
@type QWidget
"""
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.child = ChildForm()
@pyqtSlot()
def on_AddSubAction_triggered(self):
"""
Slot documentation goes here.
"""
# TODO: not implemented yet
self.gridLayout.addWidget(self.child)
self.child.show()
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
Form = MainWindow()
Form.show()
sys.exit(app.exec_())
3.6 打包資源文件
使用 PyQt5 生成的應用程式引用圖片資源的兩大主要方法
-
將資源文件轉換為 python 文件,然后引用 python 文件
注意 Qt Designer 中不能直接加入圖片圖標資源,而是需要編寫.qrc 文件- 手動新建 apprc.qrc 文件
- 在 Eric 中創建資源文件
- 切換到資源選項卡
- 新建資源
- 在程式中通過相對路徑引用外部的圖片資源
3.6.1 使用 Qt Designer 加載資源文件
- 新建一個資源文件.qrc
- 打開 Qt Designer 創建一個普通的窗體
- 進入資源瀏覽器,打開資源編輯界面
- 創建前綴是 pic 的圖片文件,加載文件路徑
- 使用文本打開 qrc 文件,發現是 XML 文件包含了加載進去的圖片資源的路徑
3.6.2 在窗體中使用資源文件
- 使用 Qt Designer 在窗體中放置控件
- 更改控件的 Pixmap 值為資源文件
- 將 ui 文件轉換為 py 文件
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(400, 300)
self.label = QtWidgets.QLabel(Form)
self.label.setGeometry(QtCore.QRect(50, 90, 291, 141))
self.label.setText("")
self.label.setPixmap(QtGui.QPixmap(":/pic/PyQt5/Chapter03/images/cartoon1.ico"))
self.label.setObjectName("label")
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
import apprcc_rc
3.6.3 轉換資源文件為 python 文件
- 使用命令 pyrcc5 -o pyfile.py rcc.qrc
- 轉換后的 py 文件中可以看出該文件已經使用了 QtCore.qRegisterResourceData 進行了資源的初始化注冊,可以直接引用該文件了
注意資源文件的路徑問題- Qt Designer 使用圖片資源時,會在資源的引用路徑之前加上’:'(冒號)
- 此時的資源路徑 = qresource 這個標簽的前綴 pic + 源資源文件的路徑
第四章 PyQt5 基本窗口控件
目標學會如何部署和調整控件
4.1 QMainWindow
QMainWindow 主窗口為用戶提供一個應用程式框架,有自己的布局,可以在布局中添加控件,比如工具欄、菜單欄、狀態欄
4.1.1 窗口類型介紹
- QMainWindow、Qwidget、QDialog 三個類都是用來創建窗口的,可以直接使用,也可以繼承之后再去使用
- QMainWindow 包含菜單欄、工具欄、狀態欄、標題欄。是最常見的窗口形式
- QDialog 是對話框窗口的基類。對話框主要用來執行短期任務,或者與用戶進行交互,包括模態和非模態對話框兩種,沒有工具欄狀態欄等
- 如果不確定是哪一種窗口類型,則可以選擇普通窗口 QWidget
4.1.2 創建主窗口
- QMainWindow 就是一個頂層窗口,它可以包含很多界面元素,如菜單欄、工具欄、子窗口等
- QMainWindow 中會有一個控件 QWidget 占位符來占據著整個中心窗口,可以使用 setCentralWidget 來設置中心窗口
- QMainWindow 繼承自 QWidget 類,擁有基類的所有方法和屬性
- QMainWindow 類中比較重要的方法如下
- addToolBar 添加工具欄
- centralWidget 返回窗口中心的一個控件,未設置時返回 NULL
- menuBar 返回主窗口的菜單欄
- setCentralWidget 設置窗口中心的控件
- setStatusBar 設置狀態欄
- statusBar 獲得狀態欄對象后,調用狀態欄對象的 showMessage(message,int timeout=0)方法,第一個參數是要顯示的 message,第二個參數默認是 0,表示一直顯示狀態欄資訊,單位是毫秒 5000 表示停留在狀態欄 5s
注意 QMainWindow 不能設置布局(即使用 setLayout()方法),因為它有自己的布局,不需要再去設置
案例 4-1 創建主窗口
import sys
from PyQt5.QtWidgets import QMainWindow,QApplication
from PyQt5.QtGui import QIcon
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.resize(400, 200)
self.status = self.statusBar()
self.status.showMessage("這是狀態欄提示", 5000)
self.setWindowTitle("PyQt MainWindow例子")
self.setWindowIcon(QIcon("images/cartoon1.ico"))
if __name__ == '__main__':
app = QApplication(sys.argv)
myWin = MainWindow()
myWin.show()
sys.exit(app.exec_())
4.1.3 將主窗口放在屏幕中間
import sys
from PyQt5.QtWidgets import QDesktopWidget, QApplication, QMainWindow
class WinForm(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("主窗口放在屏幕中間的例子")
# 設置當前窗口的大小
self.resize(370, 250)
self.center()
def center(self):
# 獲取當前顯示屏幕的大小
screen = QDesktopWidget().screenGeometry()
# 獲取當前創建的窗口的大小
size = self.geometry()
# 將創建的窗口移動到顯示屏幕的中間位置
self.move((screen.width() - size.width())/2, (screen.height() - size.height())/2)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = WinForm()
win.show()
sys.exit(app.exec_())
4.1.4 關閉窗口
- 將按鈕的關閉信號與自定義的槽函數關聯起來,實現對當前實例化的應用程式的關閉操作
import sys
from PyQt5.QtWidgets import QMainWindow, QHBoxLayout, QPushButton, QApplication, QWidget
class WinForm(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("關閉主窗口的例子")
self.closeBtn = QPushButton('關閉主窗口')
self.closeBtn.clicked.connect(self.onBtnClick)
self.layout = QHBoxLayout()
self.layout.addWidget(self.closeBtn)
self.main_frame = QWidget()
self.main_frame.setLayout(self.layout)
self.setCentralWidget(self.main_frame)
# 以上的布局就是頂層窗口---中心控件窗口---布局管理器放置在中心控件上---按鈕放置在布局管理器這個容器中
def onBtnClick(self):
# sender 是發送信號的對象
sender = self.sender()
print(sender.text()+'被按下了')
qApp = QApplication.instance()
qApp.quit()
if __name__ == '__main__':
app = QApplication(sys.argv)
win = WinForm()
win.show()
sys.exit(app.exec_())
- 以上的布局就是頂層窗口—中心控件窗口—布局管理器放置在中心控件上—按鈕放置在布局管理器這個容器中
- 在 onBtnClick 中獲取 QApplication 的實例化對象,調用它的 quit()函數來關閉窗口,在槽函數中還可以獲取發送信號的對象,這里通過 text()獲取發送對象的顯示名稱
4.2 QWidget
- 基礎窗口控件 QWidget 是所有用戶界面對象的基類,所有窗口和控件都要直接或者間接繼承自 QWidget 類
- 窗口控件(Widget)是在 PyQt 中建立界面的主要元素
- 沒有嵌入到其他控件的控件稱為窗口,一般窗口都有邊框、標題欄、工具欄等
- 控件是指按鈕、復選框。文本框等組成程式的基本元素
- 一個程式可以有多個窗口,一個窗口也可以有多個控件
4.2.1 窗口的坐標系統
- QWidget 直接提供的成員函數 x()/y()獲取窗口左上角的坐標,width()/height()獲取客戶區的寬度和高度
- QWidget 的 geometry()提供的成員函數 x()/y()獲取客戶區的左上角的坐標,width()/heught()獲取客戶區的寬高
- QWidget 的 frameGeometry()提供的成員函數 x()/y()獲取窗口左上角的坐標,width()/height()獲取包含客戶區、標題欄和邊框在內的整個窗口的寬高
總結 QWidget.geomotry()是客戶區的位置大小;QWidget.frameGeomotry()是整個 QWidget 實例化窗口的位置大小
4.2.2 常用的幾何結構
QWidget 的兩種常用的幾何結構
-
不包含外邊各種邊框的幾何結構(一般來說就是我們的客戶區)
- 改變客戶區的面積–其實就是一個矩形類的實例(QRect)
QWidget.resize(width, height) QWidget.resize(Qsize) - 獲取客戶區的大小
QWidget.size() - 獲取客戶區的寬高
QWidget.width() QWidget.height()- 設置客戶區的寬高
# 設置固定寬度 QWidget.setFixedWidth(int width) # 設置固定高度 QWidget.setFixedHeight(int height) # 設置固定寬高 QWidget.setFixedSize(int width,int height) QWidget.seFixedSize(QSize size) # 設置寬高的同時還設置客戶區的位置 QWidget.setGeometry(int x,int y, int width, int height) QWidget.setGeometry(QRect rect) - 改變客戶區的面積–其實就是一個矩形類的實例(QRect)
-
包含外邊各種邊框的幾何結構
- 獲取整個窗口的大小和位置
QWidget.frameGeometry()- 設置窗口的位置
QWidget.move(int x, int y) QWidget.move(QPoint point)- 獲取窗口的左上角的坐標—即窗口的位置坐標
QWidget.pos()----即左上角的x、y坐標
案例 4-4 屏幕坐標系統的顯示
import sys
from PyQt5.QtWidgets import QWidget, QApplication, QPushButton
app = QApplication(sys.argv)
widget = QWidget()
btn = QPushButton(widget)
btn.setText("我是按鈕")
# 以widget左上角為(0,0)
btn.move(20, 40)
widget.resize(300, 200)
# 以屏幕的左上角為(0,0)
widget.setWindowTitle('PyQt 坐標系統的顯示案例')
widget.show()
# 獲取窗口的左上角的坐標以及客戶區的寬高
print("QWidget:")
print("w.x()=%d" % widget.x())
print("w.y()=%d" % widget.y())
print("w.width()=%d" % widget.width())
print("w.height()=%d" % widget.height())
# 獲取客戶區的左上角的坐標和客戶區的寬高
print("QWidget.geometry:")
print("w.geometry().x()=%d" % widget.geometry().x())
print("w.geometry().y()=%d" % widget.geometry().y())
print("w.geometry().width()=%d" % widget.geometry().width())
print("w.geometry().height()=%d" % widget.geometry().height())
sys.exit(app.exec_())
4.2.3 創建第一個 PyQt5 應用程式
案例 4-5 建立一個主窗口
import sys
from PyQt5.QtWidgets import QWidget, QApplication
app = QApplication(sys.argv)
win = QWidget()
win.resize(300, 200)
win.move(250, 150)
win.setWindowTitle('第一個應用程式APP')
win.show()
sys.exit(app.exec_())
解析
- 這兩行代碼用來載人必須的模塊,GUI 窗口控件基本都在 PyQt5.QtWidgets 中
import sys
from PyQt5.QtWidgets import QWidget, QApplication
- 每一個 PyQt5 程式都需要有一個 QApplication 對象,sys.argv 是一個命令行參數列表,比如 py 腳本在 shell 中執行等啟動腳本的參數是不一樣的
app = QApplication(sys.argv)
- QWidget 控件是 PyQt5 中所有用戶界面類的父類,第一行代碼使用沒有參數的構造函數,沒有父類,我們稱不嵌套在控件中的控件也就是_沒有父類的控件為窗口_
注意只有是窗口,才能設置窗口標題和大小,即后面兩行代碼才會生效
win = QWidget()
win.setWindowIcon()
win.setWindowTitle('第一個應用程式APP')
- 使用 resize()方法可以改變窗口的大小
win.resize(300, 200)
- 使用 move()方法可以設置窗口初始化的位置(x,y)
win.move(250, 150)
- 使用 show()方法將窗口控件顯示在屏幕上
win.show()
- 消息循環最后進入到該程式的主循環,事件處理從本行代碼開始,主循環接收事件消息并將其分發給程式的各個控件,只有調用 exit()或者主控件被銷毀,主循環才圓滿結束。使用 sys.exit()方法退出可以確保程式完整地執行結束,即運行成功,app.exec_()的返回值是 0,否則返回值為非 0
sys.exit(app.exec_())
4.2.4 為應用程式設置圖標
圖標下載鏈接
案例 4-6 設置程式圖標
import sys
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QWidget, QApplication
# 創建一個名為Icon的窗口類 繼承自QWidget類
class Icon(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle("程式圖標")
self.setWindowIcon(QIcon("./images/cartoon1.ico"))
if __name__ == '__main__':
app = QApplication(sys.argv)
ico = Icon()
ico.show()
sys.exit(app.exec_())
解析 from PyQt5.QtGui import QIcon 導入 QIcon 模塊,同時使用 self.setWindowIcon(QIcon(“./images/cartoon1.ico”))方法設置程式的圖標,參數是一個 QIcon 類型的對象
4.2.5 顯示氣泡提示資訊
案例 在關鍵操作的控件上設置一個氣泡提示
import sys
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import QApplication, QWidget, QToolTip, QPushButton
class winForm(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
QToolTip.setFont(QFont('宋體', 10))
self.setToolTip('widget')
self.setGeometry(200, 300, 400, 400)
self.setWindowTitle("氣泡提示demo")
btn = QPushButton(self)
btn.setText('我是按鈕')
btn.setToolTip("這是一個<b>按鈕</b>")
btn.resize(100, 100)
btn.move(20, 20)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = winForm()
win.show()
sys.exit(app.exec_())
解析
- 通過此行代碼設置氣泡提示資訊的字體與字號大小
QToolTip.setFont(QFont('宋體', 10))
- 創建工具提示,則需要調用 setToolTip()方法,該方法接受富文本格式的參數
btn.setToolTip("這是一個<b>按鈕</b>")
4.3 QLabel
- QLabel 對象作為一個占位符可以顯示不可編輯的文本或者圖片
- 可以放置 GIF 動畫,標記其他控件
- 純文本。鏈接或者富文本可以顯示在標簽上
常用方法 - setAlignment 按照固定值方式對齊文本 如 Qt.AlignLeft 水平方向靠左對齊
- setIndent 設置文本縮進
- setPixmap 設置 QLabel 為一個 Pixmap 圖片資源
- text() 獲取標簽的文本內容
- setText() 設置 QLabel 的文本內容
- selectedText() 返回所選擇的字符
- setBuddy() 設置 QLabel 的助記符以及 Buddy 伙伴,使用 setBuddy(QWidget *)設置 buddy,其 QLabel 必須是文本內容,并且使用”&”符號設置了助記符
常用信號 - linkActivated 當標簽中嵌入的超鏈接被單擊時,在新窗口打開這個超鏈接,setOpenExternalLinks 特性必須為 True
- linkHovered 當滑鼠懸停在這個標簽的超鏈接上時,需要用槽函數與這個信號進行綁定
案例 4-7 顯示 QLabel 標簽
import sys
from PyQt5.QtGui import QPixmap, QPalette
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QLabel, QWidget, QVBoxLayout
class QLabelWin(QWidget):
def __init__(self):
super().__init__()
label1 = QLabel(self)
label2 = QLabel(self)
label3 = QLabel(self)
label4 = QLabel(self)
# 初始化標簽控件
label1.setText("這是一個文本標簽")
# 表示填充背景
label1.setAutoFillBackground(True)
# 調色板
palette = QPalette()
palette.setColor(QPalette.Window, Qt.blue)
label1.setPalette(palette)
label1.setAlignment(Qt.AlignCenter)
label2.setText("<a href='#'>歡迎使用Python GUI 應用</a>")
label3.setAlignment(Qt.AlignCenter)
label3.setToolTip('這是一個圖片標簽')
label3.setPixmap(QPixmap("./images/python.jpg"))
label4.setText("<a href='https://tp.youmelive.com/uploads/2021/02/03/132339031451572.html'>歡迎訪問路飛學城</a>")
label4.setAlignment(Qt.AlignRight)
label4.setToolTip("這是一個超鏈接標簽")
# 在窗口布局中添加控件
vbox = QVBoxLayout()
vbox.addWidget(label1)
vbox.addStretch()
vbox.addWidget(label2)
vbox.addStretch()
vbox.addWidget(label3)
vbox.addStretch()
vbox.addWidget(label4)
# 允許標簽label控件訪問超鏈接
label2.setOpenExternalLinks(False)
# 打開允許訪問超鏈接,默認是不允許,需要使用setOpenExternalLinks(True)允許瀏覽器訪問超鏈接
label4.setOpenExternalLinks(True)
# 點擊文本框綁定槽函數
label4.linkActivated.connect(link_clicked)
# 懸停在文本上綁定槽函數
label2.linkHovered.connect(link_hovered)
# 設置文本的交互形式是滑鼠選擇文本,使選擇的文本處于選中狀態
label1.setTextInteractionFlags(Qt.TextSelectableByMouse)
self.setLayout(vbox)
self.setWindowTitle("QLabel 例子")
def link_clicked():
print('當用滑鼠點擊label-4標簽時,觸發事件')
def link_hovered():
print('當滑鼠懸停在label-2標簽時,觸發事件')
if __name__ == '__main__':
app = QApplication(sys.argv)
win = QLabelWin()
win.show()
sys.exit(app.exec_())
解析
- 設置文本標簽居中顯示
label1.setAlignment(Qt.AlignCenter)
- 打開外部鏈接
提示打開允許訪問超鏈接,默認是不允許,需要使用 setOpenExternalLinks(True)允許瀏覽器訪問超鏈接
label2.setOpenExternalLinks(False)
# 打開允許訪問超鏈接,默認是不允許,需要使用setOpenExternalLinks(True)允許瀏覽器訪問超鏈接
label4.setOpenExternalLinks(True) # 設置True 表示滑鼠點擊超鏈接后就會跳轉到對應的URL
# 點擊label綁定槽函數
label4.linkActivated.connect(link_clicked)
# 懸停label綁定槽函數
label2.linkHovered.connect(link_hovered)
案例 4-8 QLabel 標簽的快捷鍵的使用
import sys
from PyQt5.QtWidgets import *
class QLabelShortCut(QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle("QLabel 快捷鍵的使用")
nameLb1 = QLabel('&Name')
nameEd1 = QLineEdit()
nameLb1.setBuddy(nameEd1)
nameLb2 = QLabel('&Password')
nameEd2 = QLineEdit()
nameLb2.setBuddy(nameEd2)
btnOk = QPushButton('&OK')
btnCancel = QPushButton('&Cancel')
mainLayout = QGridLayout(self)
mainLayout.addWidget(nameLb1, 0, 0)
# 表示nameLb1 在柵格的第一行第二列并且行占據一行,列占據兩列
mainLayout.addWidget(nameEd1, 0, 1, 1, 2)
mainLayout.addWidget(nameLb2, 1, 0)
mainLayout.addWidget(nameEd2, 1, 1, 1, 2)
mainLayout.addWidget(btnOk, 2, 1)
mainLayout.addWidget(btnCancel, 2, 2)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = QLabelShortCut()
win.show()
sys.exit(app.exec_())
解析
在彈出的窗口中,按”ALT+N”快捷鍵可以切換到第一個文本框,因為設置了&Name 首字母快捷鍵
nameLb1 = QLabel('&Name')
nameEd1 = QLineEdit()
nameLb1.setBuddy(nameEd1)
4.4 文本框類控件
4.4.1 QLineEdit
單行文本控件,可以輸入單行字符串,如果要輸入多行,則需要使用 QTextEdit 類
常用方法
- setAlignment() 設置文本對齊方式
- clear() 清除文本框內容
- setEchoMode() 設置文本框顯示格式
- QLineEdit.Normal 表示正常顯示輸入的字符,此為默認選項
- QLineEdit.NoEcho 表示不顯示任何輸入的內容,常用于密碼類型的輸入且保密密碼的長度
- QLineEdit.Password 表示顯示的是輸入的掩碼,用戶保護密碼
- QLineEdit.PasswordEchoOnEdit 表示在編輯時顯示輸入的字符,負責顯示密碼的輸入—-輸入結束后顯示為掩碼
- setPlaceholderText() 設置文本框的浮動文字
- setMaxLength() 設置文本框所允許輸入的最大字符數
- setReadOnly() 設置文本框為只讀的
- setText() 設置文本框的內容
- Text() 獲取文本框的內容
- setDragEnabled() 設置文本框是否允許拖拽
- selectAll() 全選
- setFocus() 獲得焦點
- setInputMask() 設置輸入掩碼
- setValidator() 設置文本框的驗證器
- QIntValidator 限制輸入整數
- QDoubleValidator 限制輸入浮點數
- QRegexpValidator 檢查輸入是否符合正則表達式
重點設置驗證器中的正則檢測是最常用的最實用的
- 掩碼由掩碼字符和分隔符字符串組成,后面可以跟一個分號和空白字符,空白字符在編輯后會從文本中刪除
- 常見的幾種掩碼注意
- 000.000.000.000;_ IP 地址,空白字符是”_”
- HH:HH:HH:HH:HH;_ MAC 地址
- 0000:00:00 日期,空白字符是空格
-
AAAA-AAAA-AAAA-AAAA;# 許可證號,空白字符是”-“,所有字母字符都要轉換為大寫
常用信號
- selectionChanged 只要選擇改變了,這個信號就會被發射
- textChanged 只要文本內容被修改了,就會發射這個信號
- editingFinished 當編輯文本結束時,這個信號就會被發射出去
案例 4-9 EchoMode 的顯示效果
import sys
from PyQt5.QtWidgets import QApplication, QLineEdit, QWidget, QFormLayout
class lineEditEchoMode(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-9 EchoMode的顯示效果")
flo = QFormLayout()
pNormalLineEdit = QLineEdit()
pNoEchoLineEdit = QLineEdit()
pPasswordLineEdit = QLineEdit()
pPasswordEchoOnEditLineEdit = QLineEdit()
flo.addRow("Normal", pNormalLineEdit)
flo.addRow("NoEcho", pNoEchoLineEdit)
flo.addRow("Password", pPasswordLineEdit)
flo.addRow("PasswordOnEdit", pPasswordEchoOnEditLineEdit)
pNormalLineEdit.setPlaceholderText("Normal")
pNoEchoLineEdit.setPlaceholderText("NoEcho")
pPasswordLineEdit.setPlaceholderText("Password")
pPasswordEchoOnEditLineEdit.setPlaceholderText("PasswordEchoOnEdit")
# 設置顯示效果
pNormalLineEdit.setEchoMode(QLineEdit.Normal)
pNoEchoLineEdit.setEchoMode(QLineEdit.NoEcho)
pPasswordLineEdit.setEchoMode(QLineEdit.Password)
pPasswordEchoOnEditLineEdit.setEchoMode(QLineEdit.PasswordEchoOnEdit)
self.setLayout(flo)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = lineEditEchoMode()
win.show()
sys.exit(app.exec_())
案例 4-10 驗證器
import sys
from PyQt5.QtGui import QIntValidator, QDoubleValidator, QRegExpValidator
from PyQt5.QtCore import QRegExp
from PyQt5.QtWidgets import QApplication, QWidget, QFormLayout, QLineEdit
class lineEditValidators(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("驗證器demo")
flo = QFormLayout()
pIntLineEdit = QLineEdit()
pdoubleLineEdit = QLineEdit()
pRegexpLineEdit = QLineEdit()
flo.addRow("整型", pIntLineEdit)
flo.addRow("浮點型", pdoubleLineEdit)
flo.addRow("字符和數字", pRegexpLineEdit)
pIntLineEdit.setPlaceholderText("整型")
pdoubleLineEdit.setPlaceholderText("浮點型")
pRegexpLineEdit.setPlaceholderText("字母和數字")
# 整型 范圍 [1, 99]
pIntValidator = QIntValidator(self)
pIntValidator.setRange(1,99)
# 浮點型 范圍 【-360,360】 精度:小數點后兩位
pDoubleValidator = QDoubleValidator(self)
pDoubleValidator.setRange(-360, 360)
# 設置標準符號
pDoubleValidator.setNotation(QDoubleValidator.StandardNotation)
pDoubleValidator.setDecimals(2)
# 子母和數字
reg = QRegExp("[a-zA-Z0-9]+$")
pRegexpValidator = QRegExpValidator(self)
pRegexpValidator.setRegExp(reg)
# 設置驗證器
pIntLineEdit.setValidator(pIntValidator)
pdoubleLineEdit.setValidator(pDoubleValidator)
pRegexpLineEdit.setValidator(pRegexpValidator)
self.setLayout(flo)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = lineEditValidators()
win.show()
sys.exit(app.exec_())
案例 4-11 輸入掩碼
_用途_要限制用戶的輸入,除了驗證器,還可以使用掩碼,常見的有 IP 地址日期等
import sys
from PyQt5.QtWidgets import QLineEdit, QApplication, QWidget, QFormLayout
class lineEditMask(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('QLineEdit 的輸入掩碼的例子')
flo = QFormLayout()
pIPLineEdit = QLineEdit()
pMACLineEdit = QLineEdit()
pDateLineEdit = QLineEdit()
pLicenseLineEdit = QLineEdit()
pIPLineEdit.setInputMask("000.000.000.000;_")
pMACLineEdit.setInputMask("HH:HH:HH:HH:HH:HH;_")
pDateLineEdit.setInputMask("0000.00.00")
pLicenseLineEdit.setInputMask(">AAAAA-AAAAA-AAAAA-AAAAA-AAAAA;#")
flo.addRow("IP掩碼", pIPLineEdit)
flo.addRow("MAC掩碼", pMACLineEdit)
flo.addRow("日期掩碼", pDateLineEdit)
flo.addRow("許可證掩碼", pLicenseLineEdit)
self.setLayout(flo)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = lineEditMask()
win.show()
sys.exit(app.exec_())
案例 4-12 QLineEdit 的綜合示例
import sys
from PyQt5.QtWidgets import QWidget, QApplication, QLineEdit, QFormLayout
from PyQt5.QtGui import QIntValidator, QDoubleValidator, QRegExpValidator, QFont
from PyQt5.QtCore import QRegExp, Qt
class lineEditComprehensive(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("QLineEdit的綜合案例")
el = QLineEdit()
el.setValidator(QIntValidator())
el.setMaxLength(4)
el.setAlignment(Qt.AlignRight)
el.setFont(QFont('Arial', 20))
e2 = QLineEdit()
e2.setValidator(QDoubleValidator(0.99, 99.99, 2))
flo = QFormLayout()
flo.addRow("integer validator", el)
flo.addRow("Double validator", e2)
e3 = QLineEdit()
e3.setInputMask('+99_9999_999999')
flo.addRow("Input Mask", e3)
e4 = QLineEdit()
e4.textChanged.connect(self.textchanged)
flo.addRow("Text changed", e4)
e5 = QLineEdit()
e5.setEchoMode(QLineEdit.Password)
# 編輯結束時也就是回車鍵被按下時,因為只有回車鍵按下,編輯的修改才會生效
e5.editingFinished.connect(self.enterPress)
flo.addRow("Password", e5)
e6 = QLineEdit("Hello PyQt5")
e6.setReadOnly(True)
flo.addRow("Read Only", e6)
self.setLayout(flo)
def textchanged(self, text):
print("輸入內容為:" + text)
def enterPress(self):
print("密碼輸入完畢")
if __name__ == '__main__':
app = QApplication(sys.argv)
win = lineEditComprehensive()
win.show()
sys.exit(app.exec_())
解析
- e1 顯示文本的自定義字體,右對齊,只允許輸入整數
- e2 限制輸入小數點后兩位
- e3 需要一個輸入掩碼應用于電話號碼
- e4 需要發射信號 textchanged 鏈接到槽函數 textchanged()
- e5 設置顯示模式 EchoMode 為密碼輸入 Password
- e6 顯示一個默認的文本,不能編輯為只讀模式
4.4.2 QTextEdit
QTextEdit 類是一個多行文本框控件,可以顯示多行文本的內容,當文本內容超過控件顯示范圍時,可以顯示滾動條,
記住 QTextEdit 不僅可以顯示文本還可以顯示 HTML 文檔
常見方法
- setPlainText() 設置多行文本框的內容
- toPlainText() 獲取多行文本框的內容
- setHtml() 設置文本框內容 HTML 文檔
- toHtml() 獲取多行文本框內的 HTML 文檔
- clear() 清除多行文本框的內容
案例 4-13 QTextEdit 的使用
import sys
from PyQt5.QtWidgets import QApplication, QTextEdit, QWidget, QVBoxLayout, QPushButton
class textEditDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-13 QTextEdit的使用")
self.resize(300, 270)
self.textEdit = QTextEdit()
self.btnText = QPushButton("顯示文本")
self.btnHTML = QPushButton("顯示HTML")
layout = QVBoxLayout()
layout.addWidget(self.textEdit)
layout.addWidget(self.btnText)
layout.addWidget(self.btnHTML)
self.setLayout(layout)
self.btnText.clicked.connect(self.btnText_Clicked)
self.btnHTML.clicked.connect(self.btnHTML_Clicked)
def btnText_Clicked(self):
self.textEdit.setPlainText("Hello PyQt5!\n單擊按鈕")
def btnHTML_Clicked(self):
self.textEdit.setHtml("<font color='red' size='6'><red>Hello PyQt5!\n單擊按鈕</font>")
if __name__ == '__main__':
app = QApplication(sys.argv)
win = textEditDemo()
win.show()
sys.exit(app.exec_())
4.5 按鈕類控件
4.5.1 QAbstractButton
概念 QAbstractButton 類是抽象類,不能實例化,是所有其他按鈕類的父類,自定義按鈕類也必須要繼承該類并實現想要的功能
基類的狀態
- isDown() 提示按鈕是否被按下
- isChecked() 提示按鈕是否已經被標記
- isEnable() 提示按鈕是否可以被用戶點擊
- ischeckAble() 提示按鈕是否可以被標記
- setAutoRepeat() 設置按鈕是否在用戶長按時可以自動重復執行槽函數
基類的信號 - Pressed() 當滑鼠指針在按鈕上左鍵按下時發射信號
- Released() 當滑鼠左鍵被釋放時發射該信號
- Clicked() 當滑鼠左鍵被按下然后釋放時或者快捷鍵被釋放時發射該信號
- Toggled() 當按鈕的標記發生開關式切換時發射該信號
4.5.2 QPushButton
QPushButton 類繼承 QAbstractButton 類,形狀為矩形可以顯示文本標題或者圖標,同時也是一個命令按鈕,可以執行一些命令或者響應一些事件,常見的有確認、申請、取消、關閉等
- QPushButton 類中的常見方法
- setCheckable() 設置按鈕是否已經被選中,是為 True,反之 False
- toggle() 切換按鈕的狀態
- setIcon() 設置按鈕上的圖標
- setEnabled() 設置按鈕是否可以使用
- isChecked() 返回按鈕的狀態
- setDefault() 設置按鈕的默認狀態
- setText() 設置按鈕的顯示文本
- text() 返回按鈕的顯示文本
- 為 QPushButton 設置快捷鍵
self.button = QPushButton("&Down")
self.button.setDefault(True)
小知識點要想單純顯示&符號就需要使用”&&”,可以類比轉義字符
案例 4-14 QPushButton 按鈕的使用
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class pushButtonDemo(QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-14 QPushButton按鈕的使用")
layout = QVBoxLayout()
self.btn = QPushButton("Button1")
self.btn.setCheckable(True)
self.btn.toggle()
# 使用lambda匿名函數來表示槽函數名---此時我們看到就可以傳遞我們想要傳遞的參數了
self.btn.clicked.connect(lambda:self.wicthBtn(self.btn))
# 直接使用函數名
self.btn.clicked.connect(self.btnState)
layout.addWidget(self.btn)
self.btnImage = QPushButton("image")
# 將非ico圖標利用像素映射轉換為ico類型支持的圖片資源
self.btnImage.setIcon(QIcon(QPixmap("./images/python.png")))
self.btnImage.clicked.connect(lambda: self.wicthBtn(self.btnImage))
self.btnImage.clicked.connect(self.btnState)
layout.addWidget(self.btnImage)
self.btnDisabled = QPushButton("Disabled")
self.btnDisabled.setEnabled(False)
layout.addWidget(self.btnDisabled)
self.btnShortCut = QPushButton("&Download")
self.btnShortCut.setDefault(True)
self.btnShortCut.clicked.connect(lambda: self.wicthBtn(self.btnShortCut))
self.btnShortCut.clicked.connect(self.btnState)
layout.addWidget(self.btnShortCut)
self.setLayout(layout)
def btnState(self):
if self.btn.isChecked():
print('button pressed')
else:
print('button released')
def wicthBtn(self, btn):
print("clicked button is: " + btn.text())
if __name__ == '__main__':
app = QApplication(sys.argv)
win = pushButtonDemo()
win.show()
sys.exit(app.exec_())
解析
- 使用_lambda 匿名函數_的方式可以傳遞額外的參數 btn
self.btn.clicked.connect(lambda:self.wicthBtn(self.btn))
- setIcon()使用 QPixmap 對象傳遞一個圖像文件作為參數并將結果轉換成 QIcon 的 ico 文件的路徑
self.btnImage.setIcon(QIcon(QPixmap("./images/python.png")))
4.5.3 QRadioButton
當將單選按鈕切換到 on 或者 off 時,就會發送 toggle 信號,綁定這個信號,在按鈕狀態發生改變時就會觸發相應的行為
常用方法
- setCheckedable() 設置按鈕的是否被選中的狀態
- isChecked() 返回按鈕的狀態
- setText() 設置按鈕的顯示文本
- text() 獲取顯示文本
經驗之談
其實 toggle 信號是在切換單選狀態時發射的,而 chicked 的信號是在每次點擊時觸發的,在實際開發中,一般只有狀態的改變才有必要去響應,因此 toggle 信號更適合用于 QRadioButton 的狀態監控
案例 4-15 QRadioButton 按鈕的使用
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class Radiodemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-15 QRadioButton按鈕的使用")
layout = QHBoxLayout()
self.btn1 = QRadioButton("Button1")
self.btn1.setChecked(True)
self.btn1.toggled.connect(lambda: self.btnState(self.btn1))
layout.addWidget(self.btn1)
self.btn2 = QRadioButton("Button2")
self.btn2.toggled.connect(lambda: self.btnState(self.btn2))
layout.addWidget(self.btn2)
self.setLayout(layout)
def btnState(self, btn):
if btn.text() == "Button1":
if btn.isChecked() == True:
print(btn.text() + "is selected")
else:
print(btn.text() + "is deselected")
if btn.text() == "Button2":
if btn.isChecked() == True:
print(btn.text() + "is selected")
else:
print(btn.text() + "is deselected")
if __name__ == '__main__':
app = QApplication(sys.argv)
radioDemo = Radiodemo()
radioDemo.show()
sys.exit(app.exec_())
解析
- 在這個例子中,兩個互斥的單選按鈕被放置在窗口中,當選擇按鈕時按鈕狀態發生改變,將觸發 toggle 信號,從而調用槽函數
self.btn1.toggled.connect(lambda: self.btnState(self.btn1))
self.btn2.toggled.connect(lambda: self.btnState(self.btn2))
當觸發 toggle 信號后,使用 btnState 函數來檢查按鈕的狀態
4.5.4 QCheckBox
- 用戶可以選擇多個選項,和 QPusheButton 一樣,復選框可以顯示文本或者圖標,其中文本可以通過構造函數或者 setText()來設置,圖標可以通過 setIcon()來設置
- 在視覺上,QButtonGroup/QGroupBox 可以把許多復選框組織在一起
- QCheckBox 在選中和取消選中時,都會發射一個
stateChangeed信號,如果想在復選框狀態發生改變時觸發相應的行為,就連接該信號 - 可以使用 isChecked() 來查詢復選框是否被選中
- 除了常用的選中和未選中兩種狀態,QCheckBox 還有第三種狀態(半選中), 來表明”沒有變化”
- 如果要使用第三種狀態,則可以通過 setTriState()來使它生效。并使用 checkState()來查詢當前的切換狀態
常用方法 - setChecked() 設置復選框的狀態
- setText() 設置復選框的顯示文本
- text() 獲取復選框的顯示文本
- isChecked() 檢查復選框是否被選中
- setTriState() 設置復選框為三態復選框–其中一狀態是半選中
- checkState() 檢查復選框的狀態
三態復選框的狀態 - Qt.Checked 組件被選中
- Qt.PartiallyChecked 組件被半選中
- Qt.Unchecked 組件未被選中
案例 4-16 QCheckBox 按鈕的使用
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class CheckBoxDemo(QWidget):
def __init__(self):
super().__init__()
groupBox = QGroupBox("Checkboxes")
# 除去邊框,使其與背景色融為一體
groupBox.setFlat(True)
layout = QHBoxLayout()
self.checkBox1 = QCheckBox("&CheckBox1")
self.checkBox1.setChecked(True)
self.checkBox1.stateChanged.connect(lambda:self.btnState(self.checkBox1))
layout.addWidget(self.checkBox1)
self.checkBox2 = QCheckBox("checkBox2")
self.checkBox2.stateChanged.connect(lambda: self.btnState(self.checkBox2))
layout.addWidget(self.checkBox2)
self.checkBox3 = QCheckBox("checkBox3")
# 使用三態復選框
self.checkBox3.setTristate(True)
# 設置選中狀態為半選中
self.checkBox3.setCheckState(Qt.PartiallyChecked)
self.checkBox3.stateChanged.connect(lambda: self.btnState(self.checkBox3))
layout.addWidget(self.checkBox3)
groupBox.setLayout(layout)
mainLayout = QVBoxLayout()
mainLayout.addWidget(groupBox)
self.setLayout(mainLayout)
self.setWindowTitle("案例4-16 QCheckBox 按鈕的使用")
def btnState(self, btn):
chk1State = btn.text()+", is checked="+\
str(btn.isChecked())+", checkState="+\
str(btn.checkState())+"\n"
print(chk1State)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = CheckBoxDemo()
win.show()
sys.exit(app.exec_())
_注意_三態復選框中的 isChecked()獲取的選中狀態中半選中狀態也是 True,歸于選中的那一方(畢竟未選中是向選中狀態進行過渡)
解析
- 在這個例子中,將三個復選框添加到一個水平布局管理器中,并添加到一個 QGroupBox 組中
groupBox = QGroupBox("Checkboxes")
# 除去邊框,使其與背景色融為一體
groupBox.setFlat(True)
- 將復選框的 stateChanged 信號都鏈接到槽函數 stateChanged()使用 lambda 匿名函數的方式將對象傳遞給槽函數
def btnState(self, btn):
chk1State = btn.text()+", is checked="+\
str(btn.isChecked())+", checkState="+\
str(btn.checkState())+"\n"
print(chk1State)
- 實例化一個 QCheckBox 類對象 checkBox3,然后使用 setTristate()開啟三態模式
self.checkBox3 = QCheckBox("checkBox3")
# 使用三態復選框
self.checkBox3.setTristate(True)
# 設置選中狀態為半選中
self.checkBox3.setCheckState(Qt.PartiallyChecked)
self.checkBox3.stateChanged.connect(lambda: self.btnState(self.checkBox3))
layout.addWidget(self.checkBox3)
4.6 QComboBox(下拉列表框)
- QComboBox 是一個集按鈕和下拉選項于一體的控件,也被稱為下拉列表框
- QComboBox 常用的方法
- addItem() 添加一個下拉選項
- addItems() 從列表中添加下拉選項
- Clear() 刪除下拉選項集合中的所有選項
- count() 返回下拉選項中的所有數據的統計
- currentText() 返回當前選中項的文本
- itemText(i) 獲取索引為 i 的選項的文本
- currentIndex() 返回當前選中項的索引
- setItemText(int index, text)設置 index 對應的那個選項的文本內容
- QComboBox 常用的信號
- Activated 當用戶選中一個下拉選項時發射該信號
- currentIndexChanged 當下拉選項的索引發生變化時發射該信號
- highlighted 當選中一個已經被選中的下拉選項時,發射該信號
案例 4-17 QComboBox 下拉列表框的使用
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class ComboxDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-17 QComboBox下拉列表框的使用")
self.resize(300, 90)
layout = QVBoxLayout()
self.lb1 = QLabel("")
self.cb = QComboBox()
self.cb.addItem("C")
self.cb.addItem("C++")
self.cb.addItems(["Java", "C#", "Python"])
self.cb.currentIndexChanged.connect(self.selectionOnchanged)
layout.addWidget(self.cb)
layout.addWidget(self.lb1)
self.setLayout(layout)
def selectionOnchanged(self, i):
self.lb1.setText(self.cb.currentText())
print("Items in the list are: ")
for count in range(0, self.cb.count()):
print('item'+str(count)+'='+self.cb.itemText(count))
print("Current index", i, "selection changed", self.cb.currentText())
if __name__ == '__main__':
app = QApplication(sys.argv)
win = ComboxDemo()
win.show()
sys.exit(app.exec_())
解析
- 添加下拉選項時可以使用單個添加還可以使用列表一次添加多個
self.cb.addItem("C")
self.cb.addItem("C++")
self.cb.addItems(["Java", "C#", "Python"])
- 當下拉框的選項發生改變時就會發射 currentIndexChanged 這個信號并連接到自定義的那個槽函數中 selectionOnchanged
self.cb.currentIndexChanged.connect(self.selectionOnchanged)
- 當選中下拉列表框中的一個選項時,將該選項中的文本設置為標簽的文本
# i是選中列表項的索引,可以不使用傳參直接使用currentIndex獲取
def selectionOnchanged(self, i):
self.lb1.setText(self.cb.currentText())
4.7 QSpinBox(計數器)
- QSpinBox 是一個計數器控件,允許用戶選擇還可以調節,也能手動輸入
- 默認 QSpinBox 的取值是 0-99,步長是 1
- QSpinBox 類和 QDoubleSpinBox 類均派生自 QAbstractSpinBox 類,前者用于處理整型數據,后者則是處理浮點型數據
- QDoubleSpinBox 的默認精度是小數點后兩位,但是可以通過 setDecimals()來改變
常用的方法 - setMinimum() 設置計數器的下限也就是最小值
- setMaximum() 設置計數器的上限也就是最大值
- setRange() 設置計數器的最大值、最小值和步長值
- setValue() 設置計數器的當前值
- Value() 獲取計數器的當前值
- singleStep() 設置計數器的步長值
常用的信號 - valueChanged() QSpinBox 計數器在當前值被改變時就會發射該信號,可以在槽函數中通過 value 來獲取計數器的當前值
案例 4-18 QSpinBox 計數器的使用
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class spinDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-18 QSpinBox計數器的使用")
self.resize(300, 100)
layout = QVBoxLayout()
self.lb1 = QLabel("current value:")
self.lb1.setAlignment(Qt.AlignCenter)
layout.addWidget(self.lb1)
self.sp = QSpinBox()
layout.addWidget(self.sp)
self.sp.valueChanged.connect(self.valueChanged)
self.setLayout(layout)
def valueChanged(self):
self.lb1.setText("current value :"+ str(self.sp.value()))
if __name__ == '__main__':
app = QApplication(sys.argv)
win = spinDemo()
win.show()
sys.exit(app.exec_())
解析
- 將一個標簽和計數器放在一個垂直布局管理器中并且居中顯示,并且將信號 self.sp.valueChanged.connect(self.valueChanged)與自定義槽函數進行關聯
self.sp = QSpinBox()
layout.addWidget(self.sp)
self.sp.valueChanged.connect(self.valueChanged)
- valueChanged 函數將計數器的當前值設置在標簽文本中
def valueChanged(self):
self.lb1.setText("current value :"+ str(self.sp.value()))
4.8 QSlider(滑動條)
- QSlider 控件提供了允許用戶水平或者垂直方向移動的滑塊,并將滑塊的位置轉換為一個合法范圍內的整數值
_本質_在槽函數中對滑塊所在位置的處理就相當于是從一個整數范圍內取值 - 可以在構造函數時控制滑塊的顯示方式
self.slider = QSlider(Qt.Horizontal) # 水平方向的滑塊
self.slider = QSlider(Qt.Vertical) # 垂直方向的滑塊
常用的方法
- setMinimum() 設置滑塊的下限也就是最小值
- setMaximum() 設置滑塊的上限也就是最大值
- setValue() 設置滑塊的當前值
- Value() 獲取滑塊的當前值
- setTickInterval() 設置刻度的間隔值
- setSingleStep() 設置滑塊遞增/遞減的步長值
- setTickPosition() 設置刻度標記的位置,可以輸入一個枚舉值,這個枚舉值指定刻度線相對于滑塊和用戶操作的位置
- QSlider.NoTicks 不繪制任何刻度線
- QSlider.TicksBothSides 在滑塊的兩側繪制刻度線
- QSlider.TicksAbove 在水平滑塊上方繪制刻度線
- QSlider.TicksBelow 在水平滑塊的下面繪制刻度線
- QSlider.TicksLeft 在垂直滑塊的左邊繪制刻度線
- QSlider.TicksRight 在垂直滑塊的右邊繪制刻度線
常用的信號
- valueChanged 當滑塊的值發生變化時發射此信號,此信號是最常使用的
- sliderPressed 當用戶按下滑塊時觸發該信號
- sliderMoved 當用戶拖動滑塊時發射此信號
- sliderReleased 當用戶釋放滑塊時發射該信號
案例 4-19 QSlider 滑塊的使用
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class sliderDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-19 QSlider滑塊的使用")
self.resize(300, 100)
layout = QVBoxLayout()
self.lb1 = QLabel("Hello PyQt5")
self.lb1.setAlignment(Qt.AlignCenter)
layout.addWidget(self.lb1)
# 水平方向
self.s1 = QSlider(Qt.Horizontal)
# 設置最小值
self.s1.setMinimum(10)
# 設置最大值
self.s1.setMaximum(50)
# 設置步長
self.s1.setSingleStep(3)
# 設置當前值
self.s1.setValue(20)
# 刻度放置的位置,刻度在下方
self.s1.setTickPosition(QSlider.TicksBelow)
# 設置刻度間隔
self.s1.setTickInterval(5)
layout.addWidget(self.s1)
self.setLayout(layout)
# 連接信號槽函數
self.s1.valueChanged.connect(self.valueChanged)
def valueChanged(self):
print('current slider value=%s' % self.s1.value())
size = self.s1.value()
self.lb1.setFont(QFont("Arial", size))
if __name__ == '__main__':
app = QApplication(sys.argv)
win = sliderDemo()
win.show()
sys.exit(app.exec_())
解析
- 在這個例子中,將一個標簽和滑塊放置在垂直布局中,將滑塊的值變化關聯到對應的槽函數上
# 連接信號槽函數
self.s1.valueChanged.connect(self.valueChanged)
- 設置間隔為 5,最大是 50,最小是 10,一共是(最大值-最小值)/5+1 個刻度點,本例中就是(50-10)/5+1 = 9
# 設置最小值
self.s1.setMinimum(10)
# 設置最大值
self.s1.setMaximum(50)
# 設置步長
self.s1.setSingleStep(3)
# 設置當前值
self.s1.setValue(20)
# 刻度放置的位置,刻度在下方
self.s1.setTickPosition(QSlider.TicksBelow)
# 設置刻度間隔
self.s1.setTickInterval(5)
4.9 對話框類控件
4.9.1 QDialog
- QDialog 類的子類主要有
- QMessageBox
- QFileDialog
- QInputDialog
- QColorDialog
- QFontDialog
- 常用的方法
- setWindowTitle() 設置對話框的標題
- setWindowModality() 設置窗口模態
- Qt.NonModal 非模態,可以跟程式的其他窗口交互
- Qt.WindowModal 窗口模態,程式在未處理完當前對話框時,將阻止和對話框的父窗口交互(但是可以跟其他應用程式窗口交互)
- Qt.ApplicationModal 應用程式模態,在當前任務未處理完畢時,阻止和其他任何的窗口進行交互
案例 4-20 QDialog 對話框的使用
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class DialogDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-20 QDialog對話框的使用")
self.resize(350, 300)
self.btn = QPushButton(self)
self.btn.setText("彈出對話框")
self.btn.move(50, 50)
self.btn.clicked.connect(self.showdialog)
def showdialog(self):
dialog = QDialog()
btn = QPushButton("ok", dialog)
btn.move(50, 50)
dialog.setWindowTitle("Dialog")
# 設置對話框的模態屬性為應用程式級別的模態
dialog.setWindowModality(Qt.ApplicationModal)
dialog.exec_()
if __name__ == '__main__':
app = QApplication(sys.argv)
dialog = DialogDemo()
dialog.show()
sys.exit(app.exec_())
解析
- 在 Dialog 窗口的 windowModality 屬性決定是否為模態或者非模態。當用戶_按下 Esc 鍵時_,對話框會默認調用 QDialog.reject(),然后關閉對話框窗口————–reject 意思是拋棄、摒棄
- 由于對話框的 windowModality 屬性設置為 Qt.ApplicationModal 模態,用戶只有處理完畢當前彈出來的所有對話框后,才能關閉主窗口
dialog.setWindowModality(Qt.ApplicationModal)
總結
- Qt.ApplicationModal 用來設置彈出的對話框為完全模態,不將彈出來的所有對話框關閉是不能關閉主窗口的
- Qt.WindowModal 用來設置彈出的對話框為不完全模態,這樣在處理主窗口時就不要先處理完畢彈出來的對話框,也能直接關閉主窗口了
4.9.2 QMessageBox
- QMessageBox 是一種通用的彈出式對話框,用于顯示消息,允許用戶通過點擊不同標準的按鈕對消息進行反饋,每個標準都有一個預定義的文本、角色和十六進制數
- QMessageBox 類提供了常用的對話框,如提示、警告、錯誤、詢問、關于等對話框,這些對話框只是圖標不同,其他的功能是一樣的
- 常用的方法
- information(QWidget parent,title,text,button,defaultButton) 彈出消息對話框
- parent 指定的父窗口控件
- title 對話框的標題
- text 對話框的文本
- button 多個標準按鈕,默認是 OK 按鈕
- defaultButton 默認選中的標準按鈕,一般默認選中的就是第一個標準按鈕
- question(QWidget parent,title,text,button,defaultButton) 彈出問答對話框
- warning(QWidget parent,title,text,button,defaultButton) 彈出警告對話框
- critical(QWidget parent,title,text,button,defaultButton) 彈出嚴重錯誤對話框
- about(QWidget parent,title,text,button,defaultButton)
彈出關于對話框 - setTitle() 設置標題
- setText() 設置消息正文
- setIcon() 設置彈出對話框的圖標
- information(QWidget parent,title,text,button,defaultButton) 彈出消息對話框
- 常用的標準按鈕
- QMessage.Ok 同意操作
- QMessage.Cancel 取消操作
- QMessage.Yes 同意操作
- QMessage.No 取消操作
- QMessage.Abort 終止操作
- QMessage.Retry 重試操作
- QMessage.Ignore 忽略操作
案例 4-21 QMessageBox 的使用
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class MyMessageBox(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-21 QMessageBox的使用")
self.resize(300, 100)
self.myButton = QPushButton(self)
self.myButton.setText("點擊彈出消息框")
self.myButton.clicked.connect(self.msg)
def msg(self):
# 使用QMessageBox中的information對話框演示
popmsg = QMessageBox.information(self,"標題", "消息正文", QMessageBox.Yes|QMessageBox.No, QMessageBox.Yes)
print(popmsg)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MyMessageBox()
win.show()
sys.exit(app.exec_())
4.9.3 QInputDialog
- QInputDialog 控件是一個標準對話框,由一個文本框和兩個按鈕(_OK 和 Cancel_按鈕)組成
- 用戶單擊 OK 或者按 Enter 鍵后,在父窗口可以收集通過 QInputDialog 控件輸入的資訊
- 在 QInputDialog 中可以輸入數字、字符串或者列表中的選項,標簽用于顯示必要的資訊
常用的方法 - getInt() 從控件中獲得標準整數的輸入
- getDouble() 從控件中獲得標準浮點數的輸入
- getText() 從控件中獲得標準字符串的輸入
- getItem() 從控件中獲得列表里的選項輸入
案例 4-22 QInputDialog 輸入對話框的使用
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class inputDialogDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-22 QInputDialog輸入對話框的使用")
layout = QFormLayout()
self.btn1 = QPushButton("獲得列表中的選項")
self.btn1.clicked.connect(self.getItem)
self.le1 = QLineEdit()
layout.addRow(self.btn1, self.le1)
self.btn2 = QPushButton("獲得字符串")
self.btn2.clicked.connect(self.getText)
self.le2 = QLineEdit()
layout.addRow(self.btn2, self.le2)
self.btn3 = QPushButton("獲得整數")
self.btn3.clicked.connect(self.getInt)
self.le3 = QLineEdit()
layout.addRow(self.btn3, self.le3)
self.setLayout(layout)
def getItem(self):
items = ("C", "C++", "Java", "Python")
# 參數說明是父級窗口、標題、正文、迭代列表、默認第一個選項被選中、editable是否可以被編輯 默認是True
item, ok = QInputDialog.getItem(self, "Select input dialog", "語言列表", items, 0, False)
if ok and item:
self.le1.setText(item)
def getText(self):
text, ok = QInputDialog.getText(self, "Text input dialog", "輸入文本:")
if ok:
self.le2.setText(text)
def getInt(self):
num, ok = QInputDialog.getInt(self, "integer input dialog", "輸入整數:")
if ok:
self.le3.setText(str(num))
if __name__ == '__main__':
app = QApplication(sys.argv)
win = inputDialogDemo()
win.show()
sys.exit(app.exec_())
解析
- QInputDialog.getItem()函數被調用時,彈出的對話框中包含一個 Qcombo 下拉列表框控件和兩個按鈕,用戶從其中選擇一個選項時,允許用戶確認或者取消操作
def getItem(self):
items = ("C", "C++", "Java", "Python")
item, ok = QInputDialog.getItem(self, "Select input dialog", "語言列表", items, 0, False)
if ok and item:
self.le1.setText(item)
- 同理 QInputDialog.getInt()包含一個 QSpinBox 計數器控件和兩個按鈕,允許用戶輸入整數
4.9.4 QFontDialog
- QFontDialog 控件是一個常用的字體選擇對話框,可以讓用戶選擇顯示文本的字號大小、樣式和格式
常用方法 - getFont() 從控件中選取所想要的字體大小、樣式和格式
案例 4-23 QFontDialog 字體對話框的使用
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class FontDialogDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-23 QFontDialog字體對話框的使用")
layout = QVBoxLayout()
self.fontButton = QPushButton("選擇字體")
self.fontButton.clicked.connect(self.getFont)
self.fontEdit = QLabel("hello 測試字體例子")
layout.addWidget(self.fontButton)
layout.addWidget(self.fontEdit)
self.setLayout(layout)
def getFont(self):
font, ok = QFontDialog.getFont()
if ok:
self.fontEdit.setFont(font)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = FontDialogDemo()
win.show()
sys.exit(app.exec_())
解析
- 通過接收選擇的字體對象以及 ok 狀態來在槽函數中作出響應
def getFont(self):
font, ok = QFontDialog.getFont()
if ok:
self.fontEdit.setFont(font)
4.9.5 QFileDialog
- QFileDialog 是用于打開文件時使用的文件過濾器,用于顯示指定擴展名的文件,也可以設置 QFileDialog 打開文件時的起始目錄和指定的擴展名文件
- 常用的方法
- getOpenFileName() 返回用戶選擇文件的文件名,并打開該文件
- getSaveFileName() 返回用戶選擇的文件名并保存文件
- setFileMode() 設置選擇的文件類型,常見的有
- QFileDialog.AnyFile 任何文件
- QFileDialog.ExistingFile 已存在的文件
- QFileDialog.Directory 文件目錄
- QFileDialog.ExistingFiles 已存在的多個文件
- setFilter() 設置過濾器,只顯示過濾器允許的文件類型,其他的文件不顯示在文件對話框中
案例 4-24 QFileDialog 文件對話框的使用
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class FileDialogDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-24 QFileDialog文件對話框的使用")
layout = QVBoxLayout()
self.btn = QPushButton("加載圖片")
self.btn.clicked.connect(self.getPic)
self.lb1 = QLabel("")
self.btn1 = QPushButton("加載文本文件")
self.btn1.clicked.connect(self.getTxt)
self.contents = QTextEdit()
layout.addWidget(self.btn1)
layout.addWidget(self.btn)
layout.addWidget(self.lb1)
layout.addWidget(self.contents)
self.setLayout(layout)
def getPic(self):
# 下劃線是一定要加的,不然會出錯
fname, _ = QFileDialog.getOpenFileName(self, "Open File", "c:\\","Image Files (*.jpg *.png)")
self.lb1.setPixmap(QPixmap(fname))
def getTxt(self):
dig = QFileDialog()
dig.setFileMode(QFileDialog.AnyFile)
dig.setFilter(QDir.Files)
if dig.exec_():
filename = dig.selectedFiles()
f = open(filename[0], 'r')
with f:
data = f.read()
self.contents.setText(data)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = FileDialogDemo()
win.show()
sys.exit(app.exec_())
解析
- 第一個按鈕使用 QFileDialog.getOpenFileName(),調用文件對話框來顯示圖像,并顯示在第一個標簽中,負責打開的是 C 盤
def getPic(self):
# 下劃線是一定要加的,不然會出錯
fname, _ = QFileDialog.getOpenFileName(self, "Open File", "c:\\","Image Files (*.jpg *.png)")
# 這里就是在標簽上加載圖片資源
self.lb1.setPixmap(QPixmap(fname))
_注意_fname, _ = QFileDialog.getOpenFileName()中的下劃線是必不可少的,不然程式會報錯
參數分析
- 第一個參數是指定父級組件
- 第二個參數是 QFileDialog 對話框的標題
- 第三個參數是對話框顯示時默認打開的目錄
- . 代表程式運行目錄
- / 代表當前盤下的根目錄
- 第四個參數是對話框中文件擴展名的過濾器(Filter),比如使用”Image Files(*.jpg *.png)”表示只能顯示擴展名為.jpg 或者.png 的文件
- 第二個按鈕使用文件對話框 QFileDialog 對象的 exec 方法來選擇文件,并把所選擇的文件的內容顯示在多行文本編輯框中
def getTxt(self):
dig = QFileDialog()
dig.setFileMode(QFileDialog.AnyFile)
dig.setFilter(QDir.Files)
if dig.exec_():
filename = dig.selectedFiles()
f = open(filename[0], 'r', encoding="utf-8")
with f:
data = f.read()
self.contents.setText(data)
4.10 窗口繪圖類控件
- 主要為三大類
- QPainter 畫布
- QPen 畫筆
- QBrush 畫刷
- QPixmap 的作用是加載并呈現本地圖像,而圖像的呈現本質也是通過繪圖的方式來實現的,所以 QPixmap 也可以視為繪圖的一個類
4.10.1 QPainter—自我理解為畫布
- QPainter 類在 QWidget 控件上執行繪圖操作,可以繪制從簡單的直線到復雜的餅圖
- 繪制操作在 QWidget.paintEvent()中完成
- 繪制方法必須在 QtGui.QPainter 對象的 begin()和 end()之間
常用的方法 - begin() 開始在目標設備上繪制
- drawArc() 在起始角度和最終角度之間畫弧
- drawEllipse() 在一個矩形內畫圓
- drawLine(int x1,int y1,int x2,int y2) 繪制一條指定的端點坐標的線(x1,y1)—>(x2,y2),并且此時的畫筆位置在(x2,y2)
- drawPixmap() 從圖像文件中提取 Pixmap 并將其顯示在指定的位置上
- drawPolygon() 使用坐標數組繪制多邊形
- drawRect(int x,inty,int w,int h) 在坐標(x,y)處為矩形的左上角起點繪制寬高 w、h 的矩形
- drawText() 在給定坐標處繪制顯示的文字
- fillRect() 使用 QColor 參數填充矩形的顏色
- setBrush() 設置畫刷的樣式
- setPen() 設置畫筆的樣式
——-設置畫筆的風格(PenStyle)- Qt.NoPen 沒有線
- Qt.SolidLine 一條簡單的實線
- Qt.DashLine 像素分割的短線
- Qt.DotLine 像素分割的點
- Qt.DashDotLine 輪流交替的點和短線
- Qt.DashDotDotLine 一條短線,兩個點,重復交替
- Qt.MPenStyle 畫筆風格的掩碼
案例 4-25 繪制文字
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class DrawingTextDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-25 繪制文字")
self.resize(300, 200)
self.text = "歡迎學習 PyQt5"
def paintEvent(self, event):
painter = QPainter(self)
painter.begin(self)
# 自定義繪制方法
self.drawText(event, painter)
painter.end()
def drawText(self, event, painter):
# 設置畫筆的顏色
painter.setPen(QColor(168, 34, 3))
# 設置字體
painter.setFont(QFont('SimSun', 20))
# 繪制文字
painter.drawText(event.rect(), Qt.AlignCenter, self.text)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = DrawingTextDemo()
win.show()
sys.exit(app.exec_())
解析
- 定義一個繪制事件,所有的繪制操作都發生在此事件中
def paintEvent(self, event):
painter = QPainter(self)
painter.begin(self)
# 自定義繪制方法
self.drawText(event, painter)
painter.end()
- QtGui.QPainter 類負責所有低級別的繪制,所有的繪制方法都要放在 begin 和 end 之間,此處放置的繪制方法是 drawText
def drawText(self, event, painter):
# 設置畫筆的顏色
painter.setPen(QColor(168, 34, 3))
# 設置字體
painter.setFont(QFont('SimSun', 20))
# 繪制文字
painter.drawText(event.rect(), Qt.AlignCenter, self.text)
案例 4-26 繪制點
import sys, math
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class DrawingPointDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-26 繪制點")
self.resize(300, 200)
def paintEvent(self, event):
# 初始化繪制工具
painter = QPainter(self)
# 開始在窗口中進行繪制
painter.begin(self)
# 自定義畫點方法
self.drawPointers(painter)
painter.end()
def drawPointers(self, painter):
painter.setPen(Qt.red)
size = self.size()
for i in range(1000):
# 繪制正弦函數圖形,周期是[-100, 100]
x = 100*(-1+2.0*i/1000) + size.width()/2.0
y = -50*math.sin((x-size.width()/2.0)*math.pi/50)+size.height()/2.0
painter.drawPoint(x, y)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = DrawingPointDemo()
win.show()
sys.exit(app.exec_()
解析
- 畫筆設置為紅色,使用預定義的 Qt.red 顏色
painter.setPen(Qt.red)
- 使用 drawPoint()方法繪制一個個的點
painter.drawPoint(x, y)
4.10.2 QPen
- QPen 鋼筆是一個基本的圖形對象,用于繪制直線、曲線或者輪廓線畫出矩形,橢圓形、多邊形以及其他形狀等
案例 4-27 QPen 鋼筆的使用
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class PenDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-27 QPen鋼筆的使用")
self.setGeometry(300, 300, 280, 270)
def paintEvent(self, Event):
painter = QPainter()
painter.begin(self)
self.drawLines(painter)
painter.end()
def drawLines(self, painter):
# pen的樣式是線條黑色 2px 實線
pen = QPen(Qt.black, 2, Qt.SolidLine)
painter.setPen(pen)
painter.drawLine(20, 40, 250, 40)
pen.setStyle(Qt.DashLine)
painter.setPen(pen)
painter.drawLine(20, 120, 250, 120)
pen.setStyle(Qt.DotLine)
painter.setPen(pen)
painter.drawLine(20, 160, 250, 160)
pen.setStyle(Qt.DashDotDotLine)
painter.setPen(pen)
painter.drawLine(20, 200, 250, 200)
pen.setStyle(Qt.CustomDashLine)
pen.setDashPattern([1, 4, 5, 4])
painter.setPen(pen)
painter.drawLine(20, 240, 250, 240)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = PenDemo()
win.show()
sys.exit(app.exec_())
解析
- 創建一個 QPen 對象,設置顏色和線條樣式(黑色 2px 實線)
pen = QPen(Qt.black, 2, Qt.SolidLine)
- 自定義一條線條的樣式,使用的數字列表定義樣式
數字列表的個數必須是偶數個,[1,4,5,4]代表的是 1px 的橫線寬度,4px 的間隔,5px 的線條寬度,4px 的間隔—–也就是說這樣自定義的樣式就是 1px 和 5px 的短線以 4px 的間隔為空余交替出現
pen.setStyle(Qt.CustomDashLine)
pen.setDashPattern([1, 4, 5, 4])
painter.setPen(pen)
painter.drawLine(20, 240, 250, 240)
4.10.3 QBrush
- QBrush 畫刷是一個基本的圖形對象,用于填充如矩形,橢圓或者多邊形等的形狀
- QBrush 有三種基本類型
- 預定義
- 過渡
- 紋理圖案
案例 4-28 QBrush 畫刷的使用
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class BrushDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-28 QBrush畫刷的使用")
self.setGeometry(300, 300, 365, 280)
def paintEvent(self, event):
painter = QPainter()
painter.begin(self)
self.drawLines(painter)
painter.end()
def drawLines(self, painter):
brush = QBrush(Qt.SolidPattern)
painter.setBrush(brush)
painter.drawRect(10, 15, 90, 60)
brush = QBrush(Qt.Dense1Pattern)
painter.setBrush(brush)
painter.drawRect(130, 15, 90, 60)
brush = QBrush(Qt.Dense2Pattern)
painter.setBrush(brush)
painter.drawRect(250, 15, 90, 60)
brush = QBrush(Qt.Dense3Pattern)
painter.setBrush(brush)
painter.drawRect(10, 105, 90, 60)
brush = QBrush(Qt.DiagCrossPattern)
painter.setBrush(brush)
painter.drawRect(10, 105, 90, 60)
brush = QBrush(Qt.Dense5Pattern)
painter.setBrush(brush)
painter.drawRect(130, 105, 90, 60)
brush = QBrush(Qt.Dense6Pattern)
painter.setBrush(brush)
painter.drawRect(250, 105, 90, 60)
brush = QBrush(Qt.HorPattern)
painter.setBrush(brush)
painter.drawRect(10, 195, 90, 60)
brush = QBrush(Qt.VerPattern)
painter.setBrush(brush)
painter.drawRect(130, 195, 90, 60)
brush = QBrush(Qt.BDiagPattern)
painter.setBrush(brush)
painter.drawRect(250, 195, 90, 60)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = BrushDemo()
win.show()
sys.exit(app.exec_())
解析
- 使用了 9 種不同的背景填充矩形,定義 QBrush 對象,通過 QPainter 對象的畫刷設置,最后調用 drawRect()方法繪制矩形
brush = QBrush(Qt.SolidPattern)
painter.setBrush(brush)
painter.drawRect(10, 15, 90, 60)
4.10.4 QPixmap 圖片像素映射
- QPixmap 類常用于繪圖設備的圖像顯示,它可以作為對象加載到控件上,通常是標簽或者按鈕,用于在標簽或者按鈕上顯示圖像
- QPixmap 可以讀取的圖像文件的類型有 BMP/JPG/GIF/JPEG/PNG/等
- 常用方法
- copy() 從 QRect 對象復制到 QPixmap 對象
- fromImage() 將 QImage 對象轉換為 QPixmap 對象
- toImage() 將 QPixmap 對象轉換為 QImage 對象
- grabWidget() 從給定的窗口小部件創建一個像素圖
- grabWindow() 在窗口中創建數據的像素圖
- load() 加載圖片文件為 QPixmap 對象
- save() 將 QPixmap 對象保存為文件
案例 4-29 QPixmap 像素映射的使用
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class PixmapDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-29 QPixmap像素映射的使用")
self.lb1 = QLabel()
self.lb1.setPixmap(QPixmap("./images/python.jpg"))
layout = QVBoxLayout()
layout.addWidget(self.lb1)
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = PixmapDemo()
win.show()
sys.exit(app.exec_())
解析
- 在這個例子中,使用 setPixmap()將圖像顯示在 QLabel 上
self.lb1 = QLabel()
self.lb1.setPixmap(QPixmap("./images/python.jpg"))
4.11 拖拽與剪切板
4.11.1 Drag 與 Drop
- 基于 MIME 類型的拖拽數據傳輸就是基于 QDrag 類的
- QMimeData 對象將關聯的數據與其對應的 MIME 類型相互關聯
- MIME 類型的數據可以簡單理解為互聯網上的各種資源,比如文本、音頻、和視訊等資源,互聯網上的每一種資源都屬于一種 MIME 類型的數據
QMimeData 類函數
- hasText() text() text/plain
- hasHtml() html() text/html
- hasUrls() urls() text/uri-list
- hasImage() imageData() image/*
- hasColor() colorData() application/x-color
- 許多 QWidget 對象都支持拖拽動作,允許拖拽數據的控件必須設置 QWidget.setDragEnabled()為 True
常用的拖拽事件 - DragEnterEvent() 當執行一個拖拽控件操作時,并且滑鼠指針進入改控件時,這個事件將被觸發
_注意點_在這個事件中可以獲取被操作的窗口控件,還可以有條件拒絕或者接收該控件的拖拽操作 - DragMoveEvent() 當拖拽操作進行時會觸發該事件
- DragLeaveEvent() 當執行一個拖拽控件操作時,并且滑鼠指針離開時觸發
- DropEvent() 當拖拽操作在目標控件上被釋放時,這個事件被觸發
案例 4-30 拖拽功能的實現
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class Combo(QComboBox):
def __init__(self, title, parent):
super().__init__()
self.setAcceptDrops(True)
def dragEnterEvent(self, event):
print(event)
# 判斷拖拽的數據是不是text類型的
# 有條件拒絕或者接收該控件的拖拽操作
if event.mimeData().hasText():
event.accept()
else:
event.ignore()
# 釋放拖拽操作事件被觸發的前提一定是被操作控件通過檢測后允許當前的拖拽行為
# event.accept()這句代碼不執行也就不會有釋放控件事件的觸發
def dropEvent(self, event):
print(event)
self.addItem(event.mimeData().text())
class DragDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-30 拖拽功能的實現")
layout = QFormLayout()
edt1 = QLineEdit()
edt1.setDragEnabled(True)
com = Combo("Button", self)
layout.addRow(edt1, com)
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
drag = DragDemo()
drag.show()
sys.exit(app.exec_())
解析
- DragEnterEvent 會驗證事件的 MIME 數據的是否包含字符串文本,如果包含,就接收該事件
- dropEvent 會在接收該事件后響應釋放拖拽的操作,并將拖拽的數據文本作為下拉列表框的 item
def dragEnterEvent(self, event):
print(event)
if event.mimeData().hasText():
event.accept()
else:
event.ignore()
def dropEvent(self, event):
print(event)
self.addItem(event.mimeData().text())
4.11.2 QClipboard
- QClipboard 類提供了對系統剪切板的訪問,可以在應用程式之間復制粘貼,類似 QDrag 類
- QApplication 類有一個靜態方法 clipboard(),它返回剪切板對象
- 任何類型的 MimeData 數據都可以從剪切板上復制以及粘貼
常用的方法 - clear() 清除剪切板的內容
- setImage() 將 QImage 對象復制到剪切板上
- setMimeData() 將 Mime 數據設置到剪切板
- setPixmap() 將 QPixmap 對象復制到剪切板上
- setText() 將 text 文本復制到剪切板上
- text() 獲取剪切板上的文本數據 text
………
QClipboard 常用的信號 - dataChanged 當剪切板內容發生改變時,這個信號就會被發射
案例 4-31 QClipboard 剪切板的使用
import sys, os
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class clipboardDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-31 QClipboard剪切板的使用")
layout = QGridLayout()
textCopyButton = QPushButton("&Copy Text")
textPasteButton = QPushButton("Paste &Text")
htmlCopyButton = QPushButton("C&opy Html")
htmlPasteButton = QPushButton("Paste &Html")
imageCopyButton = QPushButton("Co&py Image")
imagePasteButton = QPushButton("Paste &Image")
self.textLabel = QLabel("Original text")
self.imageLabel = QLabel()
self.imageLabel.setPixmap(QPixmap(os.path.join(os.path.dirname(__file__), 'images/new.png')))
layout.addWidget(textCopyButton, 0, 0)
layout.addWidget(imageCopyButton, 0, 1)
layout.addWidget(htmlCopyButton, 0, 2)
layout.addWidget(textPasteButton, 1, 0)
layout.addWidget(imagePasteButton, 1, 1)
layout.addWidget(htmlPasteButton, 1, 2)
layout.addWidget(self.textLabel, 2, 0, 1, 2)
layout.addWidget(self.imageLabel, 2, 2)
self.setLayout(layout)
textCopyButton.clicked.connect(self.copyText)
textPasteButton.clicked.connect(self.pasteText)
imageCopyButton.clicked.connect(self.copyImage)
imagePasteButton.clicked.connect(self.pasteImage)
htmlCopyButton.clicked.connect(self.copyHtml)
htmlPasteButton.clicked.connect(self.pasteHtml)
def copyText(self):
# 獲取剪切板對象
clipboard = QApplication.clipboard()
clipboard.setText("我的是被復制的文本text")
def pasteText(self):
# 獲取剪切板對象
clipboard = QApplication.clipboard()
self.textLabel.setText(clipboard.text())
def copyImage(self):
# 獲取剪切板對象
clipboard = QApplication.clipboard()
clipboard.setPixmap(QPixmap(os.path.join(os.path.dirname(__file__), 'images/python.png')))
def pasteImage(self):
# 獲取剪切板對象
clipboard = QApplication.clipboard()
self.imageLabel.setPixmap(clipboard.pixmap())
def copyHtml(self):
# 獲取剪切板對象
clipboard = QApplication.clipboard()
mimaData = QMimeData()
mimaData.setHtml("<b>wbj520<font color=red>mumu</font></b>")
clipboard.setMimeData(mimaData)
def pasteHtml(self):
# 獲取剪切板對象
clipboard = QApplication.clipboard()
mimeData = clipboard.mimeData()
if mimeData.hasHtml():
self.textLabel.setText(mimeData.html())
if __name__ == '__main__':
app = QApplication(sys.argv)
win = clipboardDemo()
win.show()
sys.exit(app.exec_())
解析
- 首先需要獲取到 clipboard 對象
clipboard = QApplication.clipboard()
clipboard.setText("我的是被復制的文本text")
- 剪切板操作常用方法中沒有操作 html 的,但是上一節我們了解到所有的資源數據都對應著 MIME 數據類型,所以可以使用 MimeData 數據
def copyHtml(self):
# 獲取剪切板對象
clipboard = QApplication.clipboard()
mimaData = QMimeData()
mimaData.setHtml("<b>wbj520<font color=red>mumu</font></b>")
clipboard.setMimeData(mimaData)
def pasteHtml(self):
# 獲取剪切板對象
clipboard = QApplication.clipboard()
mimeData = clipboard.mimeData()
if mimeData.hasHtml():
self.textLabel.setText(mimeData.html())
4.12 日歷與時間
4.12.1 QCalendar
- QCalendar 是一個日歷控件,是一個基于月份的視圖,允許用戶通過滑鼠或者鍵盤來選擇日期,默認是選中今天的日期,也可以規定日期的范圍
- 常用的方法
- setDataRange() 設置日期返回以供選擇
- setFirstDayOfWeek() 設置一周的第一天,默認是周日
- Qt.Monday 周一
…..以此類推
- Qt.Monday 周一
- setMinimumDate() 設置最小日期
- setMaximumDate() 設置最大日期
- setSelectedDate() 設置選中的日期,默認是今天
- maximumDate 獲取日歷控件的最大日期
- minimumDate 獲取日歷控件的最小日期
- selectedDate 獲取當前選中的日期
- setGridvisible() 設置日歷控件是否顯示網格(網格可視化)
案例 4-32 QCalendar 日歷的使用
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class calendarDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-32 QCalendar日歷的使用")
self.initUI()
def initUI(self):
self.cal = QCalendarWidget(self)
self.cal.setMinimumDate(QDate(1970, 1, 1))
self.cal.setMaximumDate(QDate(3000, 1, 1))
self.cal.setGridVisible(True)
self.move(20, 20)
# 表示日歷控件中的日期被點擊 [取屬性對象]
self.cal.clicked[QDate].connect(self.showDate)
self.lb1 = QLabel(self)
date = self.cal.selectedDate()
# 將日期數據轉換為字符串并且進行格式化操作
self.lb1.setText(date.toString("yyyy-MM-dd dddd"))
self.lb1.move(20, 250)
self.setGeometry(100, 100, 400, 300)
def showDate(self, date):
print(date)
print(date.toString("yyyy-MM-dd dddd"))
self.lb1.setText(date.toString("yyyy-MM-dd dddd"))
解析
- 創建日歷組件,并設置最大最小日期
self.cal = QCalendarWidget(self)
self.cal.setMinimumDate(QDate(1970, 1, 1))
self.cal.setMaximumDate(QDate(3000, 1, 1))
self.cal.setGridVisible(True)
- 從窗口組件中選定一個日期,會發射一個 QDate 點擊的信號,捕捉到后連接到自定義的槽函數上
# 表示日歷控件中的日期被點擊 [取屬性對象]
self.cal.clicked[QDate].connect(self.showDate)
- 通過調用 selectedDate 方法檢索所選定的日期,然后將日期對象轉換為指定格式的字符串并顯示在標簽上
def showDate(self, date):
print(date)
print(date.toString("yyyy-MM-dd dddd"))
self.lb1.setText(date.toString("yyyy-MM-dd dddd"))
4.12.2 QDateTimeEdit
-
QDateTimeEdit 是一個允許用戶編輯日期時間的控件,可以使用鍵盤上下箭頭按鈕增加或者減少日期時間值
-
QDateTimeEdit 通過 setDisplayFormat 來設置顯示的日期時間格式
-
常用的方法
- setDisplayFormat() 設置日期時間顯示格式
- yyyy 代表年份,用 4 位數表示
- MM 代表月份,取值是 01-12
- dd 代表日,取值是 01-31
- HH 代表小時,取值是 00-23
- mm 代表分鐘,取值是 00-59
- ss 代表秒,取值是 00-59
- setMinimumDate 設置控件的最小日期
- setMaximumDate 設置控件的最大日期
- time() 獲取編輯的時間
- date() 獲取編輯的日期
- setDisplayFormat() 設置日期時間顯示格式
-
常用的信號
- dateChanged 當日期改變時發射該信號
- dateTimeCahnged 當日期時間改變時發射該信號
- timeCahnged 當時間改變時發射該信號
-
QDateTimeEdit 的子類
- QDateEdit 和 QTimeEdit 類均繼承自 QDateTimeEdit 類
- 繼承關系是 QWidget—QAbstractSpinBox—QDateTimeEdit—(QDateEdit,QTimeEdit)
- 日期操作是 QDateEdit,時間操作是 QTimeEdit,日期時間操作是 QDateTimeEdit
dateEdit = QDateEdit(self) timeEdie = QTimeEdit(self) dateEdit.setDisplayFormat("yyyy-MM-dd") timeEdie.setDisplayFormat("HH:mm:ss")- 設置彈出日歷時要注意,用來彈出日歷的類只能是 QDateTimeEdit 和 QDateEdit, QTimeEdit 在語法上是可以的,但是不起作用不會彈出來
dateTimeEdit = QDateTimeEdit(self) dateEdit = QDateEdit(self) dateTimeEdit.setCalendarPopup(True) dateEdit.setCalendarPopup(True) -
初始化 QDateTimeEdit 類
案例 QDateTimeEdit 的簡單使用
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class DateTimeEditDemo1(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("案例 QDateTimeEdit的簡單使用")
layout = QVBoxLayout()
dateTimeEdit = QDateTimeEdit()
dateTimeEdit2 = QDateTimeEdit(QDateTime.currentDateTime())
# 設置日期時間的顯示格式
dateTimeEdit2.setDisplayFormat("yyyy-MM-dd HH:mm:ss")
# 設置最小日期
dateTimeEdit2.setMinimumDate(QDate.currentDate().addDays(-365))
# 設置最大日期
dateTimeEdit2.setMaximumDate(QDate.currentDate().addDays(365))
# 設置彈出日歷控件
dateTimeEdit2.setCalendarPopup(True)
dateEdit = QDateEdit(QDate.currentDate())
timeEdit = QTimeEdit(QTime.currentTime())
layout.addWidget(dateTimeEdit)
layout.addWidget(dateTimeEdit2)
layout.addWidget(dateEdit)
layout.addWidget(timeEdit)
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = DateTimeEditDemo1()
win.show()
sys.exit(app.exec_())
解析
- 設置日期時間的顯示格式
dateTimeEdit2.setDisplayFormat("yyyy-MM-dd HH:mm:ss")
- 設置日期時間的范圍,限制可選日期范圍為距離今天的前后 365 天
# 設置最小日期
dateTimeEdit2.setMinimumDate(QDate.currentDate().addDays(-365))
# 設置最大日期
dateTimeEdit2.setMaximumDate(QDate.currentDate().addDays(365))
- 彈出日歷控件,默認是通過計數器的上下箭頭改變數據的,現在可以彈出日歷控件,只需要調用下面的一行代碼
# 設置彈出日歷控件
dateTimeEdit2.setCalendarPopup(True)
總結
- 可以通過 date()/dateTime()等方法來獲取日期時間對象
- 可以調用 QDate 的 year()/month()/day()等函數來獲取詳細的年月日
案例 4-33 QDateTimeEdit 的使用
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class DateTimeDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-33 QDateTimeEdit的使用")
self.initUI()
def initUI(self):
self.resize(300, 90)
layout = QVBoxLayout()
self.dateEdit = QDateTimeEdit(QDateTime.currentDateTime(), self)
self.dateEdit.setDisplayFormat("yyyy-MM-dd HH:mm:ss")
# 設置最小日期
self.dateEdit.setMinimumDate(QDate.currentDate().addDays(-365))
# 設置最大日期
self.dateEdit.setMaximumDate(QDate.currentDate().addDays(365))
self.dateEdit.setCalendarPopup(True)
self.dateEdit.dateChanged.connect(self.onDateChanged)
self.dateEdit.dateTimeChanged.connect(self.ondateTimeChanged)
self.dateEdit.timeChanged.connect(self.onTimeChanged)
self.btn = QPushButton("獲取日期和時間")
self.btn.clicked.connect(self.onButtonClicked)
layout.addWidget(self.dateEdit)
layout.addWidget(self.btn)
self.setLayout(layout)
# 日期發生改變時
def onDateChanged(self, date):
print(date)
# 日期時間發生改變時
def ondateTimeChanged(self, dateTime):
print(dateTime)
# 時間發生改變時
def onTimeChanged(self, time):
print(time)
# 按鈕被點擊時
def onButtonClicked(self):
dateTime = self.dateEdit.dateTime()
maxDate = self.dateEdit.maximumDate()
minDate = self.dateEdit.minimumDate()
maxDateTime = self.dateEdit.maximumDateTime()
minDateTime = self.dateEdit.minimumDateTime()
maxTime = self.dateEdit.maximumTime()
minTime = self.dateEdit.minimumTime()
print('\n選擇日期時間')
print('dateTime=%s' % dateTime.toString())
print('maxDate=%s' % maxDate.toString())
print('minDate=%s' % minDate.toString())
print('maxDateTime=%s' % maxDateTime.toString())
print('minDateTime=%s' % minDateTime.toString())
print('maxTime=%s' % maxTime.toString())
print('minTime=%s' % minTime.toString())
if __name__ == '__main__':
app = QApplication(sys.argv)
win = DateTimeDemo()
win.show()
sys.exit(app.exec_())
4.13 菜單欄、工具欄與狀態欄
4.13.1 菜單欄
- 在 QMainWindow 對象的標題欄下方,水平的 QMenuBar 被保留顯示 QMenu 對象
- QMenu 類提供了一個可以添加到菜單欄的小控件,也用于創建上下文菜單和彈出子菜單
- 每一個 QMenu 對象都可以包含一個或者多個 QAction 對象或者級聯的 QMenu 對象
- 可以使用 createPopupMenu 函數來創建彈出子菜單
- menuBar 函數用于返回主窗口的 QMenuBar 對象
- 通過 addAction 函數可以在菜單中進行添加操作,addMenu 函數可以將菜單添加到菜單欄中
常用的方法 - menuBar() 返回主窗口的 QMenuBar 對象
- addMenu() 在菜單欄中添加一個新的菜單
- addAction() 在菜單中添加一個新的操作,包含文本或者圖標
- setEnabled() 設置菜單中的操作是否啟用
- addSeperator() 在菜單中添加一個分割線
- clear() 刪除菜單、菜單欄的內容
- setShortcut() 設置操作按鈕的快捷方式
- setText() 設置每個菜單項的文本
- setTitle() 設置 QMenu 小控件的標題
- text() 獲取每一個菜單項的文本
- title() 獲取 QMenu 小控件的標題
常用的信號 - triggered 單擊任何 QAction 按鈕時,相應的 QMenu 對象都會發射 triggered 信號
案例 4-34 QMenuBar 菜單欄的使用
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class MenuBarDemo(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-34 QMenuBar菜單欄的使用")
layout = QHBoxLayout()
bar = self.menuBar()
file = bar.addMenu("File")
file.addAction("New")
save = QAction("Save", self)
# 添加快捷方式
save.setShortcut("Ctrl+S")
file.addAction(save)
# 給file菜單添加級聯菜單
edit = file.addMenu("Edit")
edit.addAction("Copy")
edit.addAction("Paste")
quit = QAction("Quit", self)
file.addAction(quit)
file.triggered[QAction].connect(self.processTrigger)
self.setLayout(layout)
def processTrigger(self, sel):
print(sel.text()+" is triggered")
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MenuBarDemo()
win.show()
sys.exit(app.exec_())
解析
- 頂級窗口必須是 QMainWindow 對象,才可以引用 QMenuBar 對象
- 通過 addMenu 方法將 file 菜單添加到菜單欄上
bar = self.menuBar()
file = bar.addMenu("File")
- 菜單欄中的操作按鈕可以是字符串或者 QAction 對象
# 字符串文本
file.addAction("New")
# QAction對象
save = QAction("Save", self)
# 添加快捷方式
save.setShortcut("Ctrl+S")
file.addAction(save)
- 菜單發射 triggered 信號,將信號連接到槽函數,該信號是點擊 QAction 對象觸發的
file.triggered[QAction].connect(self.processTrigger)
4.13.2 QToolBar 工具欄
- QToolBar 控件是由文本控件、圖標或其他小控件按鈕組成的可移動面板,通常位于菜單欄下面
- 常用的方法
- addAction() 添加具有文本或者圖標的工具按鈕
- addSeperator() 添加分割線分組顯示工具按鈕
- addWidget() 添加工具欄中按鈕以外的控件
- addToolBar() 使用 QMainWindow 類的方法添加一個新的工具欄
- setMovable() 設置工具欄是否可移動
- setOrientation()設置工具欄的顯示方向
- Qt.Horizontal 水平顯示
- Qt.Vertical 垂直顯示
- 常用的信號
- actionTriggered 當點擊工具欄上的按鈕時,觸發該信號,并且參數傳遞該 QAction 對象的引用到槽函數
案例 4-35 QToolBar 工具欄的使用
- actionTriggered 當點擊工具欄上的按鈕時,觸發該信號,并且參數傳遞該 QAction 對象的引用到槽函數
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class ToolBarDemo(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-35 QToolBar工具欄的使用")
self.resize(300, 200)
layout = QVBoxLayout()
toolBar = self.addToolBar("File")
new = QAction(QIcon("./images/new.png"), "new", self)
toolBar.addAction(new)
open = QAction(QIcon("./images/open.png"), "open", self)
toolBar.addAction(open)
save = QAction(QIcon("./images/save.png"), "save", self)
toolBar.addAction(save)
toolBar.actionTriggered[QAction].connect(self.toolbtnPressed)
toolBar.setMovable(True)
self.setLayout(layout)
def toolbtnPressed(self, action):
print("pressed tool button is ", action.text())
if __name__ == '__main__':
app = QApplication(sys.argv)
win = ToolBarDemo()
win.show()
sys.exit(app.exec_())
解析
- 調用 QMainWindow 窗口創建一個新的工具欄
toolBar = self.addToolBar("File")
- 將具有圖形和文本名稱的 QAction 對象添加到工具欄中
open = QAction(QIcon("./images/open.png"), "open", self)
toolBar.addAction(open)
- 將 actionTriggered 的信號連接到槽函數 toolbtnPressed 上
toolBar.actionTriggered[QAction].connect(self.toolbtnPressed)
4.13.3 QStatusBar 狀態欄
- QMainWindow 對象在底部保留一個水平條,作為狀態欄(QStatusBar),用于永久或者臨時顯示狀態資訊
- 常用的方法
- addWidget() 在狀態欄中添加給定的窗口部件對象
- addPermanentWidget() 在狀態欄中添加永久的小部件對象
- showMessage() 在狀態欄顯示一條臨時資訊指定時間間隔
- clearMessage() 刪除狀態欄中顯示的資訊
- removeWidget() 從狀態欄中刪除指定的小控件
- setStatusBar() 設置一個狀態欄
案例 4-36 QStatusBar 狀態欄的使用
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class StatusBarDemo(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-36 QStatusBar狀態欄的使用")
menuBar = self.menuBar()
file = menuBar.addMenu("File")
file.addAction("show")
file.triggered[QAction].connect(self.actionPressed)
self.setCentralWidget(QTextEdit())
self.statusBar = QStatusBar()
self.setStatusBar(self.statusBar)
def actionPressed(self, action):
if action.text() == "show":
# 默認顯示時間為0也就是永久顯示 此處設置顯示5s 之后消失
self.statusBar.showMessage(action.text()+"菜單欄選項被點擊了", 5000)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = StatusBarDemo()
win.show()
sys.exit(app.exec_())
解析
- 在這個例子中頂層窗口 MainWindow 有一個菜單欄和一個 QTextEdit 控件作為窗口的中心部件
- 當單擊 MenuBar 中的菜單項 show 時就會觸發 triggered 信號,并且連接槽函數 actionPressed,將點擊的菜單項資訊顯示在狀態欄中,持續 5s
file.triggered[QAction].connect(self.actionPressed)
def actionPressed(self, action):
if action.text() == "show":
# 默認顯示時間為0也就是永久顯示 此處設置顯示5s 之后消失
self.statusBar.showMessage(action.text()+"菜單欄選項被點擊了", 5000)
4.14 QPrinter
- 列印圖像是圖像處理軟體中的一個常用的功能,列印圖像實際上是在 QPaintDevice 中畫圖
- 列印時使用的是 QPrinter 對象,本質跟之前的 QPixmap 差不多,也是一種 PaintDevice(繪圖設備)
案例 4-37 QPrinter 列印圖像的使用
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtPrintSupport import *
class printerDemo(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("案例4-37 QPrinter列印圖像的使用")
self.ImageLabel = QLabel()
self.ImageLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
self.setCentralWidget(self.ImageLabel)
self.image = QImage()
self.createActions()
self.createMenus()
self.createToolBars()
if self.image.load("./images/screen.png"):
# image對象轉換為pixmap對象
self.ImageLabel.setPixmap(QPixmap.fromImage(self.image))
self.resize(self.image.width(), self.image.height())
def createActions(self):
# self.tr()表示的是多語言國際化
self.PrintAction = QAction(QIcon("./images/printer.png"), self.tr("列印"), self)
self.PrintAction.setShortcut("Ctrl+P")
self.PrintAction.triggered.connect(self.slotPrint)
def createMenus(self):
PrintMemu = self.menuBar().addMenu(self.tr("列印"))
PrintMemu.addAction(self.PrintAction)
def createToolBars(self):
fileToolBar = self.addToolBar("Print")
fileToolBar.addAction(self.PrintAction)
def slotPrint(self):
printer = QPrinter()
printerDialog = QPrintDialog(printer, self)
if printerDialog.exec_():
painter = QPainter(printer)
# 得到繪圖的視圖大小
rect = painter.viewport()
size = self.image.size()
# 設置縮放比例為視圖大小,并且保持寬高比
size.scale(rect.size(), Qt.KeepAspectRatio)
painter.setViewport((rect.x(), rect.y(), size.width(), size.height()))
painter.setWindow(self.image.rect())
painter.drawImage(0, 0, self.image)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = printerDemo()
win.show()
sys.exit(app.exec_())
解析
- 創建了 QAction 對象給菜單欄和工具欄共用
def createActions(self):
# self.tr()表示的是多語言國際化
self.PrintAction = QAction(QIcon("./images/printer.png"), self.tr("列印"), self)
self.PrintAction.setShortcut("Ctrl+P")
self.PrintAction.triggered.connect(self.slotPrint)
def createMenus(self):
PrintMemu = self.menuBar().addMenu(self.tr("列印"))
PrintMemu.addAction(self.PrintAction)
def createToolBars(self):
fileToolBar = self.addToolBar("Print")
fileToolBar.addAction(self.PrintAction)
- 多語言國際化編寫字符串
self.tr("列印")
- 縮放時設置寬高大小時,還要設置寬高比的模式,此處是保持寬高比
size.scale(rect.size(), Qt.KeepAspectRatio)