一个简单的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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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…