一個簡單的mvvm示例與數據雙向綁定

這就是一個簡單的數據雙向綁定的demo,參考即可(cmakelist我沒按他的寫,但是大差不差)

目錄

1.示例demo

File: CMakeLists.txt

File: main.cpp

File: model/physiologymodel.cpp

File: viewmodel/physiologyviewmodel.h

File: viewmodel/physiologyviewmodel.cpp

File: qml/main.qml

2 雙向綁定詳解

1. QML 端綁定?heartRate?并觸發?setter

2. ViewModel 中定義?heartRate?屬性并傳值給?Model

3. Model 中設置值并發出信號

4. ViewModel 監聽?Model 信號并轉發

5. QML 端自動刷新綁定控件

參考


序章:目錄結構

MVVMDemo/
│
├── CMakeLists.txt
├── main.cpp
├── model/
│   └── physiologymodel.{h,cpp}
├── viewmodel/
│   └── physiologyviewmodel.{h,cpp}
└── qml/└── main.qml

主要是實現一個簡單的兩個數據在view上顯示并同步后端model數據。

1.demo代碼詳細


File: CMakeLists.txt

// ============================
// File: CMakeLists.txt
// ============================
cmake_minimum_required(VERSION 3.16)
project(MVVMDemo LANGUAGES CXX)set(CMAKE_CXX_STANDARD 17)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)find_package(Qt6 REQUIRED COMPONENTS Core Quick Qml)add_executable(MVVMDemomain.cppmodel/physiologymodel.cppviewmodel/physiologyviewmodel.cpp
)target_include_directories(MVVMDemo PRIVATE${CMAKE_CURRENT_SOURCE_DIR}/model${CMAKE_CURRENT_SOURCE_DIR}/viewmodel
)target_link_libraries(MVVMDemo PRIVATE Qt6::Core Qt6::Quick Qt6::Qml)qt6_add_resources(MVVMDemo "qml"PREFIX "/"FILESqml/main.qml
)

File: main.cpp


// ============================
// File: main.cpp
// ============================
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>#include "physiologyviewmodel.h"int main(int argc, char *argv[]) {QGuiApplication app(argc, argv);PhysiologyModel model;PhysiologyViewModel viewModel(&model);QQmlApplicationEngine engine;engine.rootContext()->setContextProperty("physiologyVM", &viewModel);const QUrl url(QStringLiteral("qrc:/qml/main.qml"));engine.load(url);if (engine.rootObjects().isEmpty())return -1;return app.exec();
}

?File: model/physiologymodel.h

// ============================
// File: model/physiologymodel.h
// ============================
#ifndef PHYSIOLOGYMODEL_H
#define PHYSIOLOGYMODEL_H#include <QObject>class PhysiologyModel : public QObject {Q_OBJECT
public:explicit PhysiologyModel(QObject *parent = nullptr);float heartRate() const;float respirationRate() const;void setHeartRate(float rate);void setRespirationRate(float rate);signals:void heartRateChanged();void respirationRateChanged();private:float m_heartRate;float m_respirationRate;
};#endif // PHYSIOLOGYMODEL_H

File: model/physiologymodel.cpp

// ============================
// File: model/physiologymodel.cpp
// ============================
#include "physiologymodel.h"PhysiologyModel::PhysiologyModel(QObject *parent): QObject(parent), m_heartRate(70.0f), m_respirationRate(16.0f) {}float PhysiologyModel::heartRate() const {return m_heartRate;
}float PhysiologyModel::respirationRate() const {return m_respirationRate;
}void PhysiologyModel::setHeartRate(float rate) {if (!qFuzzyCompare(rate, m_heartRate)) {m_heartRate = rate;emit heartRateChanged();}
}void PhysiologyModel::setRespirationRate(float rate) {if (!qFuzzyCompare(rate, m_respirationRate)) {m_respirationRate = rate;emit respirationRateChanged();}
}

File: viewmodel/physiologyviewmodel.h

// ============================
// File: viewmodel/physiologyviewmodel.h
// ============================
#ifndef PHYSIOLOGYVIEWMODEL_H
#define PHYSIOLOGYVIEWMODEL_H#include <QObject>
#include "physiologymodel.h"class PhysiologyViewModel : public QObject {Q_OBJECTQ_PROPERTY(float heartRate READ heartRate WRITE setHeartRate NOTIFY heartRateChanged)Q_PROPERTY(float respirationRate READ respirationRate WRITE setRespirationRate NOTIFY respirationRateChanged)public:explicit PhysiologyViewModel(PhysiologyModel *model, QObject *parent = nullptr);float heartRate() const;float respirationRate() const;void setHeartRate(float rate);void setRespirationRate(float rate);Q_INVOKABLE void sendTrainingCommand();signals:void heartRateChanged();void respirationRateChanged();private:PhysiologyModel *m_model;
};#endif // PHYSIOLOGYVIEWMODEL_H

File: viewmodel/physiologyviewmodel.cpp

// ============================
// File: viewmodel/physiologyviewmodel.cpp
// ============================
#include "physiologyviewmodel.h"
#include <QDebug>PhysiologyViewModel::PhysiologyViewModel(PhysiologyModel *model, QObject *parent): QObject(parent), m_model(model) {connect(m_model, &PhysiologyModel::heartRateChanged, this, &PhysiologyViewModel::heartRateChanged);connect(m_model, &PhysiologyModel::respirationRateChanged, this, &PhysiologyViewModel::respirationRateChanged);
}float PhysiologyViewModel::heartRate() const {return m_model->heartRate();
}float PhysiologyViewModel::respirationRate() const {return m_model->respirationRate();
}void PhysiologyViewModel::setHeartRate(float rate) {m_model->setHeartRate(rate);
}void PhysiologyViewModel::setRespirationRate(float rate) {m_model->setRespirationRate(rate);
}void PhysiologyViewModel::sendTrainingCommand() {qDebug() << "Sending training command with heart rate:" << m_model->heartRate()<< "and respiration rate:" << m_model->respirationRate();// 在此加入 TCP 或串口發送指令的邏輯
}

File: qml/main.qml

// ============================
// File: qml/main.qml
// ============================
import QtQuick 2.15
import QtQuick.Controls 2.15ApplicationWindow {width: 400height: 300visible: truetitle: "Physiology Monitor"Column {anchors.centerIn: parentspacing: 20TextField {id: hrFieldwidth: 200placeholderText: "Heart Rate"text: physiologyVM.heartRate.toString()onTextChanged: physiologyVM.heartRate = parseFloat(text)}TextField {id: rrFieldwidth: 200placeholderText: "Respiration Rate"text: physiologyVM.respirationRate.toString()onTextChanged: physiologyVM.respirationRate = parseFloat(text)}Button {text: "Send Training Command"onClicked: physiologyVM.sendTrainingCommand()}Text {text: "HR: " + physiologyVM.heartRate + " bpm\nRR: " + physiologyVM.respirationRate + " rpm"font.pointSize: 14}}
}

2.雙向綁定詳解


參考gpt給的答案:Qt/QML MVVM 示例:heartRate?數據綁定流程

以心率為例
? ? [ QML TextField ]
? ? ? ? ? ↓
? ? 用戶輸入 → ViewModel.setHeartRate()
? ? ? ? ? ↓
? ? Model.setHeartRate()
? ? ? ? ? ↓
? ? ? ?emit heartRateChanged()
? ? ? ? ? ↓
? ? ViewModel emit heartRateChanged()
? ? ? ? ? ↓
? ? 所有綁定 QML Text 被自動刷新


1?? Step 1:在 ViewModel 中使用 Q_PROPERTY 暴露屬性

// physiologyviewmodel.h Q_PROPERTY(float heartRate READ heartRate WRITE setHeartRate NOTIFY heartRateChanged) 

這行的作用:

  • READ heartRate: 提供 getter(用于 QML 讀取值)

  • WRITE setHeartRate: 提供 setter(用于 QML 設置值)

  • NOTIFY heartRateChanged: 數據改變時觸發,QML 自動響應更新

👉 要點:信號名必須和 NOTIFY 后綴一致


2?? Step 2:實現 getter、setter 和信號

float PhysiologyViewModel::heartRate() const { return m_model->heartRate(); } void PhysiologyViewModel::setHeartRate(float rate) { m_model->setHeartRate(rate); // Model 處理并發出信號 }

在構造函數中連接 Model 信號到 ViewModel 信號:

connect(m_model, &PhysiologyModel::heartRateChanged, this, &PhysiologyViewModel::heartRateChanged);

這樣 Model 更新 → ViewModel 也通知 → QML 自動刷新。


3?? Step 3:把 ViewModel 注冊到 QML 上下文

// main.cpp engine.rootContext()->setContextProperty("physiologyVM", &viewModel);

這句代碼的作用:

  • 將 C++ 對象 viewModel 暴露為名為 "physiologyVM" 的上下文屬性

  • QML 中就可以通過 physiologyVM.heartRate 來訪問


4?? Step 4:QML 使用這個 ViewModel 進行數據綁定

TextField { text: physiologyVM.heartRate.toString() onTextChanged: physiologyVM.heartRate = parseFloat(text) }

含義:

  • 初始顯示:text 屬性綁定了 heartRate 的值

  • 用戶修改輸入框:onTextChanged 觸發 setter

  • setter 調用 Model → 如果值改變,Model 發出信號 → ViewModel 傳遞信號 → QML 自動更新

?? 注意:

  • 這是手動實現的雙向綁定,一般不這樣操作

  • 如果你使用 Bindings { ... }PropertyBinding,可以實現更自動的綁定機制(Qt 6 新增)

使用 Bindings {}

TextField {id: hrFieldwidth: 200placeholderText: "Heart Rate"// QML 自動和 C++ 的 heartRate 雙向同步Bindings {target: physiologyVMproperty: "heartRate"value: parseFloat(hrField.text)}// UI 自動反映 C++ 數值變更onEditingFinished: text = physiologyVM.heartRate.toString()
}

👆這個例子做了兩件事:

  1. hrField.text 改變 → physiologyVM.heartRate 會自動同步

  2. physiologyVM.heartRate 從 C++ 改變時 → 我們通過 onEditingFinished 顯示最新值


使用 PropertyBinding

(推薦用于綁定純 UI 屬性)

Rectangle {width: 200height: 50color: "lightblue"property int heartRate: 0// PropertyBinding 更適合綁定 UI 屬性PropertyBinding {target: heartRatevalue: physiologyVM.heartRate}Text {anchors.centerIn: parenttext: heartRate + " bpm"}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/917069.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/917069.shtml
英文地址,請注明出處:http://en.pswp.cn/news/917069.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

哈希的概念及其應用

哈希的概念及其應用哈希概念常見的哈希其他哈希字符串哈希&#xff08;算法競賽常用&#xff09;字符串哈希OJP3370 【模板】字符串哈希 - 洛谷P10468 兔子與兔子 - 洛谷哈希沖突哈希函數設計原則哈希沖突解決方法—閉散列閉散列的線性探測閉散列的二次探測哈希沖突解決方法—開…

【分布式的個人博客部署】

綜合項目-搭建個人博客一、運行環境二、基礎配置三、業務需求第一步&#xff1a;準備工作1、配置靜態IP2、修改hosts映射3、開啟防火墻4、時間同步5、配置免密ssh登錄第二步&#xff1a;環境搭建1、Server-web端安裝LNMP環境軟件2、Server-NFS-DNS端上傳博客軟件3、Server-NFS-…

藍橋杯----DS18B20溫度傳感器

&#xff08;二&#xff09;、溫度傳感器1、One-Wire總線One-Wire總線利用一根線實現雙向通信。因此其協議對時序的要求較嚴格&#xff0c;如應答等時序都有明確的時間要求。基本的時序包括復位及應答時序、寫一位時序讀一位時序。單總線即只有一根數據線&#xff0c;系統中的數…

科技賦能成長 腦力啟迪未來

——西安臻昊科技與秦嶺云數智共筑腦科學教育新生態 2025年6月26日&#xff0c;西安臻昊科技&#xff08;集團&#xff09;有限責任公司與秦嶺云數智&#xff08;陜西&#xff09;科技有限公司正式簽署腦象評測技術戰略合作協議&#xff0c;雙方將依托技術互補與資源協同&#…

Docker部署的PostgreSQL慢查詢日志配置指南

目錄 1. 核心步驟 1.1 修改配置文件 1.2 動態加載配置&#xff08;無需重啟容器&#xff09; 1.3 驗證配置生效 1.3.1 查看參數 1.3.2 執行測試慢查詢 2. 高級用法 2.1 使用分析工具 2.2 啟用擴展 3. 注意事項 3.1 日志目錄權限 3.2 性能影響 配置Docker部署的Pos…

C# 入門教程(四)委托詳解

文章目錄1、什么是委托2、委托的聲明&#xff08;自定義委托&#xff09;3、委托的使用3.1 實例:把方法當作參數傳給另一個方法3.2 注意:難精通易使用功能強大東西&#xff0c;一旦被濫用則后果非常嚴重4、委托的高級使用4.1 多播&#xff08;multicast&#xff09;委托4.2隱式…

React的基本語法和原理

3. React條件渲染某些情況下&#xff0c;姐妹的內容會根據不同的情況顯示不同的內容&#xff0c;或者決定是否渲染某部分內容&#xff1a; 在React中&#xff0c;所有的條件判斷和普通的JavaScript代碼一致&#xff1b;常見的條件渲染的方式有哪些&#xff1f;方式一&#xff1…

如何在 Gradle 項目中添加依賴?(以添加 AndroidX 版本的 RecyclerView 為例)

1. 確保項目已啟用 AndroidX RecyclerView 的現代版本屬于 AndroidX 庫&#xff0c;需確保項目已啟用 AndroidX&#xff1a; 在 gradle.properties 中應有以下配置&#xff08;通常新建項目默認開啟&#xff09;&#xff1a;android.useAndroidXtrue android.enableJetifiert…

深度學習與圖像處理 | 基于PaddlePaddle的梯度下降算法實現(線性回歸投資預測)

演示基于PaddlePaddle自動求導技術實現梯度下降&#xff0c;簡化求解過程。01、梯度下降法梯度下降法是機器學習領域非常重要和具有代表性的算法&#xff0c;它通過迭代計算來逐步尋找目標函數極小值。既然是一種迭代計算方法&#xff0c;那么最重要的就是往哪個方向迭代&#…

負載均衡集群HAproxy

HAProxy 簡介HAProxy 是一款高性能的負載均衡器和代理服務器&#xff0c;支持 TCP 和 HTTP 應用。廣泛用于高可用性集群&#xff0c;能夠有效分發流量到多個后端服務器&#xff0c;確保服務的穩定性和可擴展性。HAProxy 核心功能負載均衡&#xff1a;支持輪詢&#xff08;round…

重生之我在10天內卷贏C++ - DAY 1

坐穩了&#xff0c;我們的C重生之旅現在正式發車&#xff01;請系好安全帶&#xff0c;前方高能&#xff0c;但絕對有趣&#xff01;&#x1f680; 重生之我在10天內卷贏C - DAY 1導師寄語&#xff1a;嘿&#xff0c;未來的編程大神&#xff01;歡迎來到C的世界。我知道&#x…

[mind-elixir]Mind-Elixir 的交互增強:單擊、雙擊與鼠標 Hover 功能實現

[mind-elixir]Mind-Elixir 的交互增強&#xff1a;單擊、雙擊與鼠標 Hover 功能實現 功能簡述 通過防抖&#xff0c;實現單擊雙擊區分通過mousemove事件&#xff0c;實現hover效果 實現思路 &#xff08;一&#xff09;單擊與雙擊事件 功能描述 單擊節點時&#xff0c;可以觸發…

c++-迭代器類別仿函數常用算法函數

C常用算法函數 1. 前置知識 1.1 迭代器的類別 C中&#xff0c;迭代器是 STL 容器庫的核心組件之一&#xff0c;具有舉足輕重的作用&#xff0c;它提供了一種 統一的方式來訪問和遍歷容器&#xff0c;而無需關心底層數據結構的具體實現。迭代器類似指針&#xff0c;但比指針更通…

Python深度學習框架TensorFlow與Keras的實踐探索

基礎概念與安裝配置 TensorFlow核心架構解析 TensorFlow是由Google Brain團隊開發的開源深度學習框架&#xff0c;其核心架構包含數據流圖&#xff08;Data Flow Graph&#xff09;和張量計算系統。數據流圖通過節點表示運算操作&#xff08;如卷積、激活函數&#xff09;&…

c# net6.0+ 安裝中文智能提示

https://github.com/stratosblue/IntelliSenseLocalizer 1、安裝tool dotnet tool install -g islocalizer 2、 安裝IntelliSense 文件&#xff0c;安裝其他net版本修改下版本號 安裝中文net6.0采集包 islocalizer install auto -m net6.0 -l zh-cn 安裝中英文雙語net6.0采集包…

【建模與仿真】二階鄰居節點信息驅動的節點重要性排序算法

導讀&#xff1a; 在復雜網絡中&#xff0c;挖掘重要節點對精準推薦、交通管控、謠言控制和疾病遏制等應用至關重要。為此&#xff0c;本文提出一種局部信息驅動的節點重要性排序算法Leaky Noisy Integrate-and-Fire (LNIF)。該算法通過獲取節點的二階鄰居信息計算節點重要性&…

指令微調Qwen3實現文本分類任務

參考文檔&#xff1a; SwanLab入門深度學習&#xff1a;Qwen3大模型指令微調 - 肖祥 - 博客園 vLLM&#xff1a;讓大語言模型推理更高效的新一代引擎 —— 原理詳解一_vllm 原理-CSDN博客 概述 為了實現對100個標簽的多標簽文本分類任務&#xff0c;前期調用gpt-4o進行prom…

【機器學習-3】 | 決策樹與鳶尾花分類實踐篇

0 序言 本文將深入探討決策樹算法&#xff0c;先回顧下前邊的知識&#xff0c;從其基本概念、構建過程講起&#xff0c;帶你理解信息熵、信息增益等核心要點。 接著在引入新知識點&#xff0c;介紹Scikit - learn 庫中決策樹的實現與應用&#xff0c;再通過一個具體項目的方式來…

【數字投影】折幕影院都是沉浸式嗎?

折幕影院作為一種現代化的展示形式&#xff0c;其核心特點在于通過多塊屏幕拼接和投影融合技術&#xff0c;打造更具包圍感的視覺體驗。折幕影院設計通常采用多折幕結構&#xff0c;如三折幕、五折幕等&#xff0c;利用多臺投影機的協同工作&#xff0c;呈現無縫銜接的超大畫面…

數據結構——圖(三、圖的 廣度/深度 優先搜索)

一、廣度優先搜索(BFS)①找到與一個頂點相鄰的所有頂點 ②標記哪些頂點被訪問過 ③需要一個輔助隊列#define MaxVertexNum 100 bool visited[MaxVertexNum]; //訪問標記數組 void BFSTraverse(Graph G){ //對圖進行廣度優先遍歷&#xff0c;處理非連通圖的函數 for(int i0;i…