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. php实现微信推送消息

    一.<?phpnamespace Org\Weixin;class OrderPush{ protected $appid; protected $secrect; protected $acc ...

  2. 如何使用mongo shell

    Cd到MongoDb安装目录到bin目录下,执行mongo命令即可,其他命令参考手册:https://www.runoob.com/mongodb/mongodb-create-collection. ...

  3. FreeBSD系统基本操作

    1:系统安装 2:关机与重启命令 立即关机,但是不关闭电源: shutdown -h now 立即关机,并且关闭电源: shutdown -p now 重启命令 shutdown -r now

  4. 最近做app项目中遇到的问题 以及一些常见注意事项

    最近做app项目中遇到的问题 1.时间兼容问题 var date = "2019-12-18 18:03:45" //不兼容代码 var newDate = new Date(da ...

  5. STL实现的底层数据结构简介

    STL实现的底层数据结构简介 C++ STL 的实现: 1.vector  底层数据结构为数组 ,支持快速随机访问 2.list    底层数据结构为双向链表,支持快速增删 3.deque   底层数 ...

  6. Powershell阻止确认

    要阻止弹出确认提示,需要设置-Confirm为false, new-VM -Name $hostname -Template $template -VMHost 10.11.31.5 -OSCusto ...

  7. 【转载】linux-查询rpm包相关安装、卸载脚本

        测试过程中,有时要测试开发自己打的rpm包,为了确认打包正确,需要查询rpm包相关安装.卸载脚本,可以使用命令:   [root@6 /]#rpm -q --scripts mysql pos ...

  8. 删除rpc01

    vim /opt/ltp/runtest/stress.part3 修改/opt/ltp/runtest/stress.part3 删除rpc01: 142 systemctl status gdm ...

  9. IT菜鸟之网线制作

    网线是属于OSI七层模型中的物理层:网络中的数据传输媒介 备注:OSI七层模型后面会记录 网线制作所需要的资源素材: 1.网线 2.水晶头(类型:电话线RJ11,宽带线RJ45) 3.网线钳(非必需) ...

  10. shell基础之变量及表达式

    本节内容 1. shell变量简介 2. 定义变量 3. 使用变量 4. 修改变量的值 5. 单引号和双引号的区别 6. 将命令的结果赋值给变量 7. 删除变量 8. 变量类型 9. 特殊变量列表 1 ...