当前位置: 首页 > article >正文

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

本教程将教你如何在 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_OBJECT
    Q_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.cpp
    qml.qrc
    demo_controller.h
    demo_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.Controller

Window {
    width: 800
    height: 600
    visible: true
    title: "C++/QML 集成演示"

    Column {
        spacing: 10
        padding: 20
        width: parent.width

        DemoController {
            id: mainController
            name: "初始名称"
            onNameChanged: console.log("名称变更:", name)
            onUrlChanged: console.log("URL 变更:", url)
            onActionTriggered: console.log("操作已触发")
        }

        Button {
            text: "显示当前名称"
            onClicked: console.log("当前名称:", mainController.name)
        }

        Button {
            text: "修改名称"
            property int clickCount: 0
            onClicked: 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.kler.cn/a/562495.html

相关文章:

  • stm32-LCD(液晶显示器)
  • 【Matlab仿真】Matlab Function中如何使用静态变量?
  • rust笔记10-多线程
  • MySQL 8.0 Enterprise Backup (MEB) 备份与恢复实践指南
  • 力扣hot100 —— 电话号码字母组合; 子集 (非回溯做法)简单易懂
  • ctfshow做题笔记—栈溢出—pwn57~pwn60
  • 基数排序:独特的排序之道
  • C++和OpenGL实现3D游戏编程【连载23】——几何着色器和法线可视化
  • Vue3 + Vite + TS,使用 配置项目别名属性:resolve
  • 补题A-E Codeforces Round 953 (Div. 2)
  • 【Java】数据类型
  • Vue中环境配置的若干问题解决
  • opencv边缘检测
  • mysql大数量表添加索引方案
  • 《AI 大模型 ChatGPT 的传奇》
  • uni-app 开发 App 、 H5 横屏签名(基于lime-signature)
  • 优选算法大集合(待更新)
  • Python 3.11 69 个内置函数(完整版)
  • 《Keras 3 使用 PointNet 进行点云分段》:此文为AI自动翻译
  • ktransformers 上的 DeepSeek-R1 671B open-webui