【QT Quick】C++扩展QML类型

news/2025/2/27 2:16:39/

本教程将教你如何在 Qt Quick 项目中使用 C++ 扩展 QML 类型,具体包括定义可被 QML 调用的类、配置支持混合开发的 CMake 项目,并演示如何在 QML 中使用这些类型,以一个包含 DemoController 类的示例项目为基础逐步讲解。

项目结构概览

在开始之前,先了解一下项目的文件结构。这些文件共同构成了一个完整的 Qt Quick 应用程序:

  • CMakeLists.txt:项目的构建配置文件,用于管理 C++ 和 QML 的编译。
  • main.cpp:程序的入口文件,负责启动应用程序并加载 QML 文件。
  • demo_controller.hdemo_controller.cpp:定义并实现了一个 C++ 类 DemoController,它将被 QML 调用。
  • qml.qrc:资源文件,用于将 QML 文件嵌入到程序中。
  • main.qml:QML 文件,定义了用户界面并使用了 C++ 扩展的类型。

使用 C++ 扩展 QML 类型

定义 C++ 类

要让一个 C++ 类能在 QML 中使用,必须满足以下要求:

  • 继承自 QObject,这是 Qt 元对象系统的核心类。
  • 使用 Q_OBJECT 宏,以便支持信号、槽和属性。
  • 使用 Q_PROPERTY 宏定义可以在 QML 中访问的属性(可选)。
  • 使用 Q_INVOKABLE 宏或 slots 关键字定义可以在 QML 中调用的方法。
  • 使用 QML_ELEMENT 宏将类注册为 QML 类型。

以下是 demo_controller.h 中的代码:

#pragma once#include <QObject>
#include <QQmlEngine>class DemoController : public QObject
{Q_OBJECTQ_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged REQUIRED)Q_PROPERTY(QString url MEMBER m_url NOTIFY urlChanged)QML_ELEMENT  // 注册为 QML 类型public:explicit DemoController(QObject *parent = nullptr);QString name() const;void setName(const QString &name);public slots:Q_INVOKABLE void performAction();Q_INVOKABLE QString fetchData(int index, const std::vector<int> &dataArray,const QVariantMap &dataMap);signals:void nameChanged();void urlChanged();void actionTriggered();private:QString m_url;
};
代码解释:
  • Q_OBJECT:启用 Qt 的元对象功能,必须放在类的私有部分顶部。
  • Q_PROPERTY
    • name:定义了一个属性,具有读取函数 (READ name)、写入函数 (WRITE setName) 和变更信号 (NOTIFY nameChanged)。
    • url:定义了一个属性,直接绑定到成员变量 m_url,并在值改变时发出 urlChanged 信号。
  • QML_ELEMENT:将类注册为 QML 类型,之后可以在 QML 中直接实例化。
  • 方法
    • performAction():一个简单的函数,标记为 Q_INVOKABLE,可在 QML 中调用。
    • fetchData():一个带参数的函数,处理数据并返回字符串。
  • 信号nameChangedurlChangedactionTriggered 是可以被 QML 监听的事件。

实现 C++ 类

接下来,在 demo_controller.cpp 中实现这些声明的功能:

#include "demo_controller.h"
#include <QDebug>DemoController::DemoController(QObject *parent) : QObject(parent)
{qDebug() << "控制器实例已创建";
}QString DemoController::name() const
{return objectName();
}void DemoController::setName(const QString &name)
{if (name == objectName()) return;setObjectName(name);emit nameChanged();
}void DemoController::performAction()
{qDebug() << "执行基础操作";emit actionTriggered();
}QString DemoController::fetchData(int index, const std::vector<int> &dataArray,const QVariantMap &dataMap)
{qDebug() << "获取数据,索引:" << index;qDebug() << "数组数据:";for (const auto &value : dataArray) {qDebug() << value;}qDebug() << "映射数据:";for (auto it = dataMap.begin(); it != dataMap.end(); ++it) {qDebug() << it.key() << ":" << it.value().toString();}return "数据处理完成";
}
代码解释:
  • 构造函数:初始化时输出一条调试信息。
  • name()setName():分别获取和设置 name 属性,使用 objectName() 存储值,改变时发出信号。
  • performAction():输出调试信息并触发 actionTriggered 信号。
  • fetchData():接收一个整数索引、一个整数向量和一个键值映射,打印这些数据并返回结果。

配置项目(CMakeLists.txt)

为了让 C++ 和 QML 协同工作,我们需要正确配置构建系统。这里使用的是 CMake:

cmake_minimum_required(VERSION 3.20)project(cpp_qml_module)set(CMAKE_PREFIX_PATH "C:/Qt/6.8.2/mingw_64")
find_package(Qt6 COMPONENTS Quick REQUIRED)set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)add_executable(${PROJECT_NAME}main.cppqml.qrcdemo_controller.hdemo_controller.cpp
)qt_add_qml_module(${PROJECT_NAME}URI Demo.Controller
)set(QML_IMPORT_PATH ${CMAKE_BINARY_DIR})target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Quick)
代码解释:
  • cmake_minimum_required:指定最低 CMake 版本。
  • project:定义项目名称。
  • set(CMAKE_PREFIX_PATH):设置 Qt 安装路径(需要根据你的环境调整)。
  • find_package:查找 Qt6 的 Quick 模块。
  • set(CMAKE_AUTOMOC ON)set(CMAKE_AUTORCC ON):自动处理 Qt 的元对象编译器(MOC)和资源文件。
  • add_executable:添加可执行目标,列出所有源文件。
  • qt_add_qml_module:注册 QML 模块,指定 URI 为 Demo.Controller
  • set(QML_IMPORT_PATH):设置 QML 模块的导入路径。
  • target_link_libraries:链接 Qt6 的 Quick 库。

在 QML 中使用 C++ 类型

现在,我们在 main.qml 中使用刚刚定义的 DemoController

import QtQuick
import QtQuick.Controls
import Demo.ControllerWindow {width: 800height: 600visible: truetitle: "C++/QML 集成演示"Column {spacing: 10padding: 20width: parent.widthDemoController {id: mainControllername: "初始名称"onNameChanged: console.log("名称变更:", name)onUrlChanged: console.log("URL 变更:", url)onActionTriggered: console.log("操作已触发")}Button {text: "显示当前名称"onClicked: console.log("当前名称:", mainController.name)}Button {text: "修改名称"property int clickCount: 0onClicked: mainController.name = "新名称-" + (++clickCount)}Button {text: "修改 URL"onClicked: mainController.url = "https://example.com/" + Date.now()}Button {text: "执行操作"onClicked: mainController.performAction()}Button {text: "获取数据"onClicked: {const result = mainController.fetchData(100,[11, 22, 33],{"name": "测试", "value": 123})console.log("操作结果:", result)}}}
}
代码解释:
  • import Demo.Controller:导入 C++ 注册的模块。
  • DemoController:实例化一个控制器对象,设置初始 name 并监听信号。
  • 按钮功能
    • 显示当前 name 属性。
    • 修改 name 属性并递增计数器。
    • 修改 url 属性。
    • 调用 performAction() 方法。
    • 调用 fetchData() 方法,传入参数并打印结果。

设置应用程序入口(main.cpp)

main.cpp 是程序的启动文件,负责加载 QML 文件:

#include <QGuiApplication>
#include <QQmlEngine>
#include <QQmlComponent>
#include <QQuickWindow>
#include <memory>int main(int argc, char *argv[])
{QGuiApplication app(argc, argv);QQmlEngine engine;QQmlComponent component(&engine);component.loadUrl(QUrl("qrc:/main.qml"));if (component.isError()) {qCritical() << "QML 加载错误:";for (const auto &error : component.errors()) {qCritical() << error.toString();}return EXIT_FAILURE;}std::unique_ptr<QQuickWindow> window(qobject_cast<QQuickWindow*>(component.create()));if (!window) {qCritical() << "窗口创建失败";return EXIT_FAILURE;}return app.exec();
}
代码解释:
  • 初始化应用程序和 QML 引擎。
  • 从资源文件加载 main.qml
  • 检查加载是否出错,若出错则打印错误信息并退出。
  • 创建窗口并运行事件循环。

配置资源文件(qml.qrc)

qml.qrc 文件将 QML 文件嵌入到程序中:

<RCC><qresource prefix="/"><file>main.qml</file></qresource>
</RCC>
代码解释:
  • main.qml 添加到资源系统中,可通过 qrc:/main.qml 访问。

运行项目

步骤

  1. 安装环境:确保已安装 Qt6 和 CMake。
  2. 调整路径:在 CMakeLists.txt 中将 CMAKE_PREFIX_PATH 改为你的 Qt 安装路径。
  3. 构建项目
    • 运行 cmake -B build 生成构建系统。
    • 运行 cmake --build build 编译项目。
  4. 运行程序:执行生成的可执行文件。

运行结果

你将看到一个窗口,包含五个按钮。点击按钮会触发与 DemoController 的交互,结果会输出到控制台。例如:

  • 点击“显示当前名称”会打印当前的 name 值。
  • 点击“获取数据”会调用 fetchData() 并显示处理结果。

总结

通过本教程,你已经掌握了以下内容:

  • 如何定义 C++ 类:通过继承 QObject 并使用 Qt 宏,使其可被 QML 调用。
  • 如何配置项目:使用 CMake 将 C++ 和 QML 集成。
  • 如何在 QML 中使用:导入模块,访问属性、调用方法和监听信号。

C++ 和 QML 的结合充分利用了 C++ 的性能优势和 QML 的界面设计灵活性,是开发复杂 Qt Quick 应用程序的理想方式。希望你能通过这个示例项目,逐步熟悉这种混合开发模式,并在未来的学习中不断实践和提升!


http://www.ppmy.cn/news/1575144.html

相关文章

利用机器学习实现实时交易欺诈检测

以下是一个基于Python的银行反欺诈AI应用示例代码,演示如何利用机器学习实现实时交易欺诈检测。该示例使用LightGBM算法训练模型,并通过Flask框架构建实时检测API: python import pandas as pd from sklearn.model_selection import train_test_split from sklearn.preproc…

stm32仿真 74hc238流水灯 数码管动态数字显示

f103c6t6a_hex文件 #include "main.h"![请添加图片描述](https://i-blog.csdnimg.cn/direct/8c0d44b121134cf08f5186df316ea07f.gif)#include "stdlib.h"void SystemClock_Config(void); static void MX_GPIO_Init(void); // 自定义abc引脚 #define A_PIN…

MQ(Message Queue)

目录 MQ(Message Queue)基本概念 为什么要使用消息队列&#xff1f; 使用消息队列有什么缺点&#xff1f; 如何保证消息不丢失?(如何保证消息的可靠性传输?/如何处理消息丢失的问题?) 通用的MQ场景&#xff1a; RabbitMQ如何保证消息不丢失&#xff1f; 生产者丢数据…

PHP课程预约小程序源码

&#x1f4f1; 课程预约小程序&#xff1a;为您专属定制的便捷预约新体验 在这个快节奏的时代&#xff0c;我们深知每一位瑜伽爱好者、普拉提追随者以及培训机构管理者对高效、便捷服务的迫切需求。因此&#xff0c;我们匠心独运&#xff0c;推出了一款基于PHPUniApp框架开发的…

docker-rss:容器更新的RSS订阅源

简介 什么是 docker-rss &#xff1f; docker-rss 是一个用 Go 语言编写的服务器&#xff0c;能通过 RSS Feed 通知 Docker 镜像的更新。 主要特点 功能&#xff1a;提供 Docker 镜像更新的 RSS Feed&#xff0c;便于用户跟踪重要软件的更新&#xff0c;特别适合用于个人实验…

WPS中Word表格做好了,忘记写标题了怎么办?

大家好&#xff0c;我是小鱼。 在使用wps制作Word表格时经常会遇到这种情况&#xff0c;就是辛辛苦苦把word表格制作好了&#xff0c;却突然发现忘了为表格添加标题了。怎么都没法为表格重写添加标题&#xff0c;真是一阵操作猛如虎&#xff0c;结果觉得表格真是白做了。其实&…

从零开始:使用PyTorch构建DeepSeek R1模型及其训练详解

本文将引导你使用 PyTorch 从零开始构建 DeepSeek R1 模型&#xff0c;并详细解释模型架构和训练步骤。DeepSeek R1 是一个假设的模型名称&#xff0c;为了演示目的&#xff0c;我们将构建一个基于 Transformer 的简单文本生成模型。 1. 模型架构 DeepSeek R1 的核心是一个基…

【数据结构】第五章:树与二叉树

本篇笔记课程来源&#xff1a;王道计算机考研 数据结构 【数据结构】第五章&#xff1a;树与二叉树 一、树的定义1. 基本概念2. 基本术语3. 常见性质 二、二叉树的定义1. 基本概念2. 特殊二叉树3. 常见性质 三、二叉树的存储结构1. 顺序存储2. 链式存储 四、二叉树的遍历1. 先序…