文章参考:
https://blog.csdn.net/foruok/article/details/32698603
http://blog.51cto.com/9291927/1975383
QT项目开发过程中,画面上显示的布局以及对应的数据可能会动态变化,如果只用QML来构件简单的布局是无法满足需求的,所以会用通过C++代码来实现业务逻辑,QML负责构件UI。这里用到MVC模式的思想。
QML与C++混合编程简介
QML与C++混合编程就是使用QML高效便捷地构建UI,而C++则用来实现业务逻辑和复杂算法。
Qt Quick与QML
Qt Quick是Qt User Interface Creation Kit的缩写。
从QT框架简介中可以了解到,QML (Qt Meta-Object Language的缩写)其实是对 JavaScript 的扩展,是Qt Quick最重要的组成部分。
Qt Quick包含一个组件集合,大部分用于图形界面。同时也包含一个用于管理组件并与组件交互的C++ API——QtDeclarative模块,用于QML与C++之间的桥梁。
C++与QML的交互通过向QML注册C++对象实现,其中C++实现中,非可视化的类均为QObject的子类,可是化的类型均为QDeclarativeItem的子类,而QDeclarativeItem等同于QML的Item类。
QML 中使用 C++ 类和对象
Qt 提供了两种在 QML 环境中使用 C++ 对象的方式:
C++类的实现
C++类要想被QML访问,首先必须满足两个条件:
信号与槽实现
只要是信号或者槽,都可以在 QML 中访问。
信号在C++中使用时要用到emit关键字,但在QML中就是个普通的函数,用法同函数一样。
注册QML类型
int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName) 有两个模板函数:
template
int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
template
int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
前一个原型一般用来注册一个新类型,而后一个可以为特定的版本注册类型。通常使用的是第一个,后面这个有兴趣可以自己详细了解。
参数含义:
typename :实现的 C++ 类的类名。
uri :让你指定一个唯一的包名,类似 Java 中的那种,一是用来避免名字冲突,而是可以把多个相关类聚合到一个包中方便引用。比如我们常写这个语句 “import QtQuick 1.0” ,其中的 “QtQuick” 就是包名 uri
versionMajor:指上述包名后面1.0中的“1”
versionMinor:指上述包名后面1.0中的“0”
qmlName : QML 中可以使用的类名。
例如:
qmlRegisterType("qt.test.TestHuman", 1, 0, "TestHuman"); //只要保证在加载qml之前即可
当将C++类注册成功之后则可以在 QML 中创建 C++ 导入类型的对象了,与 QML 内建类型的使用完全一样。如下是创建一个 TestHuman 实例的代码:
import QtQuick 2.0
import qt.test.TestFlowers 1.0
Rectangle {
width: 200;
height: 200;
TestFlowers {
id: testflowers ;
anchors.top: parent.top;
}
Button{
id: testBtn1;
text: qsTr("Hello World");
anchors.top: testflowers .bottom;
onClicked: {
testHuman .onBtnClick();
}
}
}
上述qml文件可以看出C++对象可以跟其它部件使用完全一样,qml文件中通常要使用C++对象的成员属性、方法,为了能让C++的相关数据能被访问,则需要做特殊处理。
Q_PROPERTY
Q_PROPERTY 宏用来定义可通过元对象系统访问的属性,通过它定义的属性,可以在 QML 中访问、修改,也可以在属性变化时发射特定的信号。要想使用 Q_PROPERTY 宏,你的类必须是 QObject 的派生类,必须在类首使用 Q_OBJECT 宏。
下面是 Q_PROPERTY 宏的原型:
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支持的任何类型,也可以是自定义类型,包括自定义类、列表类型、组属性等。
其它不常用的就不介绍了,感兴趣的可以查看SDK。
Q_ENUMS
如果你要导出的类定义了想在 QML 中使用枚举类型,可以使用 Q_ENUMS 宏将该枚举注册到元对象系统中。
Q_INVOKABLE
在定义一个类的成员函数时使用 Q_INVOKABLE 宏来修饰,就可以让该方法被元对象系统调用。这个宏必须放在返回类型前面。在 QML 中就可以用 O b j e c t . {Object}. Object.{method} 来访问。
代码:
C++类(.h文件):
class TestFlowers : public QObject
{
Q_OBJECT
Q_ENUMS(Color)
Q_PROPERTY(Color color READ color WRITE setColor NOTIFY colorChange)
Q_INVOKABLE void (onBtnClick)
public:
TestFlowers (QObject *parent = 0);
~TestFlowers ();
enum Color {
RED,
GREEN,
BLUE
};
};
qml:
import QtQuick 2.0
import qt.test.TestFlowers 1.0
Rectangle {
width: 200;
height: 200;
TestFlowers {
id: testflowers ;
anchors.top: parent.top;
}
Button{
id: testBtn1;
text: qsTr("Hello World");
anchors.top: testflowers .bottom;
onClicked: {
testflowers.onBtnClick(); //控件的点击事件,可以在类文件(.cpp)中实现业务逻辑
}
}
Rectangle{
id:background;
width: parent.width;
height:50;
color:testflowers .color; //可以直接使用C++属性值
anchors.bottom: testBtn1.bottom;
}
}
具体的C++类的.cpp文件就不详细写了,这里主要表达的是在qml中使用C++类的相关属性和方法。如果想在qml中使用C++类属性,则必须用特殊的宏才能使用。
注册C++ 对象为 QML 的属性
在C++应用程序加载QML对象时,可以直接嵌入一些C++数据来给QML使用,需要用到QQmlContext::setContextProperty()设置QML上下文属性,上下文属性可以是一个简单的类型,也可以是任何自定义的类对象。
以下代码在main函数中注册:
QtQuick2ApplicationViewer viewer;
viewer.rootContext()->setContextProperty("testflowers", new TestFlowers ); //这里new创建了对象,需要自己手动回收
viewer.setMainQmlFile(QStringLiteral("qml/testflowers/main.qml")); //"qml/testflowers/main.qml"表示qml文件路径
通过上述方法注册之后。qml的使用有部分改动:
import QtQuick 2.0
// import qt.test.TestFlowers 1.0 已经注册,不需要了
Rectangle {
width: 200;
height: 200;
// 已经注册成属性了,不要了
/* TestFlowers {
id: testflowers ;
anchors.top: parent.top;
}*/
Button{
id: testBtn1;
text: qsTr("Hello World");
anchors.top: testflowers .bottom;
onClicked: {
testflowers.onBtnClick(); //控件的点击事件,可以在类文件(.cpp)中实现业务逻辑
}
}
Rectangle{
id:background;
width: parent.width;
height:50;
color:testflowers .color; //可以直接使用C++属性值
anchors.bottom: testBtn1.bottom;
}
}
在 C++ 中使用 QML 对象
在C++中也可以访问QML中的属性、函数和信号。QObject 类的构造函数有一个 parent 参数,可以指定一个对象的父亲, QML 中的对象其实借助这个组成了以根 item 为父的一棵对象树。
而 QObject 定义了一个属性 objectName ,这个对象名字属性,就可以用于查找对象。现在该说到查找对象的方法了: findChild() 和 findChildren() 。它们的函数原型如下:
T QObject::findChild(const QString &name = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively) const
QList QObject::findChildren(const QString &name = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively) const
用例:
QPushButton *button = parentWidget->findChild("button1");
QList widgets = parentWidget.findChildren("widgetname");
在TestFlowers 类中查找testBtn1对象调用方法如下:
Button* button1 = this->findChild
PS:上述代码的实例没有写全,所以不要直接copy在实机上运行,上述相关方法都可以到QT SDK中找到,可以自己下载一个qt 工具,然后在里边搜索相关方法,里边都有详细介绍且非常方便(参考:QT框架简介里边的搜索方法)。