百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

QML与C++混合编程

bigegpt 2024-09-16 12:14 5 浏览

一、QML与C++混合编程简介

QML与C++混合编程就是使用QML高效便捷地构建UI,而C++则用来实现业务逻辑和复杂算法。

二、QML访问C++

Qt集成了QML引擎和Qt元对象系统,使得QML很容易从C++中得到扩展,在一定的条件下,QML就可以访问QObject派生类的成员,例如信号、槽函数、枚举类型、属性、成员函数等。

QML访问C++有两个方法:一是在Qt元对象系统中注册C++类,在QML中实例化、访问;二是在C++中实例化并设置为QML上下文属性,在QML中直接使用。第一种方法可以使C++类在QML中作为一个数据类型,例如函数参数类型或属性类型,也可以使用其枚举类型、单例等,功能更强大。

三、C++类的实现

C++类要想被QML访问,首先必须满足两个条件:一是派生自QObject类或QObject类的子类,二是使用Q_OBJECT宏。QObject类是所有Qt对象的基类,作为Qt对象模型的核心,提供了信号与槽机制等很多重要特性。Q_OBJECT宏必须在private区(C++默认为private)声明,用来声明信号与槽,使用Qt元对象系统提供的内容,位置一般在语句块首行。Projects选择Qt Quick Application,工程名为Hello。

1、信号与槽实现

A、C++类实现

#ifndef HELLO_H
#define HELLO_H
#include <QObject>
#include <QDebug>

class Hello: public QObject
{
Q_OBJECT
public slots:
void doSomething()
{
qDebug() << "Hello::dosomething() is called.";
}
signals:
void begin();
};

#endif // HELLO_H

Hello类中的信号begin()和槽doSomething()都可以被QML访问。槽必须声明为public或protected,信号在C++中使用时要用到emit关键字,但在QML中就是个普通的函数,用法同函数一样,信号处理器形式为on,Signal首字母大写。信号不支持重载,多个信号的名字相同而参数不同时,能够被识别的只是最后一个信号,与信号的参数无关。

B、注册C++类型

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "hello.h"

int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
//注册C++类型Hello
qmlRegisterType<Hello>("Hello.module",1,0,"Hello");

QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

return app.exec();
}

将C++类注册到Qt元对象系统。

C、在QML文件中导入C++类并使用

import QtQuick 2.5
import QtQuick.Window 2.2
//导入注册的C++类
import Hello.module 1.0

Window {
visible: true
width: 640
height: 480
title: qsTr("Hello QML")
MouseArea {
anchors.fill: parent
onClicked: {
hello.begin();//单击鼠标调用begin信号函数
}
}
Hello{
id:hello //Hello类的实例
onBegin:doSomething()
}
}

在QML文件中导入注册的C++类(import),关键字Hello就可以在当前QML文件中当作一种QML类型来用。MouseArea铺满界面,单击鼠标时会发送begin()信号,进而调用doSomething()槽函数。

2、枚举类型实现

A、C++类中枚举的定义

#ifndef HELLO_H
#define HELLO_H
#include <QObject>
#include <QDebug>

class Hello: public QObject
{
Q_OBJECT
Q_ENUMS(Color)
public:
Hello():m_color(RED)
{
qDebug() << "Hello() is called.";
}
//枚举
enum Color
{
RED,
BLUE,
BLACK
};
public slots:
void doSomething(Color color)
{
qDebug() << "Hello::dosomething() is called " << color;
}

signals:
void begin();
private:
Color m_color;
};

#endif // HELLO_H

C++类中添加了public的Color枚举类型,枚举类型要想在QML中使用,需要使用Q_ENUMS()宏。

B、QML文件中使用C++枚举类型

import QtQuick 2.5
import QtQuick.Window 2.2
//导入注册的C++类
import Hello.module 1.0

Window {
visible: true
width: 640
height: 480
title: qsTr("Hello QML")
MouseArea {
anchors.fill: parent
onClicked: {
hello.begin();//单击鼠标调用begin信号函数
}
}
Hello{
id:hello //Hello类的实例
onBegin:doSomething(Hello.RED)
}
}

QML中使用枚举类型的方式是通过C++类型名使用“.”操作符直接访问枚举成员,如Hello.RED。

3、成员函数实现

A、成员函数定义

#ifndef HELLO_H
#define HELLO_H
#include <QObject>
#include <QDebug>

class Hello: public QObject
{
Q_OBJECT
Q_ENUMS(Color)
public:
Hello():m_color(RED)
{
qDebug() << "Hello() is called.";
}
//枚举
enum Color
{
RED,
BLUE,
BLACK
};
Q_INVOKABLE void show()
{
qDebug() << "show() is called.";
}
public slots:
void doSomething(Color color)
{
qDebug() << "Hello::dosomething() is called " << color;
}

signals:
void begin();
private:
Color m_color;
};

#endif // HELLO_H

如果QML中访问C++成员函数,则C++成员函数必须是public或protected成员函数,且使用Q_INVOKABLE宏,位置在函数返回类型的前面。

B、QML中调用C++类成员函数

import QtQuick 2.5
import QtQuick.Window 2.2
//导入注册的C++类
import Hello.module 1.0

Window {
visible: true
width: 640
height: 480
title: qsTr("Hello QML")
MouseArea {
anchors.fill: parent
onClicked: {
hello.begin();//单击鼠标调用begin信号函数
hello.show();
}
}
Hello{
id:hello //Hello类的实例
onBegin:doSomething(Hello.RED)
}
}


在QML中访问C++的成员函数的形式是“.”,如hello.show(),支持函数重载。

4、C++类的属性

A、C++类中属性的定义

#ifndef HELLO_H
#define HELLO_H
#include <QObject>
#include <QDebug>

class Hello: public QObject
{
Q_OBJECT
Q_ENUMS(Color)
//属性声明
Q_PROPERTY(Color color READ color WRITE setColor NOTIFY colorChanged)
public:
Hello():m_color(RED)
{
qDebug() << "Hello() is called.";
}
//枚举
enum Color
{
RED,
BLUE,
BLACK
};
Q_INVOKABLE void show()
{
qDebug() << "show() is called.";
}
Color color() const
{
return m_color;
}
void setColor(const Color& color)
{
if(color != m_color)
{
m_color = color;
emit colorChanged();
}
}
public slots:
void doSomething(Color color)
{
qDebug() << "Hello::dosomething() is called " << color;
}

signals:
void begin();
void colorChanged();
private:
Color m_color;//属性
};

#endif // HELLO_H

C++类中添加了Q_PROPERTY()宏,用来在QObject派生类中声明属性,属性同类的数据成员一样,但又有一些额外的特性可通过Qt元对象系统来访问。

Q_PROPERTY()(type name
     (READ getFunction [WRITE setFunction] |
             MEMBER memberName [(READ getFunction | WRITE setFunction)])
            [RESET resetFunction]
            [NOTIFY notifySignal]
            [REVISION int]
            [DESIGNABLE bool]
            [SCRIPTABLE bool]
            [STORED bool]
            [USER bool]
            [CONSTANT]
            [FINAL])

属性的type、name是必需的,其它是可选项,常用的有READ、WRITE、NOTIFY。属性的type可以是QVariant支持的任何类型,也可以是自定义类型,包括自定义类、列表类型、组属性等。另外,属性的READ、WRITE、RESET是可以被继承的,也可以是虚函数,不常用。

READ:读取属性值,如果没有设置MEMBER的话,是必需的。一般情况下,函数是个const函数,返回值类型必须是属性本身的类型或这个类型的const引用,没有参数。

WRITE:设置属性值,可选项。函数必须返回void,有且仅有一个参数,参数类型必须是属性本身的类型或类型的指针或引用。

NOTIFY:与属性关联的可选信号,信号必须在类中声明过,当属性值改变时,就可触发信号,可以没有参数,有参数的话只能是一个类型同属性本身类型的参数,用来记录属性改变后的值。

B、QML中修改属性

import QtQuick 2.5
import QtQuick.Window 2.2
//导入注册的C++类
import Hello.module 1.0

Window {
visible: true
width: 640
height: 480
title: qsTr("Hello QML")
MouseArea {
anchors.fill: parent
onClicked: {
hello.begin()//单击鼠标调用begin信号函数
hello.show()
hello.color = 2 //修改属性
}
}
Hello{
id:hello //Hello类的实例
onBegin:doSomething(Hello.RED)
onColorChanged:console.log("color changed.")
}
}

C++类中的m_color属性可以在QML中访问、修改,访问时调用了color()函数,修改时调用setColor()函数,同时还发送了一个信号来自动更新color属性值。

四、注册C++类为QML类型

QObject派生类可以注册到Qt元对象系统,使得类在QML中同其它内建类型一样,可以作为一个数据类型来使用。QML引擎允许注册可实例化的类型,也可以是不可实例化的类型,常见的注册函数有:

qmlRegisterInterface()
qmlRegisterRevision()
qmlRegisterSingletonType()
qmlRegisterType()
qmlRegisterTypeNotAvailable()
qmlRegisterUncreatableType()
 
template<typename T>
int qmlRegisterType(const char *uri,int versionMajor,
       int versionMinor, const char *qmlName);

模板函数注册C++类到Qt元对象系统中,uri是需要导入到QML中的库名,versionMajor和versionMinor是其版本数字,qmlName是在QML中可以使用的类型名。

qmlRegisterType<Hello>("Hello.module",1,0,"Hello");

main.cpp中将Hello类注册为在QML中可以使用的GHello类型,主版本为1,次版本为0,库的名字是Hello.module。main.qml中导入了C++库,使用Hello构造了一个对象,id为hello,可以借助id来访问C++。

注册动作必须在QML上下文创建前,否则无效。

QQuickView为QtQuickUI提供了一个窗口,可以方便地加载QML文件并显示其界面。QApplication派生自QGuiApplication,而QGuiApplication又派生自QCoreApplication,这三个类是常见的管理Qt应用程序的类。QQmlApplicationEngine可以方便地从一个单一的QML文件中加载应用程序,派生自QQmlEngine,QQmlEngine则提供了加载QML组件的环境,可以与QQmlComponent、QQmlContext等一起使用。

五、QML上下文属性设置

在C++应用程序加载QML对象时,可以直接嵌入一些C++数据来给QML使用,需要用到QQmlContext::setContextProperty()设置QML上下文属性,上下文属性可以是一个简单的类型,也可以是任何自定义的类对象。

A、C++设置上下文属性

#include <QGuiApplication>
#include <QQuickView>
#include <QQmlContext>
#include "hello.h"

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

QQuickView view;
Hello hello;
view.rootContext()->setContextProperty("hello", &hello);
view.setSource(QUrl(QStringLiteral("qrc:///main.qml")));
view.show();

return app.exec();
}

Hello类先实例化为hello对象,然后注册为QML上下文属性。

B、QML使用

import QtQuick 2.5

Item {
width: 640
height: 480
MouseArea {
anchors.fill: parent
onClicked: {
hello.begin()//单击鼠标调用begin信号函数
hello.show()
}
}
Connections{
target:hello
onBegin:console.log("hello")
}
}


在main.qml中不能使用Hello类型来实例化,也不能调用doSomething()槽函数,因为doSomething()函数中的枚举类型在QML中是访问不到的,正确的用法是通过已经设置的QML上下文属性“hello”来访问C++,可以访问信号begin()和成员函数show(),此时的信号处理器需要用Connections来处理。

六、C++访问QML

在C++中也可以访问QML中的属性、函数和信号。

在C++中加载QML文件可以用QQmlComponent或QQuickView,然后就可以在C++中访问QML对象。QQuickView提供了一个显示用户界面的窗口,而QQmlComponent没有。

1、C++使用QQmlComponent

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "hello.h"

int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
//注册C++类型Hello
qmlRegisterType<Hello>("Hello.module",1,0,"Hello");

QQmlApplicationEngine engine;
QQmlComponent component(&engine, QUrl(QStringLiteral("qrc:/main.qml")));
component.create();

return app.exec();
}


2、C++使用QML的属性

在C++中加载了QML文件并进行组件实例化后,就可以在C++中访问、修改实例的属性值,可以是QML内建属性,也可以是自定义属性。

A、QML中定义部分元素的属性

import QtQuick 2.5
import QtQuick.Window 2.0
import Hello.module 1.0

Window {
visible: true
width: 640
height: 480
title: "Hello QML"
color: "white"
MouseArea {
anchors.fill: parent
onClicked: {
hello.begin()//单击鼠标调用begin信号函数
hello.doSomething(2)
}
}
Rectangle{
objectName: "rect"
anchors.fill: parent
color:"red"
}
Hello{
id:hello
onBegin:console.log("hello")
onColorChanged:hello.show()
}
}


QML中添加了一个Rectangle,设置objectName属性值为“rect”,objectName是为了在C++中能够找到Rectangle。

B、C++中使用QML元素的属性

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "hello.h"

int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
//注册C++类型Hello
qmlRegisterType<Hello>("Hello.module",1,0,"Hello");

QQmlApplicationEngine engine;
QQmlComponent component(&engine, QUrl(QStringLiteral("qrc:/main.qml")));
QObject* object = component.create();
qDebug() << "width value is" << object->property("width").toInt();
object->setProperty("width", 500);//设置window的宽
qDebug() << "width value is" << object->property("width").toInt();
qDebug() << "height value is" << QQmlProperty::read(object, "height").toInt();
QQmlProperty::write(object, "height", 300);//设置window的高
qDebug() << "height value is" << QQmlProperty::read(object, "height").toInt();
QObject* rect = object->findChild<QObject*>("rect");//查找名称为“rect”的元素
if(rect)
{
rect->setProperty("color", "blue");//设置元素的color属性值
qDebug() << "color is " << object->property("color").toString();
}

return app.exec();
}

QObject::property()/setProperty()来读取、修改width属性值。

QQmlProperty::read()/write()来读取、修改height属性值。

如果某个对象的类型是QQuickItem,例如QQuickView::rootObject()的返回值,可以使用QQuickItem::width/setWidth()来访问、修改width属性值。

QML组件是一个复杂的树型结构,包含兄弟组件和孩子组件,可以使用QObject::findchild()/findchildren()来查找。

3、C++使用QML中信号与函数

在C++中,使用QMetaObject::invokeMethod()可以调用QML中的函数,从QML传递过来的函数参数和返回值会被转换为C++中的QVariant类型,成功返回true,参数不正确或被调用函数名错误返回false,invokeMethod()共有四个重载函数。必须使用Q_ARG()宏来声明函数参数,用Q_RETURN_ARG()宏来声明函数返回值,其原型如下:

QGenericArgument Q_ARG(Type, const Type & value)

QGenericReturnArgument Q_RETURN_ARG(Type, Type & value)

使用QObject::connect()可以连接QML中的信号,connect()共有四个重载函数,都是静态函数。必须使用SIGNAL()宏来声明信号,SLOT()宏声明槽函数。

使用QObject::disconnect()可以解除信号与槽函数的连接。

A、QML中定义信号与函数

import QtQuick 2.5
import QtQuick.Window 2.0
import Hello.module 1.0

Window {
visible: true
width: 640
height: 480
title: "Hello QML"
color: "white"
//定义信号
signal qmlSignal(string message)
//定义函数
function qmlFunction(parameter) {
console.log("qml function parameter is", parameter)
return "function from qml"
}
MouseArea {
anchors.fill: parent
onClicked: {
hello.begin()//单击鼠标调用begin信号函数
hello.doSomething(2)
qmlSignal("This is an qml signal.")//发送信号
}
}
Rectangle{
objectName: "rect"
anchors.fill: parent
color:"red"
}
Hello{
id:hello
onBegin:console.log("hello")
onColorChanged:hello.show()
}
}

QML中添加了qmlSignal()信号和qmlFunction()函数,信号在QML中发送,函数在C++中调用。

点击领取Qt学习资料+视频教程~「链接」

B、C++中调用

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "hello.h"

int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
//注册C++类型Hello
qmlRegisterType<Hello>("Hello.module",1,0,"Hello");

QQmlApplicationEngine engine;
QQmlComponent component(&engine, QUrl(QStringLiteral("qrc:/main.qml")));
QObject* object = component.create();
qDebug() << "width value is" << object->property("width").toInt();
object->setProperty("width", 500);//设置window的宽
qDebug() << "width value is" << object->property("width").toInt();
qDebug() << "height value is" << QQmlProperty::read(object, "height").toInt();
QQmlProperty::write(object, "height", 300);//设置window的高
qDebug() << "height value is" << QQmlProperty::read(object, "height").toInt();
QObject* rect = object->findChild<QObject*>("rect");//查找名称为“rect”的元素
if(rect)
{
rect->setProperty("color", "blue");//设置元素的color属性值
qDebug() << "color is " << object->property("color").toString();
}
//调用QML中函数
QVariant returnedValue;
QVariant message = "Hello from C++";
QMetaObject::invokeMethod(object, "qmlFunction",
Q_RETURN_ARG(QVariant, returnedValue),
Q_ARG(QVariant, message));
qDebug() << "returnedValue is" << returnedValue.toString(); // function from qml
Hello hello;
//连接QML元素中的信号到C++槽函数
QObject::connect(object, SIGNAL(qmlSignal(QString)),
&hello, SLOT(qmlSlot(QString)));

return app.exec();
}

C++类中的槽函数:

public slots:
void doSomething(Color color)
{
qDebug() << "Hello::dosomething() is called " << color;
}
void qmlSlot(const QString& message)
{
qDebug() << "C++ called: " << message;
}

七、QML与C++混合编程注意事项

QML与混合编程注意事项:

A、自定义C++类一定要派生自QObject类或其子类,并使用Q_OBJECT宏。

B、注册自定义C++类到Qt元对象系统或设置自定义类对象实例为QML上下文属性是必须的。

C、两者交互进行数据传递时,要符合QML与C++间数据类型的转换规则。

相关推荐

方差分析简介(方差分析通俗理解)

介绍方差分析(ANOVA,AnalysisofVariance)是一种广泛使用的统计方法,用于比较两个或多个组之间的均值。单因素方差分析是方差分析的一种变体,旨在检测三个或更多分类组的均值是否存在...

正如404页面所预示,猴子正成为断网元凶--吧嗒吧嗒真好吃

吧嗒吧嗒,绘图:MakiNaro你可以通过加热、冰冻、水淹、模塑、甚至压溃压力来使网络光缆硬化。但用猴子显然是不行的。光缆那新挤压成型的塑料外皮太尼玛诱人了,无法阻挡一场试吃盛宴的举行。印度政府正...

Python数据可视化:箱线图多种库画法

概念箱线图通过数据的四分位数来展示数据的分布情况。例如:数据的中心位置,数据间的离散程度,是否有异常值等。把数据从小到大进行排列并等分成四份,第一分位数(Q1),第二分位数(Q2)和第三分位数(Q3)...

多组独立(完全随机设计)样本秩和检验的SPSS操作教程及结果解读

作者/风仕在上一期,我们已经讲完了两组独立样本秩和检验的SPSS操作教程及结果解读,这期开始讲多组独立样本秩和检验,我们主要从多组独立样本秩和检验介绍、两组独立样本秩和检验使用条件及案例的SPSS操作...

方差分析 in R语言 and Excel(方差分析r语言例题)

今天来写一篇实际中比较实用的分析方法,方差分析。通过方差分析,我们可以确定组别之间的差异是否超出了由于随机因素引起的差异范围。方差分析分为单因素方差分析和多因素方差分析,这一篇先介绍一下单因素方差分析...

可视化:前端数据可视化插件大盘点 图表/图谱/地图/关系图

前端数据可视化插件大盘点图表/图谱/地图/关系图全有在大数据时代,很多时候我们需要在网页中显示数据统计报表,从而能很直观地了解数据的走向,开发人员很多时候需要使用图表来表现一些数据。随着Web技术的...

matplotlib 必知的 15 个图(matplotlib各种图)

施工专题,我已完成20篇,施工系列几乎覆盖Python完整技术栈,目标只总结实践中最实用的东西,直击问题本质,快速帮助读者们入门和进阶:1我的施工计划2数字专题3字符串专题4列表专题5流程控制专题6编...

R ggplot2常用图表绘制指南(ggplot2绘制折线图)

ggplot2是R语言中强大的数据可视化包,基于“图形语法”(GrammarofGraphics),通过分层方式构建图表。以下是常用图表命令的详细指南,涵盖基本语法、常见图表类型及示例,适合...

Python数据可视化:从Pandas基础到Seaborn高级应用

数据可视化是数据分析中不可或缺的一环,它能帮助我们直观理解数据模式和趋势。本文将全面介绍Python中最常用的三种可视化方法。Pandas内置绘图功能Pandas基于Matplotlib提供了简洁的绘...

Python 数据可视化常用命令备忘录

本文提供了一个全面的Python数据可视化备忘单,适用于探索性数据分析(EDA)。该备忘单涵盖了单变量分析、双变量分析、多变量分析、时间序列分析、文本数据分析、可视化定制以及保存与显示等内容。所...

统计图的种类(统计图的种类及特点图片)

统计图是利用几何图形或具体事物的形象和地图等形式来表现社会经济现象数量特征和数量关系的图形。以下是几种常见的统计图类型及其适用场景:1.条形图(BarChart)条形图是用矩形条的高度或长度来表示...

实测,大模型谁更懂数据可视化?(数据可视化和可视化分析的主要模型)

大家好,我是Ai学习的老章看论文时,经常看到漂亮的图表,很多不知道是用什么工具绘制的,或者很想复刻类似图表。实测,大模型LaTeX公式识别,出乎预料前文,我用Kimi、Qwen-3-235B...

通过AI提示词让Deepseek快速生成各种类型的图表制作

在数据分析和可视化领域,图表是传达信息的重要工具。然而,传统图表制作往往需要专业的软件和一定的技术知识。本文将介绍如何通过AI提示词,利用Deepseek快速生成各种类型的图表,包括柱状图、折线图、饼...

数据可视化:解析箱线图(box plot)

箱线图/盒须图(boxplot)是数据分布的图形表示,由五个摘要组成:最小值、第一四分位数(25th百分位数)、中位数、第三四分位数(75th百分位数)和最大值。箱子代表四分位距(IQR)。IQR是...

[seaborn] seaborn学习笔记1-箱形图Boxplot

1箱形图Boxplot(代码下载)Boxplot可能是最常见的图形类型之一。它能够很好表示数据中的分布规律。箱型图方框的末尾显示了上下四分位数。极线显示最高和最低值,不包括异常值。seaborn中...