Python 和 PyQt: 構建 GUI 桌面計算器
儘管 web 和移動應用程序似乎已經佔領了軟件開發市場,但傳統的圖形用戶界面 (GUI) 桌面應用程序仍然有需求。如果您對用 Python 構建這些類型的應用程序感興趣,那麼您將發現有各種各樣的庫可供選擇。它們包括 Tkinter、wxPython、PyQt、PySide 和其他一些。
在本教程中,您將學習使用 Python 和 PyQt 構建 GUI 桌面應用程序的基礎知識。
在本教程中,您將學習如何:
-
使用 Python 和 PyQt 創建圖形用戶界面
-
將應用程序的 GUI 上的用戶事件與應用程序的邏輯連接起來
-
使用適當的項目佈局組織 PyQt 應用程序
-
用 PyQt 創建一個功能齊全的 GUI 應用程序
在本教程中,您將使用 Python 和 PyQt 創建一個計算器應用程序。這個簡短的項目將幫助您掌握基礎知識,並讓您開始使用這個 GUI 庫。
瞭解 PyQt
PyQt 是針對 Qt 的 Python 綁定,Qt 是一組 c++ 庫和開發工具,爲圖形用戶界面 (gui) 提供獨立於平臺的抽象。Qt 還提供了用於網絡、線程、正則表達式、SQL 數據庫、SVG、OpenGL、XML 和許多其他強大特性的工具。
由 RiverBank Computing Ltd 開發的 PyQt 的最新版本是:
-
PyQt5: 一個只基於 Qt5.x 構建的版本。
-
PyQt6: 一個只基於 Qt6.x 構建的版本。
在本教程中,您將使用 PyQt6,因爲這個版本是該庫的未來。從現在開始,一定要將任何提及 PyQt 的內容視爲對 PyQt6 的引用。
注意: 如果您想更深入地瞭解庫的這兩個版本之間的區別,請查看關於該主題的 PyQt6 文檔。
PyQt6 基於 Qt v6。因此,它爲 GUI 創建、XML 處理、網絡通信、正則表達式、線程、SQL 數據庫、網頁瀏覽和 Qt 中可用的其他技術提供了類和工具。PyQt6 在一組 Python 模塊中實現了許多 Qt 類的綁定,這些模塊被組織在一個名爲 PyQt6 的頂級 Python 包中。要使 PyQt6 工作,您需要 Python 3.6.1 或更高版本。
PyQt6 兼容 Windows、Unix、Linux、macOS、iOS 和 Android。如果您正在尋找一個 GUI 框架來開發在每個平臺上都具有本機外觀的多平臺應用程序,那麼這是一個有吸引力的特性。
PyQt6 在兩種許可下可用:
-
The Riverbank Commercial License
-
The General Public License (GPL), version 3
您的 PyQt6 許可證必須與您的 Qt 許可證兼容。如果您使用 GPL 許可,那麼您的代碼也必須使用與 GPL 兼容的許可。如果您想使用 PyQt6 創建商業應用程序,那麼您的安裝需要一個商業許可證。
注意: Qt 公司已經爲 Qt 庫開發並維護了自己的 Python 綁定。這個 Python 庫被稱爲 Qt for Python,是官方的 Qt for Python。它的 Python 包稱爲 PySide。
PyQt 和 PySide 都建立在 Qt 之上,它們的 API 非常相似,因爲它們反映了 Qt 的 API。這就是爲什麼將 PyQt 代碼移植到 PySide 可以像更新一些導入一樣簡單。如果你學會了其中一種,那麼你就可以用最少的努力來學習另一種。如果您想更深入地瞭解這兩個庫之間的區別,那麼可以查看 PyQt6 vs PySide6。
如果您需要更多關於 PyQt6 許可的信息,請查看項目官方文檔中的許可 FAQs page。
安裝 PyQt
在系統或開發環境上安裝 PyQt 有幾種選擇。推薦的選項是使用二元輪。輪子是從 Python 包索引 PyPI 安裝 Python 包的標準方法。
在任何情況下,您都需要考慮 PyQt6 的輪只適用於 Python 3.6.1 及以後版本。有 Linux、macOS 和 Windows(64 位) 的輪子。
所有這些輪子都包含相應 Qt 庫的副本,因此不需要單獨安裝它們。
另一個安裝選項是從源代碼構建 PyQt。這可能有點複雜,所以如果可能的話,您可能想要避免它。如果您確實需要從源代碼進行構建,那麼請查看庫文檔在這些情況下的建議。
另外,您可以選擇使用包管理器來安裝 PyQt6,例如 Linux 上的 APT 或 macOS 上的 Homebrew。在接下來的幾節中,您將瞭解從不同來源和不同平臺上安裝 PyQt6 的一些選項。
使用 pip 安裝虛擬環境
大多數時候,您應該創建一個 Python 虛擬環境,以一種隔離的方式安裝 PyQt6。要創建一個虛擬環境並在其中安裝 PyQt6,在命令行上運行以下命令:
Linux 和 macOS
$ python -m venv venv
$ source venv/bin/activate
(venv) $ python -m pip install pyqt6
Windows
PS> python -m venv venv
PS> venv\Scripts\activate
(venv) PS> python -m pip install pyqt6
這裏,您首先使用標準庫中的 venv 模塊創建一個虛擬環境。然後激活它,最後使用 pip 在其中安裝 PyQt6。注意,安裝命令必須安裝 Python 3.6.1 或更高版本才能正常工作。
使用 pip 進行系統級安裝
很少需要直接在系統 Python 環境中安裝 PyQt。如果您需要進行這種安裝,那麼在您的命令行或終端窗口中運行以下命令,而不激活任何虛擬環境:
python -m pip install pyqt6
使用此命令,您將直接在系統 Python 環境中安裝 PyQt6。您可以在安裝完成後立即開始使用庫。根據您的操作系統,您可能需要 root 或管理員權限才能運行此安裝。
儘管這是安裝 PyQt6 並立即開始使用它的一種快速方法,但並不推薦使用這種方法。推薦的方法是使用 Python 虛擬環境,正如您在上一節中所學的那樣。
特定於平臺的安裝
一些 Linux 發行版在它們的存儲庫中包含 PyQt6 的二進制包。如果是這種情況,那麼可以使用發行版的包管理器安裝庫。例如,在 Ubuntu 上,您可以使用以下命令:
$ sudo apt install python3-pyqt6
使用此命令,您將在基本系統中安裝 PyQt6 及其所有依賴項,因此您可以在任何 GUI 項目中使用該庫。注意,這裏需要根權限,您可以在這裏使用 sudo 命令調用根權限。
如果您是 macOS 用戶,那麼可以使用 Homebrew 包管理器安裝 PyQt6。要做到這一點,打開一個終端並運行以下命令:
$ brew install pyqt6
運行此命令後,您將在您的 Homebrew Python 環境中安裝 PyQt6,它就可以供您使用了。
如果在 Linux 或 macOS 上使用包管理器,則有可能得不到最新版本的 PyQt6。如果您想確保您擁有最新的版本,那麼使用 pip 安裝會更好。
創建您的第一個 PyQt 應用程序
現在您已經有了一個可以工作的 PyQt 安裝,可以創建您的第一個 GUI 應用程序了。使用 Python 和 PyQt 應用程序。以下是你需要遵循的步驟:
-
從 PyQt6.QtWidgets 導入 QApplication 和所有必需的小部件。
-
創建一個 QApplication 實例。
-
創建應用程序的 GUI。
-
顯示應用程序的 GUI。
-
運行應用程序的事件循環或主循環。
首先,在當前工作目錄中創建一個名爲 hello.py 的新文件:
# hello.py
"""Simple Hello, World example with PyQt6."""
import sys
# 1. Import QApplication and all the required widgets
from PyQt6.QtWidgets import QApplication, QLabel, QWidget
首先,導入 sys,它將允許您通過 exit() 函數處理應用程序的終止和退出狀態。然後從 QtWidgets 導入 QApplication、QLabel 和 QWidget, QWidget 是 PyQt6 包的一部分。有了這些導入,第一步就完成了。
要完成第二步,您只需要創建 QApplication 的一個實例。就像創建任何 Python 類的實例一樣:
# hello.py
# ...
# 2. Create an instance of QApplication
app = QApplication([])
在這行代碼中,您將創建 QApplication 的實例。在 PyQt 中創建任何 GUI 對象之前,應該先創建應用程序實例。
在內部,QApplication 類處理命令行參數。這就是爲什麼需要將命令行參數列表傳遞給類構造函數。在本例中,您使用空列表,因爲您的應用程序將不處理任何命令行參數。
注意: 您經常會發現開發人員傳遞 sys。argv 到 QApplication 的構造函數。該對象包含傳入 Python 腳本的命令行參數列表。如果應用程序需要接受命令行參數,那麼應該使用 sys。Argv 來處理他們。否則,您可以只使用一個空列表,就像在上面的例子中所做的那樣。
第三步涉及創建應用程序的 GUI。在本例中,您的 GUI 將基於 QWidget 類,它是 PyQt 中所有用戶界面對象的基類。
下面是如何創建應用程序的 GUI:
# hello.py
# ...# 3. Create your application's GUIwindow = QWidget()
window.setWindowTitle("PyQt App")
window.setGeometry(100, 100, 280, 80)
helloMsg = QLabel("<h1>Hello, World!</h1>", parent=window)
helloMsg.move(60, 15)
在這段代碼中,window 是 QWidget 的一個實例,它提供了創建應用程序窗口或表單所需的所有特性。顧名思義,. setwindowtitle() 在應用程序中設置窗口的標題。在本例中,應用程序的窗口將顯示 PyQt app 作爲標題。
注意: 更準確地說,這一步需要你創建應用程序的頂層或主窗口。術語應用程序的 GUI 有點通用。通常,應用程序的 GUI 由多個窗口組成。
您可以使用. setgeometry() 來定義窗口的大小和屏幕位置。前兩個參數是將放置窗口的 x 和 y 屏幕座標。第三個和第四個參數是窗口的寬度和高度。
每個 GUI 應用程序都需要小部件或圖形化組件來製作應用程序的 GUI。在本例中,您使用 QLabel 小部件 helloMsg 來顯示消息 Hello, World! 在您的應用程序窗口上。
QLabel 對象可以顯示 HTML 格式的文本,因此可以使用 HTML 元素 “
Hello, World!”
”提供所需的文本作爲 h1 標題。最後,使用. move()將 helloMsg 放置在應用程序窗口的座標 (60,15) 處。
注意: 在 PyQt 中,您可以使用任何小部件—qwidget 的子類—作爲頂級窗口。唯一的條件是目標小部件不能有父小部件。當您使用小部件作爲頂層窗口時,PyQt 會自動爲它提供一個標題欄,並將其轉換爲普通窗口。
小部件之間的父子關係有兩個互補的目的。沒有父窗口的小部件被認爲是主窗口或頂層窗口。相比之下,帶有顯式父部件的小部件是子部件,它顯示在其父部件中。
這種關係也被稱爲所有權,父母擁有他們的孩子。PyQt 所有權模型確保如果您刪除一個父小部件,例如頂級窗口,那麼它的所有子小部件也將自動刪除。
爲了避免內存泄漏,您應該始終確保任何 QWidget 對象都有一個父對象 (頂級窗口除外)。
你已經完成了第三步,所以你可以繼續最後兩個步驟,讓你的 PyQt GUI 應用程序準備好運行:
# hello.py
# ...
# 4. Show your application's GUI
window.show()
# 5. Run your application's event loop
sys.exit(app.exec())
在此代碼片段中,在 window 上調用. show()。對. show() 的調用調度一個繪製事件,這是一個繪製組成 GUI 的小部件的請求。然後將此事件添加到應用程序的事件隊列中。您將在後面的部分了解更多關於 PyQt 事件循環的內容。
最後,通過調用. exec() 啓動應用程序的事件循環。對. exec() 的調用被封裝在對 sys.exit() 的調用中,這允許您在應用程序終止時乾淨地退出 Python 並釋放內存資源。
你可以用以下命令運行你的第一個 PyQt 應用程序:
$ python hello.py
當你運行這個腳本時,你會看到一個類似這樣的窗口:
您的應用程序顯示一個基於 QWidget 的窗口。窗口顯示 Hello, World! 消息。爲了顯示消息,它使用了一個 QLabel 小部件。至此,您已經使用 PyQt 和 Python 編寫了您的第一個 GUI 桌面應用程序! 這不是很酷嗎?
考慮到代碼風格
如果您檢查前一節中示例 GUI 應用程序的代碼,那麼您將注意到 PyQt 的 API 不遵循 PEP 8 編碼風格和命名約定。PyQt 是圍繞 Qt 構建的,Qt 是用 c++ 編寫的,對函數、方法和變量使用駝峯式命名風格。也就是說,在開始編寫 PyQt 項目時,需要決定使用哪種命名風格。
在這方面,PEP 8 指出:
新的模塊和包 (包括第三方框架) 應該按照這些標準編寫,但是如果現有的庫具有不同的風格,則內部一致性是首選。(Source)
如果您想要編寫一致的 pyqt 相關代碼,那麼您應該堅持框架的編碼風格。在本教程中,爲了保持一致性,您將遵循 PyQt 編碼風格。您將使用駝峯情況而不是通常的 Python snake case.。
學習 PyQt 的基礎知識
如果您想熟練地使用這個庫開發 GUI 應用程序,則需要掌握 PyQt 的基本組件。其中一些組成部分包括:
-
小部件
-
佈局管理器
-
對話框
-
主窗口
-
應用程序
-
事件循環
-
信號與插槽
這些元素是任何 PyQt GUI 應用程序的構建塊。它們中的大多數都表示爲 PyQt6 中的 Python 類。QtWidgets 模塊。這些因素非常重要。您將在以下幾個部分了解更多關於它們的內容。
小部件
小部件是矩形圖形化組件,您可以將它們放在應用程序的窗口中以構建 GUI。小部件有幾個屬性和方法,允許您調整它們的外觀和行爲。他們還可以在屏幕上畫出自己的形象。
小部件還檢測來自用戶、窗口系統和其他來源的鼠標單擊、按鍵和其他事件。每當小部件捕獲一個事件時,它都會發出一個信號來宣佈其狀態更改。PyQt 擁有豐富而現代的小部件集合。每個小部件都有不同的用途。
一些最常見和最有用的 PyQt 小部件是:
-
按鈕
-
標籤
-
行編輯
-
組合框
-
單選按鈕
首先是按鈕。您可以通過實例化 QPushButton 來創建按鈕,這是一個提供經典命令按鈕的類。典型的按鈕有 “確定”、“取消”、“應用”、“是”、“否” 和“關閉”。下面是它們在 Linux 系統上的樣子:
像這樣的按鈕可能是任何 GUI 中最常用的小部件。當有人點擊它們時,你的應用程序就會命令計算機執行操作。這就是如何在用戶單擊按鈕時執行計算。
接下來是標籤,您可以使用 QLabel 創建標籤。標籤可以讓你以文本或圖像的形式顯示有用的信息:
你將使用這些標籤來解釋如何使用你的應用程序的 GUI。您可以通過幾種方式調整標籤的外觀。正如您前面看到的,標籤甚至可以接受 html 格式的文本。還可以使用標籤指定一個鍵盤快捷鍵,將光標焦點移動到 GUI 上的給定小部件上。
另一個常見的小部件是行編輯,也稱爲輸入框。這個小部件允許您輸入單行文本。可以使用 QLineEdit 類創建行編輯。當需要以純文本形式獲取用戶輸入時,行編輯非常有用。
下面是 Linux 系統上的行編輯:
像這樣的行編輯自動提供基本的編輯操作,如複製、粘貼、撤消、重做、拖放等。在上圖中,您還可以看到第一行上的對象顯示佔位符文本,以通知用戶需要什麼樣的輸入。
組合框是 GUI 應用程序中另一種基本類型的小部件。您可以通過實例化 QComboBox 來創建它們。組合框將以一種佔用最小屏幕空間的方式爲用戶提供選項的下拉列表。
下面是一個組合框的例子,它提供了流行編程語言的下拉列表:
這個組合框是隻讀的,這意味着用戶可以從多個選項中選擇一個,但不能添加自己的選項。組合框還可以編輯,允許用戶動態添加新選項。組合框還可以包含像素圖、字符串或兩者都包含。
您將瞭解的最後一個小部件是單選按鈕,您可以使用 QRadioButton 創建它。QRadioButton 對象是一個選項按鈕,您可以單擊它來打開它。當需要用戶從多個選項中選擇一個時,單選按鈕很有用。單選按鈕中的所有選項同時顯示在屏幕上:
在這個單選按鈕組中,在給定的時間內只能選中一個按鈕。如果用戶選擇另一個單選按鈕,那麼先前選擇的按鈕將自動關閉。
PyQt 有大量的小部件集合。在撰寫本文時,有超過 40 種可供您用於創建應用程序的 GUI。在這裏,你只研究了一小部分樣本。但是,這足以向您展示 PyQt 的強大功能和靈活性。在下一節中,您將學習如何佈局不同的小部件,爲您的應用程序構建現代且功能齊全的 GUI。
佈局管理器
既然您已經瞭解了小部件以及如何使用它們構建 GUI,那麼您還需要了解如何安排一組小部件,以使 GUI 既連貫又有功能。在 PyQt 中,您將發現一些在窗體或窗口中佈局小部件的技術。例如,您可以使用. resize() 和. move() 方法來給出小部件的絕對大小和位置。
然而,這種技術可能有一些缺點。你必須:
-
做很多手工計算來確定每個部件的正確大小和位置。
-
是否進行額外的計算以響應窗口大小調整事件。
-
當窗口的佈局以任何方式發生變化時,重新做大部分計算。
另一種技術涉及使用. resizeevent() 動態計算小部件的大小和位置。在這種情況下,您將遇到與前一種技術類似的頭痛。
最有效和推薦的技術是使用 PyQt 的佈局管理器。它們將提高您的工作效率,降低出錯的風險,並提高代碼的可維護性。
佈局管理器是允許您在應用程序窗口或窗體上調整小部件的大小和位置的類。它們自動適應調整事件和 GUI 更改的大小,控制所有子部件的大小和位置。
注意: 如果您開發國際化的應用程序,那麼您可能會看到翻譯後的文本在句中被截斷。當目標自然語言比原始語言更冗長時,這種情況很可能發生。佈局管理器可以根據可用空間自動調整小部件的大小,從而幫助您避免這種常見問題。然而,對於特別冗長的自然語言,這個特性有時會失敗。
PyQt 提供了四個基本的佈局管理器類:
-
QHBoxLayout
-
QVBoxLayout
-
QGridLayout
-
QFormLayout
第一個佈局管理器類,QHBoxLayout,從左到右水平排列小部件,就像下圖中的假設小部件:
在水平佈局中,小部件將一個接一個地出現,從左側開始。下面的代碼示例展示瞭如何使用 QHBoxLayout 水平排列三個按鈕:
# h_layout.py
"""Horizontal layout example."""
import sys
from PyQt6.QtWidgets import (
QApplication,
QHBoxLayout,
QPushButton,
QWidget,
)
app = QApplication([])
window = QWidget()
window.setWindowTitle("QHBoxLayout")
layout = QHBoxLayout()
layout.addWidget(QPushButton("Left"))
layout.addWidget(QPushButton("Center"))
layout.addWidget(QPushButton("Right"))
window.setLayout(layout)
window.show()
sys.exit(app.exec())
下面是這個例子如何創建一個水平佈局的按鈕:
-
第 18 行創建一個名爲 layout 的 QHBoxLayout 對象。
-
第 19 至 21 行通過調用. addwidget() 方法向佈局添加三個按鈕。
-
第 22 行使用. setlayout() 將佈局設置爲窗口的佈局。
當你從命令行運行 python h_layout.py 時,你會得到以下輸出:
上圖顯示了三個按鈕的水平排列。這些按鈕從左到右顯示的順序與您在代碼中添加它們的順序相同。
下一個佈局管理器類是 QVBoxLayout,它從上到下垂直排列小部件,如下圖所示:
每個新的小部件將出現在前一個小部件的下面。這種佈局允許您構建垂直佈局,並在 GUI 上從上到下組織您的小部件。
下面是如何創建一個包含三個按鈕的 QVBoxLayout 對象:
# v_layout.py
"""Vertical layout example."""
import sys
from PyQt6.QtWidgets import (
QApplication,
QPushButton,
QVBoxLayout,
QWidget,
)
app = QApplication([])
window = QWidget()
window.setWindowTitle("QVBoxLayout")
layout = QVBoxLayout()
layout.addWidget(QPushButton("Top"))
layout.addWidget(QPushButton("Center"))
layout.addWidget(QPushButton("Bottom"))
window.setLayout(layout)
window.show()
sys.exit(app.exec())
在第 18 行,創建一個名爲 layout 的 QVBoxLayout 實例。在接下來的三行中,爲佈局添加三個按鈕。最後,通過第 22 行上的. setlayout() 方法,使用佈局對象將小部件排列爲垂直佈局。
當你運行這個示例應用程序時,你會看到一個類似這樣的窗口:
該圖顯示了三個按鈕的垂直排列,一個在另一個的下面。按鈕的出現順序與您在代碼中添加它們的順序相同,從上到下。
列表中的第三個佈局管理器是 QGridLayout。該類在行和列的網格中排列小部件。每個小部件在網格上都有一個相對位置。您可以使用一對座標 (row, column) 來定義小部件的位置。每個座標必須是整數。這些座標對定義了給定小部件將佔據網格中的哪個單元格。
網格佈局看起來像這樣:
QGridLayout 利用可用空間,將其劃分爲行和列,並將每個子部件放入自己的單元格中。
下面是如何在你的 GUI 中創建一個網格佈局:
# g_layout.py
"""Grid layout example."""
import sys
from PyQt6.QtWidgets import (
QApplication,
QGridLayout,
QPushButton,
QWidget,
)
app = QApplication([])
window = QWidget()
window.setWindowTitle("QGridLayout")
layout = QGridLayout()
layout.addWidget(QPushButton("Button (0, 0)"), 0, 0)
layout.addWidget(QPushButton("Button (0, 1)"), 0, 1)
layout.addWidget(QPushButton("Button (0, 2)"), 0, 2)
layout.addWidget(QPushButton("Button (1, 0)"), 1, 0)
layout.addWidget(QPushButton("Button (1, 1)"), 1, 1)
layout.addWidget(QPushButton("Button (1, 2)"), 1, 2)
layout.addWidget(QPushButton("Button (2, 0)"), 2, 0)
layout.addWidget(
QPushButton("Button (2, 1) + 2 Columns Span"), 2, 1, 1, 2
)
window.setLayout(layout)
window.show()
sys.exit(app.exec())
在本例中,您將創建一個應用程序,該應用程序使用 QGridLayout 對象來組織屏幕上的小部件。注意,在本例中,傳遞給. addwidget() 的第二個和第三個參數是整數,定義了每個小部件在網格中的位置。
在第 26 至 28 行,您將另外兩個參數傳遞給. addwidget()。這些參數是 rowSpan 和 columnSpan,它們是傳遞給函數的第四個和第五個參數。可以使用它們使小部件佔用多行或多列,就像在示例中所做的那樣。
如果你從你的命令行運行這段代碼,那麼你會看到一個像這樣的窗口:
在此圖中,可以看到小部件排列在行和列的網格中。最後一個小部件佔用兩列,正如您在第 26 至 28 行中指定的那樣。
您將學習的最後一個佈局管理器是 QFormLayout。這個類以兩列布局的方式排列小部件。第一列通常在標籤中顯示消息。第二列通常包含 QLineEdit、QComboBox、QSpinBox 等小部件。這允許用戶輸入或編輯關於第一列中的信息的數據。
下圖展示了表單佈局在實踐中的工作原理:
左列由標籤組成,而右列由輸入部件組成。如果您正在開發一個數據庫應用程序,那麼這種佈局可能是一個有用的工具,可以提高您在創建輸入表單時的工作效率。
下面的例子展示瞭如何創建一個使用 QFormLayout 對象來排列小部件的應用程序:
# f_layout.py
"""Form layout example."""
import sys
from PyQt6.QtWidgets import (
QApplication,
QFormLayout,
QLineEdit,
QWidget,
)
app = QApplication([])
window = QWidget()
window.setWindowTitle("QFormLayout")
layout = QFormLayout()
layout.addRow("Name:", QLineEdit())
layout.addRow("Age:", QLineEdit())
layout.addRow("Job:", QLineEdit())
layout.addRow("Hobbies:", QLineEdit())
window.setLayout(layout)
window.show()
sys.exit(app.exec())
在這個例子中,第 18 到 23 行完成了困難的工作。QFormLayout 有一個叫做. addrow() 的方便方法。您可以使用此方法向佈局中添加包含兩個小部件的行。. addrow() 的第一個參數應該是一個標籤或字符串。然後,第二個參數可以是允許用戶輸入或編輯數據的任何小部件。在這個特定的示例中,您使用了行編輯。
如果你運行這段代碼,你會看到一個像這樣的窗口:
上圖顯示了一個使用表單佈局的窗口。第一列包含向用戶詢問一些信息的標籤。第二列顯示允許用戶輸入或編輯所需信息的小部件。
對話框
使用 PyQt,您可以開發兩種類型的 GUI 桌面應用程序。根據您用來創建主窗體或窗口的類的不同,您將擁有以下其中之一:
-
主窗口樣式的應用程序: 應用程序的主窗口繼承自 QMainWindow。
-
對話框風格的應用程序: 應用程序的主窗口繼承自 QDialog。
您將首先從對話框風格的應用程序開始。在下一節中,您將瞭解主窗口樣式的應用程序。
要開發對話框風格的應用程序,您需要創建一個繼承自 QDialog 的 GUI 類,它是所有對話框窗口的基類。對話框窗口是一個獨立的窗口,您可以將其用作應用程序的主窗口。
注意: 在主窗口風格的應用程序中,對話窗口通常用於與用戶進行簡短的通信和交互。
當您使用對話框窗口與用戶通信時,這些對話框可以是:
-
Modal: 阻止對同一應用程序中任何其他可見窗口的輸入。你可以通過調用它的. exec() 方法來顯示模態對話框。
-
無模態: 獨立於同一應用程序中的其他窗口操作。你可以使用它的. show() 方法來顯示一個非模態對話框。
對話框窗口還可以提供返回值並具有默認按鈕,如 Ok 和 Cancel。
對話框總是一個獨立的窗口。如果一個對話框有一個父窗口,那麼它將顯示在父窗口部件的頂部。與父級的對話框將共享父級的任務欄條目。如果你沒有爲一個給定的對話框設置 parent,那麼這個對話框將在系統的任務欄中獲得它自己的條目。
下面是一個如何使用 QDialog 開發對話框式應用程序的例子:
# dialog.py
"""Dialog-style application."""
import sys
from PyQt6.QtWidgets import (
QApplication,
QDialog,
QDialogButtonBox,
QFormLayout,
QLineEdit,
QVBoxLayout,
)
class Window(QDialog):
def __init__(self):
super().__init__(parent=None)
self.setWindowTitle("QDialog")
dialogLayout = QVBoxLayout()
formLayout = QFormLayout()
formLayout.addRow("Name:", QLineEdit())
formLayout.addRow("Age:", QLineEdit())
formLayout.addRow("Job:", QLineEdit())
formLayout.addRow("Hobbies:", QLineEdit())
dialogLayout.addLayout(formLayout)
buttons = QDialogButtonBox()
buttons.setStandardButtons(
QDialogButtonBox.StandardButton.Cancel
| QDialogButtonBox.StandardButton.Ok
)
dialogLayout.addWidget(buttons)
self.setLayout(dialogLayout)
if __name__ == "__main__":
app = QApplication([])
window = Window()
window.show()
sys.exit(app.exec())
這個應用程序稍微複雜一些。以下是這段代碼的作用:
-
第 16 行通過繼承 QDialog 爲應用程序的 GUI 定義了一個 Window 類。
-
第 18 行使用 super() 調用父類的.init() 方法。這個調用允許您正確地初始化該類的實例。在本例中,父參數被設置爲 None,因爲這個對話框將是您的主窗口。
-
第 19 行設置窗口的標題。
-
第 20 行將一個 QVBoxLayout 對象賦給 dialogLayout。
-
第 21 行將一個 QFormLayout 對象分配給 formLayout。
-
第 22 至 25 行向 formLayout 添加小部件。
-
第 26 行調用 dialogLayout 上的. addlayout()。這個調用將表單佈局嵌入到全局對話框佈局中。
-
第 27 行定義了一個按鈕框,它提供了一個方便的空間來顯示對話框的按鈕。
-
第 28 至 31 行向對話框添加兩個標準按鈕 Ok 和 Cancel。
-
第 32 行通過調用. addwidget() 將按鈕框添加到對話框中。
if name == "main": 結構包裝了應用程序的主代碼。這種條件語句在 Python 應用程序中很常見。它確保縮進代碼只在包含的文件作爲程序執行而不是作爲模塊導入時纔會運行。有關此構造的更多信息,請查看 if name == "main" 在 Python 中做了什麼?
注意: 在上面示例的第 26 行,您將注意到佈局管理器可以彼此嵌套。您可以通過在容器佈局上調用. addlayout() 來嵌套佈局,並使用嵌套佈局作爲參數。
上面的代碼示例將顯示一個類似這樣的窗口:
該圖顯示了您使用 QFormLayout 對象創建的 GUI,該對象用於排列小部件,並使用 QVBoxLayout 佈局用於應用程序的全局佈局。
主窗口
大多數時候,你的 GUI 應用程序將是主窗口風格的應用程序。這意味着它們將有一個菜單欄、一些工具欄、一個狀態欄和一個作爲 GUI 主要元素的中心小部件。對於你的應用來說,有幾個對話框來完成依賴於用戶輸入的次要操作也是很常見的。
您將繼承 QMainWindow 來開發主窗口樣式的應用程序。從 QMainWindow 派生的類的實例被認爲是應用程序的主窗口,並且應該是唯一的。
QMainWindow 爲快速構建應用程序的 GUI 提供了一個框架。這個類有自己的內置佈局,它接受以下圖形組件:
如果沒有中心小部件,就無法創建主窗口。您需要一箇中心部件,即使它只是一個佔位符。在這種情況下,可以使用 QWidget 對象作爲中心小部件。
可以使用. setcentralwidget() 方法設置窗口的中心小部件。主窗口的佈局將允許您只有一箇中心小部件,但它可以是單個或複合小部件。下面的代碼示例向您展示瞭如何使用 QMainWindow 創建一個主窗口樣式的應用程序:
# main_window.py
"""Main window-style application."""
import sys
from PyQt6.QtWidgets import (
QApplication,
QLabel,
QMainWindow,
QStatusBar,
QToolBar,
)
class Window(QMainWindow):
def __init__(self):
super().__init__(parent=None)
self.setWindowTitle("QMainWindow")
self.setCentralWidget(QLabel("I'm the Central Widget"))
self._createMenu()
self._createToolBar()
self._createStatusBar()
def _createMenu(self):
menu = self.menuBar().addMenu("&Menu")
menu.addAction("&Exit", self.close)
def _createToolBar(self):
tools = QToolBar()
tools.addAction("Exit", self.close)
self.addToolBar(tools)
def _createStatusBar(self):
status = QStatusBar()
status.showMessage("I'm the Status Bar")
self.setStatusBar(status)
if __name__ == "__main__":
app = QApplication([])
window = Window()
window.show()
sys.exit(app.exec())
下面是這段代碼的工作原理:
-
第 15 行創建了一個繼承自 QMainWindow 的類 Window。
-
第 16 行定義了類初始化式。
-
第 17 行調用基類的初始化式。同樣,parent 參數被設置爲 None,因爲這是應用程序的主窗口,所以它必須沒有父窗口。
-
第 18 行設置窗口的標題。
-
第 19 行將 QLabel 設置爲窗口的中心小部件。
-
第 20 至 22 行調用非公共方法來創建不同的 GUI 元素: 第 24 至 26 行使用名爲 menu 的下拉菜單創建主菜單欄。這個菜單將有一個退出應用程序的菜單選項。第 28 行到 31 行創建工具欄,它將有一個工具欄按鈕,用於退出應用程序。第 33 至 36 行創建應用程序的狀態欄。
當您使用它們自己的方法實現 GUI 組件時,就像您在本例中對菜單欄、工具欄和狀態欄所做的那樣,您正在使您的代碼更具可讀性和可維護性。
注意: 如果你在 macOS 上運行這個例子,那麼你可能會遇到應用程序主菜單的問題。macOS 隱藏了某些菜單選項,比如 Exit。記住,macOS 在屏幕頂部的應用程序條目下顯示 Exit 或 Quit 選項。
當你運行上面的示例應用程序時,你會看到如下的窗口:
你可以確認,你的主窗口風格的應用程序有以下組件:
-
一個主菜單通常稱爲菜單
-
一個工具欄與退出工具按鈕
-
一箇中心小部件,包含一個帶有文本消息的 QLabel 對象
-
窗口底部有一個狀態欄
就是這樣! 您已經學習瞭如何使用 Python 和 PyQt 構建主窗口樣式的應用程序。到目前爲止,您已經瞭解了 PyQt 的小部件集中一些更重要的圖形化組件。在接下來的幾節中,您將學習與使用 PyQt 構建 GUI 應用程序相關的其他重要概念。
應用程序
在開發 PyQt GUI 應用程序時,QApplication 是最基本的類。該類是任何 PyQt 應用程序的核心組件。它管理應用程序的控制流以及它的主要設置。
在 PyQt 中,QApplication 的任何實例都是一個應用程序。每個 PyQt GUI 應用程序必須有一個 QApplication 實例。這個類的一些職責包括:
-
處理應用程序的初始化和終結
-
提供事件循環和事件處理
-
處理大多數系統範圍和應用程序範圍的設置
-
提供對全局信息的訪問,例如應用程序的目錄、屏幕大小等
-
解析常用的命令行參數
-
定義應用程序的外觀
-
提供定位功能
這些只是 QApplication 的一些核心職責。因此,在開發 PyQt GUI 應用程序時,這是一個基本類。
QApplication 最重要的職責之一是提供事件循環和整個事件處理機制。在下一節中,您將深入瞭解什麼是事件循環以及它是如何工作的。
事件循環
GUI 應用程序是事件驅動的。這意味着函數和方法是根據用戶操作調用的,比如單擊按鈕、從組合框中選擇項、在文本編輯中輸入或更新文本、按下鍵盤上的一個鍵等等。這些用戶操作通常稱爲事件。
事件由事件循環處理,也稱爲主循環。事件循環是一個無限循環,在這個循環中,來自用戶、窗口系統和任何其他源的所有事件都被處理和分派。事件循環等待事件發生,然後將其分派去執行某些任務。事件循環將繼續工作,直到應用程序終止。
所有 GUI 應用程序都有一個事件循環。當事件發生時,循環檢查它是否爲終止事件。在這種情況下,循環結束,應用程序退出。否則,事件將被髮送到應用程序的事件隊列進行進一步處理,並再次循環。在 PyQt6 中,可以通過在 QApplication 對象上調用. exec() 來運行應用程序的事件循環。
要使事件觸發操作,您需要將事件與想要執行的操作連接起來。在 PyQt 中,可以用信號和槽機制建立連接,這將在下一節中進行探討。
信號與插槽
PyQt 小部件充當事件捕獲器。這意味着每個小部件都可以捕獲特定的事件,如鼠標點擊、按鍵等。作爲對這些事件的響應,小部件發出一個信號,這是一種宣佈其狀態發生變化的消息。
信號本身不執行任何動作。如果你想要一個信號觸發一個動作,那麼你需要把它連接到一個插槽。這是一個函數或方法,它將在發出相關信號時執行操作。可以使用任何 Python 可調用對象作爲插槽。
如果一個信號連接到一個插槽,那麼每當信號發出時就調用該插槽。如果一個信號沒有連接到任何插槽,那麼什麼也不會發生,信號將被忽略。信號和槽的一些最相關的特性包括:
-
一個信號可以連接到一個或多個插槽。
-
一個信號也可以連接到另一個信號。
-
一個槽可以連接到一個或多個信號。
你可以使用下面的語法來連接信號和插槽:
widget.signal.connect(slot_function)
這將連接 slot_function 到 widget.signal。從現在開始,每當. signal 被觸發時,都會調用 slot_function()。
下面的代碼展示瞭如何在 PyQt 應用程序中使用信號和插槽機制:
# signals_slots.py
"""Signals and slots example."""
import sys
from PyQt6.QtWidgets import (
QApplication,
QLabel,
QPushButton,
QVBoxLayout,
QWidget,
)
def greet():16 if msgLabel.text():
msgLabel.setText("")
else:
msgLabel.setText("Hello, World!")
app = QApplication([])
window = QWidget()
window.setWindowTitle("Signals and slots")
layout = QVBoxLayout()
button = QPushButton("Greet")
button.clicked.connect(greet)28
layout.addWidget(button)
msgLabel = QLabel("")
layout.addWidget(msgLabel)
window.setLayout(layout)
window.show()
sys.exit(app.exec())
在第 15 行,創建 greet(),將其用作插槽。然後在第 27 行中,將按鈕的. clicked 信號連接到 greeting()。這樣,每當用戶單擊 Greet 按鈕時,就會調用 Greet() 槽,並且標籤對象的文本在 Hello, World! 和一個空字符串:
當您單擊 Greet 按鈕時,Hello, World! 消息在應用程序的主窗口上出現和消失。
注意: 每個小部件都有自己的一組預定義信號。您可以在小部件的文檔中查看它們。
如果你的 slot 函數需要接收額外的參數,那麼你可以使用 functools.partial() 傳遞它們。例如,你可以修改 greet() 來接受一個參數,如下面的代碼所示:
# signals_slots.py
# ...
def greet(name):
if msg.text():
msg.setText("")
else:
msg.setText(f"Hello, {name}")
# ...
現在 greet() 需要接收一個名爲 name 的參數。如果你想把這個新版本的 greet() 連接到. clicked 信號,你可以這樣做:
# signals_slots.py
"""Signals and slots example."""
import sys
from functools import partial
# ...
button = QPushButton("Greet")
button.clicked.connect(partial(greeting, "World!"))
# ...
要使這段代碼正常工作,您需要首先從 functools 導入 partial()。對 partial() 的調用返回一個函數對象,當使用 name="World!" 調用時,該函數對象的行爲與 greet() 類似。現在,當用戶單擊按鈕時,消息 Hello, World! 會像以前一樣出現在標籤上。
注意: 您還可以使用 lambda 函數將信號連接到需要額外參數的槽位。作爲練習,請嘗試使用 lambda 而不是 functools.partial() 來編寫上面的示例。
信號和槽機制將用於賦予 PyQt GUI 應用程序生命。這種機制將允許您將用戶事件轉換爲具體的操作。您可以通過查閱有關該主題的 PyQt6 文檔來更深入地研究信號和槽。
現在您已經瞭解了 PyQt 的幾個重要概念的基礎知識。有了這些知識和庫的文檔,您就可以開始開發自己的 GUI 應用程序了。在下一節中,您將構建第一個功能齊全的 GUI 應用程序。
用 Python 和 PyQt 創建一個計算器應用程序
在本節中,您將使用模型 - 視圖 - 控制器 (MVC) 設計模式開發一個計算器 GUI 應用程序。這個模式有三層代碼,每一層都有不同的角色:
-
該模型負責應用程序的業務邏輯。它包含核心功能和數據。在計算器應用程序中,模型將處理輸入值和計算。
-
視圖實現了應用程序的 GUI。它承載終端用戶與應用程序交互所需的所有小部件。視圖還接收用戶的操作和事件。對於您的示例,視圖將是屏幕上的計算器窗口。
-
控制器連接模型和視圖以使應用程序工作。用戶的事件或請求被髮送到控制器,控制器使模型工作。當模型以正確的格式交付所請求的結果或數據時,控制器將其轉發給視圖。在計算器應用程序中,控制器將從 GUI 接收目標數學表達式,要求模型執行計算,並使用結果更新 GUI。
以下是 GUI 計算器應用程序如何工作的一步一步描述:
-
用戶在視圖 (GUI) 上執行操作或請求(事件)。
-
視圖通知控制器用戶的操作。
-
控制器獲取用戶的請求並查詢模型以獲得響應。
-
模型處理控制器的查詢,執行所需的計算,並返回結果。
-
控制器接收模型的響應並相應地更新視圖。
-
用戶最終在視圖上看到請求的結果。
您將使用這個 MVC 設計用 Python 和 PyQt 構建計算器應用程序。
爲 PyQt 計算器應用程序創建框架
首先,在一個名爲 pycalc.py 的文件中爲應用程序實現一個最小的框架。
如果您希望自己編寫項目代碼,那麼可以在當前工作目錄中創建 pycalc.py。在您最喜歡的代碼編輯器或 IDE 中打開該文件,並鍵入以下代碼:
# pycalc.py
"""PyCalc is a simple calculator built with Python and PyQt."""
import sys
from PyQt6.QtWidgets import QApplication, QMainWindow, QWidget
WINDOW_SIZE = 235
class PyCalcWindow(QMainWindow):
"""PyCalc's main window (GUI or view)."""
def __init__(self):
super().__init__()
self.setWindowTitle("PyCalc")
self.setFixedSize(WINDOW_SIZE, WINDOW_SIZE)
centralWidget = QWidget(self)
self.setCentralWidget(centralWidget)
def main():
"""PyCalc's main function."""
pycalcApp = QApplication([])
pycalcWindow = PyCalcWindow()
pycalcWindow.show()
sys.exit(pycalcApp.exec())
if __name__ == "__main__":
main()
這個腳本實現了運行基本 GUI 應用程序所需的所有樣板代碼。您將使用這個框架來構建計算器應用程序。
下面是這段代碼的工作原理:
-
第 5 行導入 sys。這個模塊提供了 exit() 函數,您將使用它乾淨地終止應用程序。
-
第 7 行從 PyQt6.QtWidgets 中導入所需的類。
-
第 9 行創建一個 Python 常量,爲計算器應用程序保存一個固定的窗口大小 (以像素爲單位)。
-
第 11 行創建 PyCalcWindow 類來提供應用程序的 GUI。注意,這個類繼承自 QMainWindow。
-
第 14 行定義了類初始化式。
-
第 15 行調用超類的.init() 以實現初始化目的。
-
第 16 行將窗口標題設置爲 “PyCalc”。
-
第 17 行使用. setfixedsize() 給窗口一個固定的大小。這確保了用戶在應用程序執行期間不能調整窗口的大小。
-
第 18 行和第 19 行創建一個 QWidget 對象,並將其設置爲窗口的中心小部件。這個對象將是計算器應用程序中所有必需的 GUI 組件的父組件。
-
第 21 行定義了計算器的主要函數。使用這樣的 main() 函數是 Python 中的最佳實踐。這個函數提供應用程序的入口點。在 main() 中,你的程序執行以下操作: 第 23 行創建一個名爲 pycalcApp 的 QApplication 對象。第 24 行創建了應用程序窗口 pycalcWindow 的一個實例。第 25 行通過在窗口對象上調用. show() 來顯示 GUI。第 26 行使用. exec() 運行應用程序的事件循環。
最後,第 29 行調用 main() 來執行計算器應用程序。當運行上述腳本時,屏幕上會出現以下窗口:
就是這樣! 您已經成功地爲 GUI 計算器應用程序構建了一個功能齊全的應用程序框架。現在可以繼續構建項目了。
完成應用的視圖
此時您擁有的 GUI 看起來並不像計算器。您需要通過添加顯示目標數學運算的顯示器和表示數字和基本數學運算符的按鈕鍵盤來完成這個 GUI。您還將添加表示其他所需符號和操作的按鈕,比如清除顯示。
首先,你需要像下面的代碼一樣更新你的導入:
# pycalc.py
import sys
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import (
QApplication,
QGridLayout,
QLineEdit,
QMainWindow,
QPushButton,
QVBoxLayout,
QWidget,
)
# ...
您將使用 QVBoxLayout 佈局管理器來進行計算器的全局佈局。要排列按鈕,您將使用一個 QGridLayout 對象。QLineEdit 類將作爲計算器的顯示,QPushButton 將提供所需的按鈕。
現在你可以更新 PyCalcWindow 的初始化式了:
# pycalc.py
# ...
class PyCalcWindow(QMainWindow):
"""PyCalc's main window (GUI or view)."""
def __init__(self):
super().__init__()
self.setWindowTitle("PyCalc")
self.setFixedSize(WINDOW_SIZE, WINDOW_SIZE)
self.generalLayout = QVBoxLayout()
centralWidget = QWidget(self)
centralWidget.setLayout(self.generalLayout)
self.setCentralWidget(centralWidget)
self._createDisplay()
self._createButtons()
# ...
您已經添加了突出顯示的代碼行。你將使用. generallayout 作爲應用程序的總體佈局。在這個佈局中,您將把顯示器放在頂部,鍵盤按鈕放在底部的網格佈局中。
對. _createdisplay() 和. _createbuttons() 的調用此時還不能工作,因爲您還沒有實現這些方法。要修復這個問題,首先編寫. _createdisplay()。
回到代碼編輯器,像下面這樣更新 pycalc.py:
# pycalc.py
# ...
WINDOW_SIZE = 235
DISPLAY_HEIGHT = 35
class PyCalcWindow(QMainWindow):
# ...
def _createDisplay(self):
self.display = QLineEdit()
self.display.setFixedHeight(DISPLAY_HEIGHT)
self.display.setAlignment(Qt.AlignmentFlag.AlignRight)
self.display.setReadOnly(True)
self.generalLayout.addWidget(self.display)
# ...
在此代碼片段中,首先定義一個新常量來保存以像素爲單位的顯示高度。然後在 PyCalcWindow 中定義. _createdisplay()。
要創建計算器的顯示,可以使用 QLineEdit 小部件。然後使用 DISPLAY_HEIGHT 常量爲顯示設置 35 個像素的固定高度。顯示的文本將左對齊。最後,顯示將是隻讀的,以防止用戶直接編輯。最後一行代碼將顯示添加到計算器的總體佈局中。
接下來,您將實現. _createbuttons() 方法,爲計算器鍵盤創建所需的按鈕。這些按鈕將位於網格佈局中,因此您需要一種方法在網格中表示它們的座標。每個座標對將由一行和一列組成。要表示座標對,您將使用列表的列表。每個嵌套列表將表示一行。
現在繼續,用以下代碼更新 pycalc.py 文件:
# pycalc.py
# ...WINDOW_SIZE = 235
DISPLAY_HEIGHT = 35
BUTTON_SIZE = 40
# ...
在這段代碼中,您定義了一個名爲 BUTTON_SIZE 的新常量。您將使用這個常量來提供計算器按鈕的大小。在這個特定的例子中,所有的按鈕都是一個每邊 40 像素的正方形形狀。
通過這個初始設置,您可以編寫. _createbuttons() 方法。您將使用列表的列表來保存鍵或按鈕及其在計算器鍵盤上的位置。QGridLayout 允許你安排計算器窗口上的按鈕:
# pycalc.py
# ...
class PyCalcWindow(QMainWindow):
# ...
def _createButtons(self):
self.buttonMap = {}
buttonsLayout = QGridLayout()keyBoard = [
["7", "8", "9", "/", "C"],
["4", "5", "6", "*", "("],
["1", "2", "3", "-", ")"],
["0", "00", ".", "+", "="],
]
for row, keys in enumerate(keyBoard):
for col, key in enumerate(keys):
self.buttonMap[key] = QPushButton(key)
self.buttonMap[key].setFixedSize(BUTTON_SIZE, BUTTON_SIZE)
buttonsLayout.addWidget(self.buttonMap[key], row, col)
self.generalLayout.addLayout(buttonsLayout)
# ...
首先創建空字典 self。buttonMap 用於保存計算器按鈕。然後,創建一個列表的列表來存儲鍵標籤。每一行或嵌套列表將表示網格佈局中的一行,而每個鍵標籤的索引將表示佈局中相應的列。
然後定義兩個 for 循環。外部循環遍歷行,內部循環遍歷列。在內部循環中,創建按鈕並將它們添加到兩個 self 中。buttonMap buttonsLayout。每個按鈕都有一個 40x40 像素的固定大小,這可以通過. setfixedsize() 和 BUTTON_SIZE 常量來設置。
最後,通過調用. generalayout 對象上的. addlayout() 將網格佈局嵌入到計算器的通用佈局中。
注意: 當談到小部件大小時,您很少會在 PyQt 文檔中找到度量單位。測量單位被假定爲像素,除非使用 QPrinter 類,它使用點。
現在,計算器的 GUI 將優雅地顯示顯示和按鈕。但是,您無法更新顯示的信息。你可以通過在 PyCalcWindow 中添加一些額外的方法來解決這個問題:
這些方法將提供 GUI 的公共接口,併爲您的 Python 計算器應用程序完成視圖類。
下面是一個可能的實現:
# pycalc.py
# ...
class PyCalcWindow(QMainWindow):
# ...
def setDisplayText(self, text):
"""Set the display's text."""
self.display.setText(text)
self.display.setFocus()
def displayText(self):
"""Get the display's text."""
return self.display.text()
def clearDisplay(self):
"""Clear the display."""
self.setDisplayText("")
# ...
以下是每種方法的具體功能:
-
. setdisplaytext() 使用. settext() 來設置和更新顯示的文本。它還使用. setfocus() 將光標的焦點設置在顯示器上。
-
. displaytext()是一個返回顯示當前文本的 getter 方法。當用戶點擊計算器鍵盤上的等號 (=) 時,應用程序將使用. displaytext()的返回值作爲要求值的數學表達式。
-
. cleardisplay() 將顯示的文本設置爲空字符串 (""),以便用戶可以引入一個新的數學表達式。每當用戶按下計算器面板上的 C 按鈕時,就會觸發此方法。
現在您的計算器的 GUI 已經準備好使用了! 當你運行這個應用程序時,你會看到一個如下面的窗口:
您已經完成了計算器的 GUI,它看起來非常流暢! 然而,如果您嘗試進行一些計算,那麼計算器將不會像預期的那樣響應。這是因爲您還沒有實現模型和控制器組件。在下一節中,您將編寫計算器的模型。
實現計算器的模型
在 MVC 模式中,模型是負責業務邏輯的代碼層。在計算器應用程序中,業務邏輯都是關於基本的數學計算。因此,您的模型將計算用戶在計算器的 GUI 中引入的數學表達式。
計算器的模型還需要處理錯誤。爲此,你將定義以下全局常數:
# pycalc.py
# ...
ERROR_MSG = "ERROR"
WINDOW_SIZE = 235
# ...
如果用戶引入了無效的數學表達式,則 ERROR_MSG 常量是用戶將在計算器顯示器上看到的消息。
有了以上的更改,你就可以編寫應用程序的模型了,在這個例子中,它將是一個單獨的函數:
# pycalc.py
# ...
class PyCalcWindow(QMainWindow):
# ...
def evaluateExpression(expression):
"""Evaluate an expression (Model)."""
try:
result = str(eval(expression, {}, {}))
except Exception:
result = ERROR_MSGreturn result
# ...
在 evaluateExpression() 中,使用 eval() 對作爲字符串的數學表達式求值。如果計算成功,則返回結果。否則,將返回預定義的錯誤消息。注意,這個函數並不完美。它有幾個重要的問題:
-
try…except 塊不會捕獲特定的異常,因此它使用的是 Python 中不鼓勵使用的實踐。
-
該函數使用 eval(),這可能會導致一些嚴重的安全問題。
您可以自由地修改該函數,使其更加可靠和安全。在本教程中,您將按原樣使用該函數,將重點放在實現 GUI 上。
爲計算器創建控制器類
在本節中,您將編寫計算器的控制器類。這個類將視圖連接到您剛剛編寫的模型。您將使用控制器類使計算器執行響應用戶事件的操作。
你的控制器類需要執行三個主要任務:
-
訪問 GUI 的公共接口。
-
處理數學表達式的創建。
-
連接所有按鈕的。點擊信號與適當的插槽。
要執行所有這些操作,稍後將編寫一個新的 PyCalc 類。繼續使用以下代碼更新 pycalc.py:
# pytcalc.py
import sys
from functools import partial
# ...
def evaluateExpression(expression):
# ...
class PyCalc:
"""PyCalc's controller class."""
def __init__(self, model, view):
self._evaluate = model
self._view = view
self._connectSignalsAndSlots()
def _calculateResult(self):
result = self._evaluate(expression=self._view.displayText())
self._view.setDisplayText(result)
def _buildExpression(self, subExpression):
if self._view.displayText() == ERROR_MSG:
self._view.clearDisplay()
expression = self._view.displayText() + subExpression
self._view.setDisplayText(expression)
def _connectSignalsAndSlots(self):
for keySymbol, button in self._view.buttonMap.items():
if keySymbol not in {"=", "C"}:
button.clicked.connect(
partial(self._buildExpression, keySymbol)
)
self._view.buttonMap["="].clicked.connect(self._calculateResult)
self._view.display.returnPressed.connect(self._calculateResult)
self._view.buttonMap["C"].clicked.connect(self._view.clearDisplay)
# ...
在 pycalc.py 的頂部,從 functools 導入 partial()。您將使用此函數將信號與需要接受額外參數的方法連接起來。
在 PyCalc 內部,定義類初始化式,它接受兩個參數: 應用程序的模型和它的視圖。然後將這些參數存儲在適當的實例屬性中。最後,調用. _connectsignalsandslots() 來建立所有必需的信號和插槽連接。
在. _calculateresult() 中,使用._evaluate() 對用戶剛剛輸入計算器顯示的數學表達式求值。然後在計算器視圖上調用. setdisplaytext(),用計算結果更新顯示文本。
顧名思義,. _buildexpression() 方法負責構建目標數學表達式。爲此,該方法將初始顯示值與用戶在計算器鍵盤上輸入的每個新值連接起來。
最後,. _connectsignalsandslots() 方法將所有按鈕的. clicked 信號與控制器類中的適當 slots 方法連接起來。
就是這樣! 控制器類已經準備好了。然而,對於所有這些代碼作爲一個真正的計算器工作,你需要更新應用程序的 main() 函數如下所示:
# pytcalc.py
# ...
def main():
"""PyCalc's main function."""
pycalcApp = QApplication([])
pycalcWindow = PyCalcWindow()
pycalcWindow.show()
PyCalc(model=evaluateExpression, view=pycalcWindow)
sys.exit(pycalcApp.exec())
這段代碼創建了 PyCalc 的一個新實例。PyCalc 類構造函數的模型參數保存了對 evaluateExpression() 函數的引用,而視圖參數保存了對 pycalcWindow 對象的引用,該對象提供了應用程序的 GUI。現在 PyQt 計算器應用程序可以運行了。
運行計算器
現在您已經完成了用 Python 和 PyQt 編寫計算器應用程序,是時候進行現場測試了! 如果你從你的命令行運行應用程序,那麼你會得到這樣的結果:
要使用 PyCalc,請用鼠標輸入有效的數學表達式。然後,按 Enter 鍵或單擊等號 (=) 按鈕進行計算,並在計算器的顯示器上顯示錶達式結果。就是這樣! 您已經使用 Python 和 PyQt 開發了第一個功能齊全的 GUI 桌面應用程序!
額外的工具
PyQt6 提供了一組有用的附加工具,可以幫助您構建可靠的、現代的、功能齊全的 GUI 應用程序。與 PyQt 相關的一些最顯著的工具包括 Qt Designer 和國際化工具包。
Qt Designer 允許您使用拖放界面設計和構建圖形用戶界面。您可以使用這個工具通過使用屏幕上的表單和拖放機制來設計小部件、對話框和主窗口。下面的動畫展示了 Qt Designer 的一些功能:
Qt Designer 使用 XML .ui 文件來存儲 GUI 設計。PyQt 包含一個名爲 uic 的模塊來幫助處理. ui 文件。您還可以使用名爲 pyyuic6 的命令行工具將. ui 文件內容轉換爲 Python 代碼。
注意: 要深入瞭解 Qt Designer 並更好地理解如何使用該工具創建圖形用戶界面,請查看 Qt Designer 和 Python: 更快地構建您的 GUI 應用程序。
PyQt6 還提供了一套全面的工具,用於將應用程序國際化爲本地語言。pylupdate6 命令行工具創建和更新翻譯 (.ts) 文件,該文件可以包含接口字符串的翻譯。如果您更喜歡 GUI 工具,那麼您可以使用 Qt Linguist 創建和更新. ts 文件,其中包含接口字符串的翻譯。
結論
圖形用戶界面 (GUI) 應用程序仍然佔據軟件開發市場的很大份額。Python 提供了一些框架和庫,可以幫助您開發現代而健壯的 GUI 應用程序。
在本教程中,您學習瞭如何使用 PyQt,它是用 Python 開發 GUI 應用程序最流行和最可靠的庫之一。現在您知道了如何有效地使用 PyQt 構建現代 GUI 應用程序。
在本教程中,您已經學習瞭如何:
-
使用 Python 和 PyQt 構建圖形用戶界面
-
將用戶的事件與應用程序的邏輯連接起來
-
使用適當的項目佈局組織 PyQt 應用程序
-
使用 PyQt 創建一個真實的 GUI 應用程序
現在,您可以使用 Python 和 PyQt 知識爲自己的桌面 GUI 應用程序注入活力。這不是很酷嗎?
雖然 PyQt6 文檔是這裏列出的第一個資源,但它的一些重要部分仍然缺失或不完整。幸運的是,您可以使用 Qt 文檔來填補空白。
來源: https://www.toutiao.com/article/7152331573755593231/?log_from=1f6b6730d0a47_1665452947340
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/hXthKtsHRbH9eZsU3MvorA