QML中访问c++数据,并实现类似C#中mvvm模式详细方法

news2025/4/15 22:40:47
  • 1. 背景需求
  • 2. 实现步骤
    • 2.1. 定义 Model(数据模型)
      • 2.1.1. DataModel.h
      • 2.1.2. DataModel.cpp
    • 2.2. 定义 ViewModel(视图模型)
      • 2.2.1. PersonViewModel.h
      • 2.2.2. PersonViewModel.cpp
    • 2.3. 在 QML 中使用 ViewModel
      • 2.3.1. main.cpp
    • 2.4. QML 文件
      • 2.4.1. main.qml
    • 2.5. 运行效果
      • 2.5.1. 总结

1. 背景需求

在 QML 中加载 C++ 数据是一种常见的需求,尤其是在需要处理复杂逻辑或调用系统功能时。通过将 C++ 数据和功能暴露给 QML,可以充分利用 Qt 的强大功能。

在C#开发有代码后置的概念,比如MVVM模式,这种做法有助于将界面展示逻辑与业务逻辑分离,提高代码的可维护性和可测试性。

QML中通常通过在QML文件中引用外部JavaScript文件或C++类来实现。可以将逻辑代码放在单独的 JavaScript 文件中,或者使用 C++ 来实现更复杂的逻辑。这样也能让 QML 代码保持简洁。称之为代码后置

关于MVVM设计模式的实现,QML非常适合实现,因为它支持数据绑定,可以轻松地将视图与视图模型连接起来。以下是一个简单的示例,展示如何在QML中应用MVVM模式:

好的,以下是一个使用 C++ 来定义 Model 和 ViewModel,并在 QML 中使用的完整示例。

2. 实现步骤

2.1. 定义 Model(数据模型)

Model 是数据的来源,通常是一个简单的类,用于存储和管理数据。

2.1.1. DataModel.h


#ifndef DATAMODEL_H

#define DATAMODEL_H



#include <QObject>

#include <QString>



class DataModel : public QObject {

    Q_OBJECT
    Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
    Q_PROPERTY(int age READ age WRITE setAge NOTIFY ageChanged)


public:

    explicit DataModel(QObject *parent = nullptr);


    QString name() const;
    void setName(const QString &name);


    int age() const;
    void setAge(int age);


signals:

    void nameChanged();
    void ageChanged();


private:

    QString m_name;
    int m_age;
};



#endif // DATAMODEL_H

2.1.2. DataModel.cpp


#include "DataModel.h"



DataModel::DataModel(QObject *parent) : QObject(parent) {

    m_name="";
    m_age=0;
}



QString DataModel::name() const {

    return m_name;
}



void DataModel::setName(const QString &name) {

    if (m_name != name) {
        m_name = name;
        emit nameChanged();
    }
}



int DataModel::age() const {

    return m_age;
}



void DataModel::setAge(int age) {

    if (m_age != age) {
        m_age = age;
        emit ageChanged();
    }
}

2.2. 定义 ViewModel(视图模型)

ViewModel 是 Model 和 View 之间的桥梁,负责处理业务逻辑和数据绑定。

2.2.1. PersonViewModel.h


#ifndef PERSONVIEWMODEL_H

#define PERSONVIEWMODEL_H



#include <QObject>

#include "DataModel.h"



class PersonViewModel : public QObject {

    Q_OBJECT
    Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
    Q_PROPERTY(int age READ age WRITE setAge NOTIFY ageChanged)


public:

    explicit PersonViewModel(QObject *parent = nullptr);


    QString name() const;
    void setName(const QString &name);


    int age() const;
    void setAge(int age);


signals:

    void nameChanged();
    void ageChanged();


private:

    DataModel *m_model;
};



#endif // PERSONVIEWMODEL_H

2.2.2. PersonViewModel.cpp


#include "PersonViewModel.h"



PersonViewModel::PersonViewModel(QObject *parent) : QObject(parent), m_model(new DataModel(this)) {}



QString PersonViewModel::name() const {

    return m_model->name();
}



void PersonViewModel::setName(const QString &name) {

    m_model->setName(name);
    emit nameChanged();
}



int PersonViewModel::age() const {

    return m_model->age();
}



void PersonViewModel::setAge(int age) {

    m_model->setAge(age);
    emit ageChanged();
}

2.3. 在 QML 中使用 ViewModel

main.cpp 中,将 PersonViewModel 注册到 QML 引擎中,使其可以在 QML 文件中使用。

2.3.1. main.cpp


#include <QGuiApplication>

#include <QQmlApplicationEngine>

#include <QQmlContext>

#include "PersonViewModel.h"



int main(int argc, char *argv[]) {

    QGuiApplication app(argc, argv);


    QQmlApplicationEngine engine;


    // 创建 PersonViewModel 实例并注册到 QML
    PersonViewModel viewModel;
    engine.rootContext()->setContextProperty("viewModel", &viewModel);


    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;


    return app.exec();
}

2.4. QML 文件

在 QML 文件中,通过 viewModel 对象访问和更新数据。

2.4.1. main.qml


import QtQuick 2.15

import QtQuick.Controls 2.15



ApplicationWindow {

    visible: true
    width: 400
    height: 300
    title: "MVVM Example"


    Column {
        anchors.centerIn: parent
        spacing: 10


        Text {
            text: "Name: " + viewModel.name
        }


        TextField {
            placeholderText: "Enter name"
            text: viewModel.name
            onEditingFinished: viewModel.name = text
        }


        Text {
            text: "Age: " + viewModel.age
        }


        TextField {
            placeholderText: "Enter age"
            text: viewModel.age.toString()
            onEditingFinished: viewModel.age = parseInt(text)
        }
    }
}

2.5. 运行效果

  1. ViewModelPersonViewModel 提供了 nameage 属性,并通过 Q_PROPERTY 定义了这些属性的 getter 和 setter 方法。
  2. ModelDataModel 存储了实际的数据,并通过信号通知 ViewModel 数据的变化。
  3. View:QML 文件通过 viewModel 对象访问和更新数据,实现了双向数据绑定。

当你运行程序时,你可以通过输入框修改 nameage 的值,这些值会实时更新到 Model 中,并反映在界面上。

2.5.1. 总结

通过这种方式,你可以在 QML 中实现类似于 C# 的 MVVM 模式,将数据(Model)、业务逻辑(ViewModel)和用户界面(View)分离,提高代码的可维护性和可测试性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2334631.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

React 获得dom节点和组件通信

通过REF 实例对象的.current属性获得绑定的DOM节点 组件通信 组件通信 1 父传子 父组件传递数据 子组件接受数据 通过pros对象接受 子组件的形参列表props只读 props中数据不可修改 特殊情况 在子传父的过程中没有直接给子组件添加属性&#xff0c;而是向父组件中添加其他…

代码,Java Maven项目打包遇到的环境问题

这几天在写一些Java版本的Langchain4J的 AI 测试case&#xff0c;有一段时间不运行的Java环境&#xff0c;反复出现环境问题&#xff0c;记录下 1、Java编译版本的问题 修改编译版本&#xff1a; 2、在IDE中运行遇到Maven中JDK版本问题 在ide中执行maven命令&#xff0c;遇到下…

fisco-bcos 关于服务bash status.sh启动runing 中但是5002端口监听不到,出错的问题

bash status.sh Server com.webank.webase.front.Application Port 5002 is running PID(4587) yjmyjm-VMware-Virtual-Platform:~/webase-front$ sudo netstat -anlp | grep 5002 没有端口信息输出 此时可以查看log文件夹下的WeBASE-front.log&#xff0c;找到报错信息如下…

linux多线(进)程编程——(5)虚拟内存与内存映射

前言&#xff08;前情回顾&#xff09; 进程君开发了管道这门技术后&#xff0c;修真界的各种沟通越来越频繁&#xff0c;这天进程君正与自己的孩子沟通&#xff0c;进程君的孩子说道&#xff1a; “爸爸&#xff0c;昨天我看他们斗法&#xff0c;小明一拳打到了小刚的肚子上&…

SpringBoot 动态路由菜单 权限系统开发 菜单权限 数据库设计 不同角色对应不同权限

介绍 系统中的路由配置可以根据用户的身份、角色或其他权限信息动态生成&#xff0c;而不是固定在系统中。不同的用户根据其权限会看到不同的路由&#xff0c;访问不同的页面。对应各部门不同的权限。 效果 [{"id": 1,"menuName": "用户管理"…

[dp8_子数组] 乘积为正数的最长子数组长度 | 等差数列划分 | 最长湍流子数组

目录 1.乘积为正数的最长子数组长度 2.等差数列划分 3.最长湍流子数组 写代码做到&#xff0c;只用维护好自己的一小步 1.乘积为正数的最长子数组长度 链接&#xff1a;1567. 乘积为正数的最长子数组长度 给你一个整数数组 nums &#xff0c;请你求出乘积为正数的最长子数…

【图像处理基石】什么是通透感?

一、画面的通透感定义 画面的通透感指图像在色彩鲜明度、空间层次感、物体轮廓清晰度三方面的综合表现&#xff0c;具体表现为&#xff1a; 色彩鲜明&#xff1a;颜色纯净且饱和度适中&#xff0c;无灰暗或浑浊感&#xff1b;层次分明&#xff1a;明暗过渡自然&#xff0c;光…

无锡无人机超视距驾驶证怎么考?

无锡无人机超视距驾驶证怎么考&#xff1f;在近年来&#xff0c;无人机技术的迅猛发展使得无人机的应用场景变得愈发广泛&#xff0c;其不仅在环境监测、农业喷洒、快递配送等领域展现出真金白银的价值&#xff0c;同时也推动了无人机驾驶证的需求。尤其是在无锡&#xff0c;随…

213、【图论】有向图的完全联通(Python)

题目描述 原题链接&#xff1a;105. 有向图的完全联通 代码实现 import collectionsn, k list(map(int, input().split())) adjacency collections.defaultdict(list) for _ in range(k):head, tail list(map(int, input().split()))adjacency[head].append(tail)visited_…

图像形态学操作对比(Opencv)

形态学基于图像的形状进行操作&#xff0c;用于处理二值化图像&#xff0c;主要包括腐蚀和膨胀两种基本操作。这些操作通常用于去除噪声、分隔或连接相邻的元素以及寻找图像中显著的最大点和最小点。 1. 形态学操作 import cv2 import numpy as np import matplotlib.pyplot …

复刻系列-星穹铁道 3.2 版本先行展示页

复刻星穹铁道 3.2 版本先行展示页 0. 视频 手搓&#xff5e;星穹铁道&#xff5e;展示页&#xff5e;&#xff5e;&#xff5e; 1. 基本信息 作者: 啊是特嗷桃系列: 复刻系列官方的网站: 《崩坏&#xff1a;星穹铁道》3.2版本「走过安眠地的花丛」专题展示页现已上线复刻的网…

Linux:进程理解1(查看进程,创造进程,进程状态)

进程理解 &#xff08;一&#xff09;查看进程通过系统调用获取进程标示* &#xff08;二&#xff09;创造进程&#xff08;fork&#xff09;1. 创造的子进程的PCB代码数据怎么来&#xff1f;2.一个函数为什么有两个返回值&#xff1f;3. 为什么这里会有 两个 id值&#xff1f;…

异形遮罩之QML中的 `OpacityMask` 实战

文章目录 &#x1f327;️ 传统实现的问题&#x1f449; 效果图 &#x1f308; 使用 OpacityMask 的理想方案&#x1f449;代码如下&#x1f3af; 最终效果&#xff1a; ✨ 延伸应用&#x1f9e0; 总结 在 UI 设计中&#xff0c;经常希望实现一些“异形区域”拥有统一透明度或颜…

如何为您的设计应用选择高速连接器

电气应用的设计过程需要考虑诸多因素&#xff0c;尤其是在设计高速网络时。许多连接器用户可能没有意识到&#xff0c;除了在两个互连之间组装导电线路之外&#xff0c;还需要考虑各种工艺。在建立高速连接并确保适当的信号完整性时&#xff0c;必须考虑蚀刻、公差、屏蔽等因素…

【论文阅读】UniAD: Planning-oriented Autonomous Driving

一、Introduction 传统的无人驾驶采用了区分子模块的设计&#xff0c;即将无人驾驶拆分为感知规划控制三个模块&#xff0c;这虽然能够让无人驾驶以一个很清晰的结构实现&#xff0c;但是感知的结果在传达到规划部分的时候&#xff0c;会导致部分信息丢失&#xff0c;这势必会…

upload-labs二次打

1(前端js绕过) 弹窗&#xff0c;先看看有没有js有&#xff0c;禁用js 禁用后就可以上传php文件了&#xff0c;然后我们就去访问文件&#xff0c;成功 2&#xff08;MIME绕过&#xff09; 先上传一个php文件试试&#xff0c;不行&#xff0c;.htaccess不行, 试试MIME类型&am…

提权实战!

就是提升权限&#xff0c;当我们拿到一个shell权限较低&#xff0c;当满足MySQL提权的要求时&#xff0c;就可以进行这个提权。 MySQL数据库提权&#xff08;Privilege Escalation&#xff09;是指攻击者通过技术手段&#xff0c;从低权限的数据库用户提升到更高权限&#xff…

ChromeOS 135 版本更新

ChromeOS 135 版本更新 一、ChromeOS 135 更新内容 1. ChromeOS 电池寿命优化策略 为了延长 Chromebook 的使用寿命&#xff0c;ChromeOS 135 引入了一项全新的电池充电限制策略 —— DevicePowerBatteryChargingOptimization&#xff0c;可提供更多充电优化选项&#xff0c…

javaSE.Lambda表达式

如果一个接口中有且只有一个待实现的抽象方法&#xff0c;那么我们可以将匿名内部类简写为Lambda表达式。 简写规则 标准格式&#xff1a; &#xff08;【参数类型 参数名称&#xff0c;】...&#xff09; -> {代码语句&#xff0c; 包括返回值} 只有一行花括号{}可以省略。…

【随身wifi】青龙面板保姆级教程

0.操作前必看 本教程基于Debian系统&#xff0c;从Docker环境。面板安装&#xff0c;到最后拉取脚本的使用。 可以拉库跑狗东京豆&#xff0c;elm红包等等&#xff0c;也可以跑写自己写的脚本&#xff0c;自行探索 重要的号别搞&#xff0c;容易黑号&#xff0c;黑号自己负责…