1.Qml调用C++类

Qt QML模块提供了一组API,用来将C++类扩展QML中。您可以编写扩展来添加自己的QML类型,扩展现有的Qt类型,或调用无法从普通QML代码访问的C/C++函数
本章将学习如何使用C++类编写QML扩展,其中包括属性、QML function和属性绑定等
为了方便大家理解,本章示例的函数实现能写在头文件,就写在头文件.

2.创建QML
将C++类扩展QML时,一般用来实现QML目前无法实现的功能,比如访问系统信息,文件信息等。
本章demo是显示一个简单的饼图,创建一个C++类提供给QML使用

这里导入一个"import Charts 1.0"模块,然后创建一个名为"PieChart"的QML类型,该类型具有两个属性:name和color。

import QtQuick.Window 2.12
import Charts 1.0 Window {
visible: true
width: 640
height: 480
PieChart {
width: 100; height: 100
name: "A simple pie chart"
color: "red"
}
}

要做到这一点,我们需要一个C++类,它封装了这个PieChart类型及其name和color两个属性。

3.创建C++类

由于QML大量使用了Qt的元对象系统,因此该类必须是:

  • 继承于QObject的派生类
  • 并且有Q_OBJECT宏

以下是我们的饼图PieChart类,在piechart.h中定义:

#include <QtQuick/QQuickPaintedItem>
#include <QColor>
#include <QPen>
#include <QPainter>
#include <QDebug>
#include <QTimer>
class PieChart : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName)
Q_PROPERTY(QColor color READ color WRITE setColor) public:
PieChart(QQuickItem *parent = 0); QString name() const { return m_name; }
void setName(const QString &name) { m_name = name; } QColor color() const { return m_color; }
void setColor(const QColor &color) { m_color = color; } void paint(QPainter *painter); private:
QString m_name;
QColor m_color; public slots:
void onTimeout(); };

请注意,尽管color在QML中指定为字符串.比如"#00FF00",但它会自动转换为QColor对象,像“640x480”这样的字符串可以自动转换为QSize值。

  • 该类继承自QQuickPaintedItem,因为我们希望在使用QPainter API执行绘图操作时重写QQuickPaintedItem::paint()。
  • 如果类只是表示了某些数据类型,而不是实际需要显示的内容,它可以简单地从QObject继承。
  • 如果我们想创建一个不需要使用QPainter API执行绘图操作的可视化项,我们可以只对QQuickItem子类进行子类。

Ps:

  • QQuickItem:  Qt Quick中的所有可视项都继承自QQuickItem。虽然QQuickItem没有视觉外观,但它定义了视觉项目中常见的所有属性,如x和y位置、宽度和高度、锚定和Key处理支持。
  • QQuickPaintedItem:继承自QQuickItem,并扩展了Qt中的QPainter API函数,使得QPainter将能够直接绘制到QML场景的纹理上。调用update()时可以重新绘制。在paint()中使用setAntaliasing()时可以设置抗锯齿渲染

PieChart类使用Q_PROPERTY宏定义了两个属性name和color,并重写QQuickPaintedItem::paint()。

3.1 Q_PROPERTY介绍
Q_PROPERTY 宏定义属性的一些主要关键字的意义如下:

  • READ  指定一个读取属性值的函数,没有 MEMBER 关键字时必须设置 READ。
  • WRITE  指定一个设定属性值的函数,只读属性没有 WRITE 设置。
  • MEMBER  指定一个成员变量与属性关联,成为可读可写的属性,无需再设置 READ 和 WRITE。
  • RESET  是可选的,用于指定一个设置属性缺省值的函数。
  • NOTIFY  是可选的,用于设置一个信号,当属性值变化时发射此信号(在QML中经常用到,比如onXChanged)。
  • DESIGNABLE  表示属性是否在 Qt Designer 里可见,缺省为 true。
  • CONSTANT  表示属性值是一个常数,对于一个对象实例,READ 指定的函数返回值是常数,但是每个实例的返回值可以不一样。具有 CONSTANT 关键字的属性不能有 WRITE 和 NOTIFY 关键字。
  • FINAL  表示所定义的属性不能被子类重载。

在C++中属性的使用
不管是否用 READ 和 WRITE 定义了接口函数,只要知道属性名称,就可以通过 QObject::property() 读取属性值,并通过 QObject::setProperty() 设置属性值。
比如定义一个类:

class MyObj : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName) public:
MyObj(QQuickItem *parent = 0) { } QString name() const { return m_name; }
void setName(const QString &name) { qDebug()<<name; m_name = name; } // 添加了一个打印 private:
QString m_name; // 用来保存name属性的值
};

然后我们调用setProperty时:

MyObj ct;
ct.setProperty("name","1234"); // 将会调用setName()接口函数,并且打印"1234"

在QML中属性的使用(在"5.属性绑定"会讲解)
在QML中,属性就更加常见了,比如Rectangle的color属性,其实本质就是:

    Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
public:
QColor color() const { return m_color; }
void setColor(const QColor &color) { if (color == m_color) return; m_color = color; emit colorChanged(); }
signals:
void xChanged(const QString &name);
private:
QColor m_color;

假如在c++类中自己更改属性时,并且该属性设置了NOTIFY关键字,那么必须更改后,主动emit来触发属性更改信号,比如:

m_color = QColor::QColor(255, 0, 0, 255);
emit colorChanged();

3.2 piechart.cpp最终如下所示:

#include "piechart.h"

PieChart::PieChart(QQuickItem *parent)
: QQuickPaintedItem(parent)
{
} void PieChart::paint(QPainter *painter)
{
QPen pen(m_color, 2);
painter->setPen(pen);
painter->setRenderHints(QPainter::Antialiasing, true);
painter->drawPie(boundingRect().adjusted(1, 1, -1, -1), 90 * 16, 290 * 16);
}

4.在main.cpp中通过qmlRegisterXXX注册C++类到QML中
我们已经创建好了C++类,剩下的就是注册到QML中即可大功告成了.注册函数是qmlRegisterType(),当然也可以通过qmlRegisterSingletonType()注册单例类(后面章节介绍).
qmlRegisterType函数模版声明如下:

template<typename T>
int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
// uri: 类似于java包名,比如"import QtQuick 2.12","QtQuick"就是包名,而2.12是versionMajor和versionMinor拼接的版本号
// qmlName: 包名中的类型名称,比如Rectangle就是QtQuick包名中的其中一个类型名称

main函数如下所示:

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "piechart.h" int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv); qmlRegisterType<PieChart>("Charts", 1, 0, "PieChart"); // 注册C++类 QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url); return app.exec();
}

运行效果如下所示:

5.属性绑定
属性绑定是QML的一个强大功能,它允许自动同步不同类型的值。当属性值更改时,它使用信号通知和更新其他类型的值。
我们来创建两个PieChart图,名称分别为chartA和chartB,然后我们在chartB里进行color属性绑定"color: chartA.color".
修改main.qml:

import QtQuick 2.14
import QtQuick.Window 2.12
import Charts 1.0 Window {
visible: true
width: 640
height: 480 Row {
PieChart {
id: chartA
width: 100; height: 100
name: "A simple pie chart"
color: "red"
}
PieChart {
id: chartB
width: 100; height: 100
name: "A simple pie chart"
color: chartA.color
}
} MouseArea {
anchors.fill: parent
onClicked: { chartA.color = "blue" }
} }

修改piechart.h:

#include <QtQuick/QQuickPaintedItem>
#include <QColor>
#include <QPen>
#include <QPainter>
#include <QDebug>
#include <QTimer>
class PieChart : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) public:
PieChart(QQuickItem *parent = 0); QString name() const { return m_name; }
void setName(const QString &name)
{
if (name != m_name) {
m_name = name;
emit nameChanged(name);
}
} QColor color() const { return m_color; }
void setColor(const QColor &color)
{
if (color != m_color) {
m_color = color;
emit colorChanged(color);
update();
}
}
void paint(QPainter *painter); private:
QString m_name;
QColor m_color; signals:
void nameChanged(const QString &name);
void colorChanged(const QColor &color); public slots:
void onTimeout(); };

这里我们在Q_PROPERTY()中添加了一个NOTIFY功能:

  • 属性绑定主要是靠的是属性中的NOTIFY功能,每当color值更改时,就会发出colorChanged信号。从而使得绑定的目标属性自动更新值.
  • 调用WRITE功能的函数时(比如setColor()),我们必须判断要设置的值是否与当前属性值相等,这样确保信号不会必要地发出,从而导致可能死循环的事情发生.

当我们点击应用后,由于立即设置chartA.color = "blue",然后由于属性绑定,所以chartB也跟着改变了.最终两个chart都变成了蓝色:

6.使用Q_INVOKABLE修饰函数提供给QML使用
比如在QML中,我们想使用C++类的clearChart()函数来清除绘图时,那我们需要在C++类的clearChart()函数前面使用Q_INVOKABLE修饰来注册到元对象中(本质就是signal和slots).这样QML就可以像使用function那样调用该函数了.

修改类头文件,添加clearChart():

class PieChart : public QQuickPaintedItem
{
...
public:
...
Q_INVOKABLE void clearChart(); };

在cpp文件中实现函数:

void PieChart::clearChart()
{
setColor(QColor(Qt::transparent));
update();
}

修改main.qml:

Window {
visible: true
width: 640
height: 480 Row {
PieChart {
id: chartA
width: 100; height: 100
name: "A simple pie chart"
color: "red"
}
PieChart {
id: chartB
width: 100; height: 100
name: "A simple pie chart"
color: "blue"
}
} MouseArea {
anchors.fill: parent
onClicked: { chartA.clearChart();}
}
}

当我们点击应用后,就会调用chartA.clearChart()方法,从而清除chartA:

7.信号、槽函数提供给QML使用

上一节我们讲过使用Q_INVOKABLE修饰来注册到元对象中其实本质就是signal和slots.这是因为:

所以不管用Q_INVOKABLE,还是signalslots修饰,最终都会变成QT_ANNOTATE_FUNCTION(...)

修改piechart.h,添加一个clearChart2槽函数:

class PieChart : public QQuickPaintedItem
{
...
public slots:
void clearChart2()
{
setColor(QColor(Qt::transparent));
update();
}
...
}; 

修改main.qml:

MouseArea {
anchors.fill: parent
onClicked: { chartA.clearChart2();}
}

发现,最终效果和调用chartA.clearChart()的效果一样.

8.使用Q_ENUM()将枚举提供给QML使用
在C++类中,我们可以通过Q_ENUM()将C++类的枚举类型注册到元对象中,修改头文件如下所示:

class PieChart : public QQuickPaintedItem
{
...
public :
enum Priority {
High,
Low,
VeryHigh,
VeryLow
};
Q_ENUM(Priority) Q_INVOKABLE Priority setPriority(Priority value)
{
qDebug()<<value;
}
...
};

在main.qml中添加:

Component.onCompleted: {
chartA.setPriority(chartA.VeryHigh);
}

运行效果如下所示:

需要注意的是: 在qml中,我们只能使用这些枚举,假如是打印这些枚举变量,值将会是0.

未完待续,下章学习如何向QML中注册单例类

29.qt quick-在QML中调用C++类的更多相关文章

  1. QT之在QML中使用C++类和对象

    QML其实是对ECMAScript的扩展,融合了Qt object系统,它是一种新的解释性语言,QML引擎虽然由Qt C++实现,但QML对象的运行环境说到底和C++对象的上下文环境是不通的,是平行的 ...

  2. Qt Quick 之 QML 与 C++ 混合编程具体解释

    Qt Quick 技术的引入.使得你能够高速构建 UI ,具有动画.各种绚丽效果的 UI 都不在话下.但它不是万能的.也有非常多局限性,原来 Qt 的一些技术,比方低阶的网络编程如 QTcpSocke ...

  3. 【Qt】Qt Quick 之 QML 与 C++ 混合编程详解

    Qt Quick 之 QML 与 C++ 混合编程详解 - CSDN博客   专栏:Qt Quick简明教程 - CSDN博客   .

  4. Qt Quick 和qml介绍

    很多人不了解Qt Quick和Qml,还有很多人对其存在偏见.这篇文章就是来向这些有困惑的人介绍一下其是什么,有什么特点. 首先,这两个是一个东西吗? 答案:是的.但是,具体来说,Qt Quick是框 ...

  5. 在 QML 中使用 C++ 类和对象

    Qt Quick 技术的引入,使得你能够快速构建 UI ,具有动画.各种绚丽效果的 UI 都不在话下.但它不是万能的,也有很多局限性,原来 Qt 的一些技术,比如低阶的网络编程如 QTcpSocket ...

  6. Oracle数据库中调用Java类开发存储过程、函数的方法

    Oracle数据库中调用Java类开发存储过程.函数的方法 时间:2014年12月24日  浏览:5538次 oracle数据库的开发非常灵活,不仅支持最基本的SQL,而且还提供了独有的PL/SQL, ...

  7. kettle中调用java类

    kettle中调用java类 有时须要在kettle调用java类,如:验证.查询或自己定义加密等.有时甚至连主要的数据訪问都不那么简单,如获取一个存储文件或使用一个数据库连接,某些数据源可能封装在应 ...

  8. 只能从脚本中调用在类定义上有[ScriptService]属性的Web服务问题的解决方案

    ajax调用webservice中的接口时, 会出现[只能从脚本中调用在类定义上有[ScriptService]属性的...]的异常. 这是因为, 在.net3.5中, 访问web服务, 要对web服 ...

  9. C#在派生类中调用基类成员

    一.在派生类中调用基类成员 在C#的派生类中,我们可以使用base关键字调用基类中的公有或者受保护成员.这些成员只能是构造函数.实例方法或者实例属性. base关键字调用基类成员的语法格式如下: ba ...

随机推荐

  1. Spring MVC工作原理及源码解析(四) ViewResolver实现原理及源码解析

    0.ViewResolver原理介绍 根据视图的名称将其解析为 View 类型的视图,如通过 ModelAndView 中的视图名称将其解析成 View,View 是用来渲染页面的,也就是将 Mode ...

  2. PE文件中的输入表

    前言 PE文件中的输入表含有三个重要结构IID,IDT,IAT.PE文件为需要加载的DLL文件创建一个IID结构,一个DLL与一个IID对应.IDT是输入名称表,IAT输入地址表,在没有绑定输入的情况 ...

  3. 人人都爱Kubernetes,Docker难道就不香了吗?

    开篇 提起Docker,有很多人第一印象会认为它就是一个虚拟化容器,所以大家特别容易陷入到一种误区,就是觉得Docker只是在Linux操作系统之上又增加了一层,就跟OS上跑了一个VMWare一样.D ...

  4. 『动善时』JMeter基础 — 20、JMeter配置元件【HTTP Cookie管理器】详细介绍

    目录 1.HTTP Cookie管理器介绍 2.HTTP Cookie管理器界面详解 3.JMeter中对Cookie的管理 (1)Cookie的存储 (2)Cookie的管理策略 4.补充:Cook ...

  5. [Qt] 事件机制(三)

    在主窗口Widget中增加几个小功能 1.点击左键,在左上角label中显示"haha",点击右键,显示"lala" 在widget.h中添加: 1 #incl ...

  6. 使用 dd 命令进行硬盘 I/O 性能检测

    使用 dd 命令进行硬盘 I/O 性能检测 作者: Vivek Gite 译者: LCTT DongShuaike | 2015-08-28 07:30   评论: 1 收藏: 6 如何使用dd命令测 ...

  7. Linux中级之ansible概念及hoc命令行调用模式

    一.Ansible简介 ansible是新出现的开源的自动化运维工具,基于Python开发,集合了众多运维工具(puppet.cfengine.chef.func.fabric)的优点,实现了批量系统 ...

  8. JavaSE 知识图谱

    JAVA基础语法 DOS命令 JAVA介绍 JDK安装 JAVA环境的搭建 关键字 注释 标识符命名规则(编码规范) 字面值常量 进制转换 基本类型 变量(局部变量.静态变量) 运算符 表达式 控制语 ...

  9. centos7 搭建 nginx web服务 反代理

    Nginx("engine x")是一款是由俄罗斯的程序设计师Igor Sysoev所开发高性能的 Web和 反向代理 服务器,也是一个 IMAP/POP3/SMTP 代理服务器. ...

  10. System Verilog MCDF(二)

    整形器的接口时序: reg,grant是维持了两个clk的. chid ,length在发送数据期间不可以变化. 第一个data数据必须在start上升沿的同一个clk发送. reg,grant两者之 ...