利用MVC設計模式構建GUI(PyQt5版)

語言: CN / TW / HK

今天介紹一個PyQt5中利用MVC設計模式構建GUI的例子,這個案例來源於《MATLAB面向物件程式設計——從入門到設計模式(第2版)》第7章內容,關於存取款的GUI工具設計,詳情請參考127~160頁面。

利用PyQt5最終建立的介面如下:

接下來聊一聊,具體的實現細節。

與Matlab設計的版本對比,唯一的區別在於事件處理機制的實現方式不同而已。

PyQt5/Qt 中稱為訊號和槽,用於物件之間的通訊,當指定事件發生,一個事件訊號會被髮射,槽可以被任何Python指令碼呼叫。當和槽連線的訊號被髮射時,槽會被呼叫。自定義事件由PyQt5.QtCore中的pyqtSignal方法完成。

Matlab 中的handle類也已經幫我們實現好了事件處理機制,除了控制元件自帶的,諸如 CallbackButtonPushedFcn等屬性(也可以理解成內建事件)外,也可以自定義事件(events定義),然後通過notify方法進行事件的註冊(對應於Qt中的訊號發射!),最後由addlistener監聽事件(相當於槽的作用)。

實現的模型類原始碼如下:

python  # !/usr/bin/env python3  # -*- coding:utf-8 -*-  ​  from PyQt5.QtCore import QObject, pyqtSignal  ​  ​  class BalanceModel(QObject):  ​      balance_changed = pyqtSignal()  ​      def __init__(self, balance):          super(BalanceModel, self).__init__()          self.balance = balance  ​      def withdraw(self, val):          self.balance -= val          self.balance_changed.emit()  ​      def deposit(self, val):          self.balance += val          self.balance_changed.emit()  ​

由語句balance_changed = pyqtSignal()自定義了一個事件訊號:balance_changed。在模型類中,當取款或存款發生時,餘額發生了改變,即balance_changed事件訊號觸發了,會發布(發射)訊息(通過繫結emit()方法)。接下來需要在檢視類中定義所謂的槽函式,即監聽balance_changed事件訊號,做出響應。

檢視類的原始碼如下:

python  # !/usr/bin/env python3  # -*- coding:utf-8 -*-  ​  from PyQt5.QtWidgets import (QWidget,                               QGridLayout,                               QLabel,                               QLineEdit,                               QPushButton)  from PyQt5.QtCore import Qt  from BalanceController import BalanceController  ​  ​  class BalanceView(QWidget):  ​      def __init__(self, m_obj):          super(BalanceView, self).__init__()          self.m_obj = m_obj          self.c_obj = self.make_controller()  # Controller object          self.balance_label = None          self.balance_text = None          self.rmb_label = None          self.rmb_text = None          self.withdraw_btn = None          self.deposit_btn = None          self.build_app()          self.attach2controller(self.c_obj)          self.m_obj.balance_changed.connect(self.update_balance)  ​      def build_app(self):          self.setVisible(False)          self.resize(300, 120)          self.setWindowTitle('存取款介面')  ​          self.balance_label = QLabel('Balance')          self.balance_label.setAlignment(Qt.AlignRight)          self.balance_text = QLineEdit()          self.balance_text.setAlignment(Qt.AlignRight)          self.balance_text.setReadOnly(True)          self.balance_text.setText('0')          self.rmb_label = QLabel('RMB')          self.rmb_label.setAlignment(Qt.AlignRight)          self.rmb_text = QLineEdit()          self.rmb_text.setAlignment(Qt.AlignRight)          self.rmb_text.setText('0')          self.withdraw_btn = QPushButton('withdraw')          self.withdraw_btn.setAutoFillBackground(True)          self.deposit_btn = QPushButton('deposit')  ​          main_layout = QGridLayout(self)          main_layout.setHorizontalSpacing(15)          main_layout.setVerticalSpacing(15)  ​          main_layout.addWidget(self.balance_label, 0, 0, 1, 2)          main_layout.addWidget(self.balance_text, 0, 2, 1, 2)          main_layout.addWidget(self.rmb_label, 1, 0, 1, 2)          main_layout.addWidget(self.rmb_text, 1, 2, 1, 2)          main_layout.addWidget(self.withdraw_btn, 2, 0, 1, 2)          main_layout.addWidget(self.deposit_btn, 2, 2, 1, 2)  ​          self.setLayout(main_layout)          self.setVisible(True)          self.update_balance()  ​      def update_balance(self):          self.balance_text.setText(str(self.m_obj.balance))  ​      def make_controller(self):          controller = BalanceController(self, self.m_obj)          return controller  ​      def attach2controller(self, controller):          self.withdraw_btn.clicked.connect(controller.withdraw_btn_callback)          self.deposit_btn.clicked.connect(controller.deposit_btn_callback)  ​

對於按鈕控制元件,需要connect方法監聽該事件訊號: self.m_obj.balance_changed.connect(self.update_balance)

控制器類原始碼如下:

python  # !/usr/bin/env python3  # -*- coding:utf-8 -*-  ​  ​  class BalanceController:  ​      def __init__(self, v_obj, m_obj):          self.v_obj = v_obj          self.m_obj = m_obj  ​      def withdraw_btn_callback(self):          val = float(self.v_obj.rmb_text.displayText())          self.m_obj.withdraw(val)  ​      def deposit_btn_callback(self):          val = float(self.v_obj.rmb_text.displayText())          self.m_obj.deposit(val)  ​

最後,我們可以自定義指令碼 balanceApp.py 將MVC的原始碼組合起來,就可以執行出這個GUI小工具了,具體如下動圖所示:

至此,我們介紹了一個比較完整的利用MVC設計模式構建GUI的方法!希望您喜歡,並且可以從中獲得有用的東西。

本文完整程式碼,請在gzh內回“pyqt5_mvc”進行下載。

【往期推薦】