最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于Python+PyQt+MVVM方式開發(fā)桌面應(yīng)用

 更新時(shí)間:2026年05月18日 08:36:01   作者:yuanpan  
本文通過一個(gè)簡(jiǎn)單的待辦事項(xiàng)小工具,介紹了使用Python和PyQt6實(shí)現(xiàn)MVVM架構(gòu)的方法,文章詳細(xì)解釋了什么是MVVM,并展示了如何將項(xiàng)目分為Model、View和ViewModel三層,從而使代碼更加清晰、可維護(hù),文中還提供了項(xiàng)目結(jié)構(gòu)建議、代碼示例和安裝方法,需要的朋友可以參考下

很多 Python 開發(fā)者第一次寫 PyQt 桌面應(yīng)用時(shí),代碼很容易變成這樣:

  • 按鈕點(diǎn)擊邏輯寫在窗口類里。
  • 數(shù)據(jù)處理也寫在窗口類里。
  • 數(shù)據(jù)校驗(yàn)、狀態(tài)刷新、列表更新都寫在窗口類里。
  • 項(xiàng)目越寫越大,最后 MainWindow 變成幾千行。

小 demo 這樣寫沒問題,但如果你要做一個(gè)長期維護(hù)的桌面應(yīng)用,比如數(shù)據(jù)處理工具、內(nèi)部管理軟件、設(shè)備控制客戶端、圖像處理工具,就需要更清晰的代碼組織方式。

這篇文章用一個(gè)簡(jiǎn)單的待辦事項(xiàng)小工具,帶大家入門 Python + PyQt6 + MVVM 的開發(fā)方式。

說明:本文使用 PyQt6 演示。根據(jù) PyPI 頁面,PyQt6 是 Qt6 的 Python 綁定,截至 2026-04-22 可見的最新版本為 6.11.0,安裝要求 Python >=3.10。PyQt6 使用 GPL v3 或商業(yè)授權(quán),正式商業(yè)項(xiàng)目請(qǐng)?zhí)崆按_認(rèn)授權(quán)。

一、什么是 MVVM

MVVM 是 Model - View - ViewModel 的縮寫。

在桌面應(yīng)用中,可以這樣理解:

負(fù)責(zé)什么不應(yīng)該負(fù)責(zé)什么
Model業(yè)務(wù)數(shù)據(jù)、業(yè)務(wù)規(guī)則、數(shù)據(jù)結(jié)構(gòu)不關(guān)心按鈕、窗口、控件
View窗口、按鈕、輸入框、列表等界面控件不寫復(fù)雜業(yè)務(wù)邏輯
ViewModel連接 View 和 Model,處理命令、狀態(tài)、校驗(yàn)、通知界面刷新不直接依賴具體界面布局

簡(jiǎn)單說:

  • Model 關(guān)心“數(shù)據(jù)是什么”。
  • View 關(guān)心“界面長什么樣”。
  • ViewModel 關(guān)心“界面操作后應(yīng)該怎么改變數(shù)據(jù)和狀態(tài)”。

二、PyQt 里為什么適合用 MVVM

Qt 本身有兩個(gè)非常重要的機(jī)制:

  1. Signals / Slots:信號(hào)和槽,用來做對(duì)象之間的通信。
  2. Model / View:模型視圖機(jī)制,用來把數(shù)據(jù)和顯示控件分離。

Qt 官方文檔中提到,Model/View 架構(gòu)用于管理數(shù)據(jù)與顯示方式之間的關(guān)系,數(shù)據(jù)變化可以通過信號(hào)通知視圖刷新。Signals/Slots 則是 Qt 對(duì)象之間通信的核心機(jī)制。

這兩個(gè)機(jī)制和 MVVM 很契合:

  • View 點(diǎn)擊按鈕,調(diào)用 ViewModel 的方法。
  • ViewModel 修改 Model。
  • ViewModel 通過信號(hào)通知 View 更新狀態(tài)。
  • View 使用 Qt 的 item model 顯示列表、表格、樹。

需要注意的是,Qt 里的 Model/View 和 MVVM 里的 Model 不是一回事:

  • MVVM 的 Model 是業(yè)務(wù)模型,比如 TodoItemUserOrder。
  • Qt 的 QStandardItemModel、QAbstractListModel 是給界面控件顯示數(shù)據(jù)用的模型。

初學(xué)時(shí)先把這兩件事區(qū)分開,后面架構(gòu)會(huì)清晰很多。

三、本文要做的演示程序

我們做一個(gè)簡(jiǎn)單的待辦事項(xiàng)桌面應(yīng)用:

功能包括:

  1. 輸入任務(wù)名稱。
  2. 點(diǎn)擊按鈕添加任務(wù)。
  3. 在列表中顯示任務(wù)。
  4. 雙擊任務(wù)切換完成狀態(tài)。
  5. 選中任務(wù)后可以刪除。
  6. 底部顯示統(tǒng)計(jì)信息。
  7. 輸入為空時(shí)給出提示。

這個(gè)功能不復(fù)雜,但足夠演示 MVVM 的基本分層。

四、項(xiàng)目結(jié)構(gòu)

建議使用下面的目錄結(jié)構(gòu):

pyqt_mvvm_demo/
  requirements.txt
  main.py
  models/
    __init__.py
    todo_item.py
  viewmodels/
    __init__.py
    todo_list_view_model.py
  views/
    __init__.py
    main_window.py

每個(gè)目錄的職責(zé):

文件職責(zé)
models/todo_item.py定義業(yè)務(wù)數(shù)據(jù)結(jié)構(gòu)
viewmodels/todo_list_view_model.py管理任務(wù)列表、處理添加/刪除/切換完成狀態(tài)
views/main_window.py創(chuàng)建窗口和控件,綁定 ViewModel
main.py程序入口

五、安裝 PyQt6

先創(chuàng)建虛擬環(huán)境:

python -m venv .venv

Windows 激活:

.venv\Scripts\activate

macOS / Linux 激活:

source .venv/bin/activate

安裝 PyQt6:

pip install PyQt6

requirements.txt

PyQt6>=6.11.0

如果你的環(huán)境暫時(shí)不能安裝最新版本,也可以先不鎖版本:

PyQt6

六、編寫 Model:業(yè)務(wù)數(shù)據(jù)

創(chuàng)建 models/todo_item.py

from dataclasses import dataclass


@dataclass
class TodoItem:
    title: str
    done: bool = False

這個(gè)類非常簡(jiǎn)單,只表達(dá)業(yè)務(wù)數(shù)據(jù):

  • title:任務(wù)標(biāo)題。
  • done:是否完成。

它不關(guān)心界面,也不依賴 PyQt。

這就是 MVVM 里 Model 層應(yīng)該保持的狀態(tài):干凈、簡(jiǎn)單、可測(cè)試。

七、編寫 ViewModel:業(yè)務(wù)狀態(tài)和界面命令

創(chuàng)建 viewmodels/todo_list_view_model.py

from PyQt6.QtCore import QObject, Qt, pyqtSignal
from PyQt6.QtGui import QStandardItem, QStandardItemModel

from models.todo_item import TodoItem


class TodoListViewModel(QObject):
    message_changed = pyqtSignal(str)
    stats_changed = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self._items: list[TodoItem] = []
        self.list_model = QStandardItemModel()
        self.list_model.itemChanged.connect(self._on_item_changed)
        self._update_stats()

    def add_task(self, title: str) -> bool:
        title = title.strip()
        if not title:
            self.message_changed.emit("任務(wù)名稱不能為空")
            return False

        item = TodoItem(title=title)
        self._items.append(item)
        self._append_item_to_view_model(item)

        self.message_changed.emit(f"已添加任務(wù):{title}")
        self._update_stats()
        return True

    def remove_task(self, row: int) -> None:
        if row < 0 or row >= len(self._items):
            self.message_changed.emit("請(qǐng)先選擇要?jiǎng)h除的任務(wù)")
            return

        removed = self._items.pop(row)
        self.list_model.removeRow(row)

        self.message_changed.emit(f"已刪除任務(wù):{removed.title}")
        self._update_stats()

    def toggle_task(self, row: int) -> None:
        if row < 0 or row >= len(self._items):
            return

        item = self._items[row]
        item.done = not item.done

        qt_item = self.list_model.item(row)
        qt_item.setCheckState(
            Qt.CheckState.Checked if item.done else Qt.CheckState.Unchecked
        )

        self.message_changed.emit(
            f"已完成:{item.title}" if item.done else f"已取消完成:{item.title}"
        )
        self._update_stats()

    def _append_item_to_view_model(self, todo: TodoItem) -> None:
        qt_item = QStandardItem(todo.title)
        qt_item.setEditable(False)
        qt_item.setCheckable(True)
        qt_item.setCheckState(
            Qt.CheckState.Checked if todo.done else Qt.CheckState.Unchecked
        )
        qt_item.setData(todo, Qt.ItemDataRole.UserRole)
        self.list_model.appendRow(qt_item)

    def _on_item_changed(self, qt_item: QStandardItem) -> None:
        row = qt_item.row()
        if row < 0 or row >= len(self._items):
            return

        self._items[row].done = qt_item.checkState() == Qt.CheckState.Checked
        self._update_stats()

    def _update_stats(self) -> None:
        total = len(self._items)
        done = sum(1 for item in self._items if item.done)
        pending = total - done
        self.stats_changed.emit(f"總數(shù):{total},已完成:{done},未完成:{pending}")

這一層是本文最關(guān)鍵的部分。

ViewModel 做了幾件事:

  1. 保存業(yè)務(wù)數(shù)據(jù)列表:self._items
  2. 提供給界面顯示的 Qt 模型:self.list_model。
  3. 提供界面可調(diào)用的命令:add_task()remove_task()、toggle_task()。
  4. 通過信號(hào)告訴界面狀態(tài)變化:message_changedstats_changed。

ViewModel 不應(yīng)該做的事:

  • 不創(chuàng)建窗口。
  • 不直接操作按鈕樣式。
  • 不關(guān)心控件放在第幾行第幾列。

這樣以后即使 View 從 QWidget 改成 QML,或者從桌面窗口改成別的界面形式,很多業(yè)務(wù)邏輯仍然可以復(fù)用。

八、編寫 View:窗口和控件

創(chuàng)建 views/main_window.py

from PyQt6.QtCore import QModelIndex
from PyQt6.QtWidgets import (
    QHBoxLayout,
    QLabel,
    QLineEdit,
    QListView,
    QMainWindow,
    QPushButton,
    QVBoxLayout,
    QWidget,
)

from viewmodels.todo_list_view_model import TodoListViewModel


class MainWindow(QMainWindow):
    def __init__(self, view_model: TodoListViewModel):
        super().__init__()
        self.view_model = view_model

        self.setWindowTitle("PyQt6 + MVVM 待辦事項(xiàng)")
        self.resize(520, 360)

        self.task_input = QLineEdit()
        self.task_input.setPlaceholderText("輸入任務(wù)名稱")

        self.add_button = QPushButton("添加")
        self.remove_button = QPushButton("刪除選中")
        self.remove_button.setEnabled(False)

        self.todo_list = QListView()
        self.todo_list.setModel(self.view_model.list_model)

        self.message_label = QLabel("請(qǐng)輸入任務(wù)名稱,然后點(diǎn)擊添加")
        self.stats_label = QLabel()

        self._build_layout()
        self._bind_events()
        self._bind_view_model()

    def _build_layout(self) -> None:
        input_layout = QHBoxLayout()
        input_layout.addWidget(self.task_input)
        input_layout.addWidget(self.add_button)

        button_layout = QHBoxLayout()
        button_layout.addStretch()
        button_layout.addWidget(self.remove_button)

        root_layout = QVBoxLayout()
        root_layout.addLayout(input_layout)
        root_layout.addWidget(self.todo_list)
        root_layout.addLayout(button_layout)
        root_layout.addWidget(self.stats_label)
        root_layout.addWidget(self.message_label)

        container = QWidget()
        container.setLayout(root_layout)
        self.setCentralWidget(container)

    def _bind_events(self) -> None:
        self.add_button.clicked.connect(self._on_add_clicked)
        self.task_input.returnPressed.connect(self._on_add_clicked)
        self.remove_button.clicked.connect(self._on_remove_clicked)
        self.todo_list.doubleClicked.connect(self._on_task_double_clicked)
        self.todo_list.selectionModel().currentChanged.connect(self._on_selection_changed)

    def _bind_view_model(self) -> None:
        self.view_model.message_changed.connect(self.message_label.setText)
        self.view_model.stats_changed.connect(self.stats_label.setText)

    def _on_add_clicked(self) -> None:
        ok = self.view_model.add_task(self.task_input.text())
        if ok:
            self.task_input.clear()
            self.task_input.setFocus()

    def _on_remove_clicked(self) -> None:
        row = self.todo_list.currentIndex().row()
        self.view_model.remove_task(row)

    def _on_task_double_clicked(self, index: QModelIndex) -> None:
        self.view_model.toggle_task(index.row())

    def _on_selection_changed(self, current: QModelIndex) -> None:
        self.remove_button.setEnabled(current.isValid())

View 層主要做三件事:

  1. 創(chuàng)建界面控件。
  2. 綁定控件事件。
  3. 綁定 ViewModel 的信號(hào)。

這里的窗口類沒有直接修改 _items,也沒有自己計(jì)算任務(wù)統(tǒng)計(jì)信息。它只負(fù)責(zé)把用戶操作轉(zhuǎn)交給 ViewModel。

這就是 MVVM 的核心思想:View 盡量薄,業(yè)務(wù)狀態(tài)放到 ViewModel。

九、編寫程序入口

創(chuàng)建 main.py

import sys

from PyQt6.QtWidgets import QApplication

from viewmodels.todo_list_view_model import TodoListViewModel
from views.main_window import MainWindow


def main() -> int:
    app = QApplication(sys.argv)

    view_model = TodoListViewModel()
    window = MainWindow(view_model)
    window.show()

    return app.exec()


if __name__ == "__main__":
    raise SystemExit(main())

運(yùn)行:

python main.py

十、為了方便復(fù)制,這里給一個(gè)單文件版本

如果你只是想先跑起來,可以把下面代碼保存為 app.py。

import sys
from dataclasses import dataclass

from PyQt6.QtCore import QModelIndex, QObject, Qt, pyqtSignal
from PyQt6.QtGui import QStandardItem, QStandardItemModel
from PyQt6.QtWidgets import (
    QApplication,
    QHBoxLayout,
    QLabel,
    QLineEdit,
    QListView,
    QMainWindow,
    QPushButton,
    QVBoxLayout,
    QWidget,
)


@dataclass
class TodoItem:
    title: str
    done: bool = False


class TodoListViewModel(QObject):
    message_changed = pyqtSignal(str)
    stats_changed = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self._items: list[TodoItem] = []
        self.list_model = QStandardItemModel()
        self.list_model.itemChanged.connect(self._on_item_changed)
        self._update_stats()

    def add_task(self, title: str) -> bool:
        title = title.strip()
        if not title:
            self.message_changed.emit("任務(wù)名稱不能為空")
            return False

        todo = TodoItem(title=title)
        self._items.append(todo)

        qt_item = QStandardItem(todo.title)
        qt_item.setEditable(False)
        qt_item.setCheckable(True)
        qt_item.setCheckState(Qt.CheckState.Unchecked)
        qt_item.setData(todo, Qt.ItemDataRole.UserRole)
        self.list_model.appendRow(qt_item)

        self.message_changed.emit(f"已添加任務(wù):{title}")
        self._update_stats()
        return True

    def remove_task(self, row: int) -> None:
        if row < 0 or row >= len(self._items):
            self.message_changed.emit("請(qǐng)先選擇要?jiǎng)h除的任務(wù)")
            return

        removed = self._items.pop(row)
        self.list_model.removeRow(row)
        self.message_changed.emit(f"已刪除任務(wù):{removed.title}")
        self._update_stats()

    def toggle_task(self, row: int) -> None:
        if row < 0 or row >= len(self._items):
            return

        todo = self._items[row]
        todo.done = not todo.done

        qt_item = self.list_model.item(row)
        qt_item.setCheckState(
            Qt.CheckState.Checked if todo.done else Qt.CheckState.Unchecked
        )

        self.message_changed.emit(
            f"已完成:{todo.title}" if todo.done else f"已取消完成:{todo.title}"
        )
        self._update_stats()

    def _on_item_changed(self, qt_item: QStandardItem) -> None:
        row = qt_item.row()
        if row < 0 or row >= len(self._items):
            return

        self._items[row].done = qt_item.checkState() == Qt.CheckState.Checked
        self._update_stats()

    def _update_stats(self) -> None:
        total = len(self._items)
        done = sum(1 for item in self._items if item.done)
        pending = total - done
        self.stats_changed.emit(f"總數(shù):{total},已完成:{done},未完成:{pending}")


class MainWindow(QMainWindow):
    def __init__(self, view_model: TodoListViewModel):
        super().__init__()
        self.view_model = view_model

        self.setWindowTitle("PyQt6 + MVVM 待辦事項(xiàng)")
        self.resize(520, 360)

        self.task_input = QLineEdit()
        self.task_input.setPlaceholderText("輸入任務(wù)名稱")

        self.add_button = QPushButton("添加")
        self.remove_button = QPushButton("刪除選中")
        self.remove_button.setEnabled(False)

        self.todo_list = QListView()
        self.todo_list.setModel(self.view_model.list_model)

        self.stats_label = QLabel()
        self.message_label = QLabel("請(qǐng)輸入任務(wù)名稱,然后點(diǎn)擊添加")

        self._build_layout()
        self._bind_events()
        self._bind_view_model()

    def _build_layout(self) -> None:
        input_layout = QHBoxLayout()
        input_layout.addWidget(self.task_input)
        input_layout.addWidget(self.add_button)

        button_layout = QHBoxLayout()
        button_layout.addStretch()
        button_layout.addWidget(self.remove_button)

        root_layout = QVBoxLayout()
        root_layout.addLayout(input_layout)
        root_layout.addWidget(self.todo_list)
        root_layout.addLayout(button_layout)
        root_layout.addWidget(self.stats_label)
        root_layout.addWidget(self.message_label)

        container = QWidget()
        container.setLayout(root_layout)
        self.setCentralWidget(container)

    def _bind_events(self) -> None:
        self.add_button.clicked.connect(self._on_add_clicked)
        self.task_input.returnPressed.connect(self._on_add_clicked)
        self.remove_button.clicked.connect(self._on_remove_clicked)
        self.todo_list.doubleClicked.connect(self._on_task_double_clicked)
        self.todo_list.selectionModel().currentChanged.connect(self._on_selection_changed)

    def _bind_view_model(self) -> None:
        self.view_model.message_changed.connect(self.message_label.setText)
        self.view_model.stats_changed.connect(self.stats_label.setText)

    def _on_add_clicked(self) -> None:
        ok = self.view_model.add_task(self.task_input.text())
        if ok:
            self.task_input.clear()
            self.task_input.setFocus()

    def _on_remove_clicked(self) -> None:
        self.view_model.remove_task(self.todo_list.currentIndex().row())

    def _on_task_double_clicked(self, index: QModelIndex) -> None:
        self.view_model.toggle_task(index.row())

    def _on_selection_changed(self, current: QModelIndex) -> None:
        self.remove_button.setEnabled(current.isValid())


def main() -> int:
    app = QApplication(sys.argv)
    view_model = TodoListViewModel()
    window = MainWindow(view_model)
    window.show()
    return app.exec()


if __name__ == "__main__":
    raise SystemExit(main())

運(yùn)行:

python app.py

十一、這個(gè)例子里的 MVVM 關(guān)系

可以用一張簡(jiǎn)單的關(guān)系圖理解:

用戶點(diǎn)擊按鈕
    |
    v
View(MainWindow)
    |
    v
ViewModel(add_task / remove_task / toggle_task)
    |
    v
Model(TodoItem)
    |
    v
ViewModel 發(fā)出 message_changed / stats_changed 信號(hào)
    |
    v
View 更新 QLabel / QListView

關(guān)鍵點(diǎn)是:View 不直接處理業(yè)務(wù)。

比如添加任務(wù)時(shí),View 只做:

ok = self.view_model.add_task(self.task_input.text())

至于:

  • 標(biāo)題能不能為空。
  • 添加到哪里。
  • 統(tǒng)計(jì)信息怎么更新。
  • 錯(cuò)誤信息怎么提示。

這些都放在 ViewModel 中。

十二、為什么不用 MainWindow 直接寫完

當(dāng)然可以直接寫在 MainWindow 里:

def on_add_clicked(self):
    title = self.task_input.text().strip()
    if not title:
        self.message_label.setText("不能為空")
        return
    self.items.append(title)
    self.list_widget.addItem(title)

小工具沒問題。

但項(xiàng)目一旦變復(fù)雜,就會(huì)出現(xiàn)這些問題:

  1. 窗口類越來越長。
  2. 業(yè)務(wù)邏輯和界面邏輯混在一起。
  3. 很難寫單元測(cè)試。
  4. 多個(gè)窗口復(fù)用同一套邏輯很麻煩。
  5. UI 改版時(shí)容易誤傷業(yè)務(wù)邏輯。

MVVM 的價(jià)值不是讓代碼變少,而是讓代碼邊界更清楚。

十三、初學(xué)者常見誤區(qū)

誤區(qū) 1:ViewModel 里不能出現(xiàn)任何 Qt 類型

在 PyQt 項(xiàng)目里,ViewModel 繼承 QObject,使用 pyqtSignal 是很常見的做法。它確實(shí)依賴 Qt,但它不應(yīng)該依賴具體窗口控件。

可以接受:

class UserViewModel(QObject):
    message_changed = pyqtSignal(str)

不建議:

class UserViewModel(QObject):
    def __init__(self, label: QLabel):
        self.label = label

前者是用 Qt 的通信機(jī)制,后者是直接操控界面控件。

誤區(qū) 2:用了 QStandardItemModel 就等于用了 MVVM

不是。

QStandardItemModel 是 Qt 的界面數(shù)據(jù)模型,主要服務(wù)于 QListView、QTableViewQTreeView。

MVVM 的重點(diǎn)是:

  • 業(yè)務(wù)數(shù)據(jù)放在哪里。
  • 用戶命令放在哪里。
  • View 和業(yè)務(wù)邏輯是否解耦。
  • 狀態(tài)變化如何通知界面。

誤區(qū) 3:所有邏輯都要拆得很細(xì)

也不是。

如果只是一個(gè) 200 行的小工具,沒必要強(qiáng)行拆 10 個(gè)文件??梢韵扔脝挝募姹緦懬宄?/p>

  • Model 類。
  • ViewModel 類。
  • View 類。

等功能變多,再拆目錄。

十四、繼續(xù)擴(kuò)展這個(gè) demo

這個(gè) demo 可以繼續(xù)擴(kuò)展成更像真實(shí)項(xiàng)目的結(jié)構(gòu):

  1. 加入文件保存:把任務(wù)列表保存為 JSON。
  2. 加入數(shù)據(jù)庫:用 SQLite 保存任務(wù)。
  3. 加入搜索框:View 調(diào)用 ViewModel 的過濾方法。
  4. 加入任務(wù)優(yōu)先級(jí):Model 增加 priority 字段。
  5. 加入多窗口:多個(gè) View 共用同一個(gè) ViewModel 或共享 Service。
  6. 加入單元測(cè)試:直接測(cè)試 ViewModel 的添加、刪除、統(tǒng)計(jì)邏輯。

例如,保存 JSON 時(shí)可以加一個(gè) services/todo_repository.py

import json
from pathlib import Path

from models.todo_item import TodoItem


class TodoRepository:
    def __init__(self, path: str = "todos.json"):
        self.path = Path(path)

    def load(self) -> list[TodoItem]:
        if not self.path.exists():
            return []

        data = json.loads(self.path.read_text(encoding="utf-8"))
        return [TodoItem(**item) for item in data]

    def save(self, items: list[TodoItem]) -> None:
        data = [item.__dict__ for item in items]
        self.path.write_text(
            json.dumps(data, ensure_ascii=False, indent=2),
            encoding="utf-8",
        )

然后 ViewModel 依賴 Repository,而不是在 View 里讀寫文件。

十五、總結(jié)

使用 PyQt 開發(fā)桌面應(yīng)用時(shí),最容易犯的錯(cuò)誤是把所有代碼都塞進(jìn)窗口類。短期看寫得快,長期看維護(hù)成本會(huì)越來越高。

MVVM 的核心價(jià)值是分層:

  • Model:保存業(yè)務(wù)數(shù)據(jù)。
  • View:顯示界面,接收用戶操作。
  • ViewModel:處理界面命令、業(yè)務(wù)狀態(tài)和信號(hào)通知。

在 PyQt 中,可以使用:

  • QObject
  • pyqtSignal
  • QStandardItemModel
  • QListView
  • QMainWindow

來搭建一套簡(jiǎn)單但清晰的 MVVM 架構(gòu)。

對(duì)于 Python 開發(fā)者來說,剛開始不需要追求完美架構(gòu)。建議先記住一句話:

讓窗口類少寫業(yè)務(wù)邏輯,讓 ViewModel 管狀態(tài)和命令。

只要做到這一點(diǎn),你的 PyQt 項(xiàng)目就會(huì)比“所有代碼都寫在 MainWindow 里”更容易維護(hù)。

以上就是基于Python+PyQt+MVVM方式開發(fā)桌面應(yīng)用的詳細(xì)內(nèi)容,更多關(guān)于Python PyQt MVVM桌面應(yīng)用的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 深入Python Tkinter 模塊

    深入Python Tkinter 模塊

    本文介紹Python Tkinter模塊,強(qiáng)調(diào)其輕量、跨平臺(tái)、無依賴的優(yōu)勢(shì),適用于快速開發(fā)無需復(fù)雜環(huán)境配置的桌面工具,通過布局、事件回調(diào)、樣式美化等核心技巧,結(jié)合PyInstaller打包,實(shí)現(xiàn)從腳本到可交付程序的完整流程,適合制作簡(jiǎn)單實(shí)用的小工具,感興趣的朋友跟隨小編一起看看吧
    2025-08-08
  • 使用python將csv數(shù)據(jù)導(dǎo)入mysql數(shù)據(jù)庫

    使用python將csv數(shù)據(jù)導(dǎo)入mysql數(shù)據(jù)庫

    這篇文章主要為大家詳細(xì)介紹了如何使用python將csv數(shù)據(jù)導(dǎo)入mysql數(shù)據(jù)庫,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-05-05
  • Python Pandas list列表數(shù)據(jù)列拆分成多行的方法實(shí)現(xiàn)

    Python Pandas list列表數(shù)據(jù)列拆分成多行的方法實(shí)現(xiàn)

    這篇文章主要介紹了Python Pandas list(列表)數(shù)據(jù)列拆分成多行的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • python使用opencv實(shí)現(xiàn)馬賽克效果示例

    python使用opencv實(shí)現(xiàn)馬賽克效果示例

    這篇文章主要介紹了python使用opencv實(shí)現(xiàn)馬賽克效果,結(jié)合實(shí)例形式分析了Python使用cv2模塊操作圖片實(shí)現(xiàn)馬賽克效果的相關(guān)技巧,需要的朋友可以參考下
    2019-09-09
  • TensorFlow-GPU完美安裝與配置的實(shí)現(xiàn)步驟

    TensorFlow-GPU完美安裝與配置的實(shí)現(xiàn)步驟

    本文主要介紹了TensorFlow-GPU的兩種安裝方法,推薦通過清華鏡像下載whl文件進(jìn)行高效安裝,并提供版本升級(jí)、路徑查詢和環(huán)境配置步驟,感興趣的可以了解一下
    2026-04-04
  • Python數(shù)組與列表的區(qū)別解析

    Python數(shù)組與列表的區(qū)別解析

    列表因?yàn)槠浯鎯?chǔ)的類型可以是任何對(duì)象,因此列表的用處更廣泛,更多樣化,并且列表可以有更多的存儲(chǔ)空間去使用,而數(shù)組使用的空間就相對(duì)較少,這篇文章主要介紹了Python數(shù)組與列表的區(qū)別,需要的朋友可以參考下
    2023-11-11
  • python中語法糖的妙用分享

    python中語法糖的妙用分享

    在Python中,語法糖是指一些為了增加代碼的可讀性、易用性和簡(jiǎn)潔性而添加的特性,本文為大家整理了一些python中常見語法糖的使用,需要的小伙伴可以了解下
    2023-11-11
  • python?pdfplumber庫批量提取pdf表格數(shù)據(jù)轉(zhuǎn)換為excel

    python?pdfplumber庫批量提取pdf表格數(shù)據(jù)轉(zhuǎn)換為excel

    這篇文章主要為大家介紹了python使用pdfplumber庫批量提取pdf表格數(shù)據(jù)轉(zhuǎn)換為excel格式的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • Python利用神經(jīng)網(wǎng)絡(luò)解決非線性回歸問題實(shí)例詳解

    Python利用神經(jīng)網(wǎng)絡(luò)解決非線性回歸問題實(shí)例詳解

    這篇文章主要介紹了Python利用神經(jīng)網(wǎng)絡(luò)解決非線性回歸問題,結(jié)合實(shí)例形式詳細(xì)分析了Python使用神經(jīng)網(wǎng)絡(luò)解決非線性回歸問題的相關(guān)原理與實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2019-07-07
  • Django模板導(dǎo)入母版繼承和自定義返回Html片段過程解析

    Django模板導(dǎo)入母版繼承和自定義返回Html片段過程解析

    這篇文章主要介紹了Django模板導(dǎo)入母版繼承和自定義返回Html片段過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09

最新評(píng)論

柳林县| 衢州市| 扶绥县| 新源县| 吉林市| 东莞市| 五华县| 贵阳市| 万源市| 赫章县| 澜沧| 阳江市| 清原| 荣成市| 苏尼特右旗| 南投县| 集安市| 平远县| 民勤县| 三台县| 五寨县| 潍坊市| 沐川县| 甘南县| 左贡县| 新余市| 仁化县| 巢湖市| 安康市| 阿图什市| 鞍山市| 盱眙县| 曲水县| 英吉沙县| 互助| 军事| 汉川市| 伊宁市| 太仓市| 灵寿县| 寿光市|