QML 与 C++ 混合编程
内容:
1. QML 扩展
2. C++ 与 QML 交互
3. 开发时要尽量避免使用的 QML 元素
4. demo 讲解
5. QML 语法
C++ 与 QML 的交互是通过注册 C++ 对象给 QML 环境得以实现的:在 C++
实现中,非可视化的类型均为 QObject 的子类,可视化的类型均为 QDeclarativeIt
em 的子类。
注意: QDeclarativeItem 等同于 QML 的 Item 类。
一. QML 扩展
如果用户想要定义自己的类型,有两种方法:
(一)方法一: 使用 QML 创建新的 QML 类型
1. 用 QML 实现新定义的类型;
2. 在 QML 中,导入 1 中实现的新的 QML 类型; (如果定义文件和使用文件不
在同级目录,只要在使用文件中导入定义文件所在的目
3. 在 QML 中,使用自己定义的类型
例子: MyButton_qml
(二)方法二: 使用 C++创建新的 QML 类型
1. 在 C++中,实现派生于 QObject 或 QDeclarativeItem 的子类,它是新定义
2. 在 C++中,将 1 中实现的新 item 类型注册给 QML;
3. 在 QML 中,导入含有 1 中定义的新 item 的模块;
4. 在 QML 中,向使用标准的 item 一样使用新定义的 item
现举例说明,如何在 QML 中使用用 C++ 实现的 MyButton 对象(如下 qml 代
码),该对象有自己的属性、方法以及信号。用法与使用其它标准的 QML 元素
一样,所需要做的是导入包含 MyButton的对应模块名称及其版本“MyItems 1.0 ”。
//main.qml
import Qt 4.7
import MyItems 1.0
Item
{
300; height: 200
MyButton
{
id: myButton
/*
* 注意:x, y, width, height 是继承自 item 的属性,
* 无需再自定义的 item 中实现
*/
x: 50; y: 50
600; height: 100
color: "gray" //自定义属性
onMySignals: color = "red" //自定义信号 mySignals
MouseArea
{
anchors.fill: parent
// 调用 C++定义的方法 myColor
onClicked: parent.myColor()
}
}
}
为了能够上述 qml 代码工作,需要为在 Qt C++代码中注册 MyButton 及其所
属的模块,对应的 main.cpp 代码如下:
#include <QtGui/QApplication>
#include <QtDeclarative/QDeclarativeComponent>
#include <QtDeclarative/QDeclarativeView>
#include "mybuttonitem.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QDeclarativeView viewer;
/*
* MyButtonItem 是与 QML 中 MyButton 相对应的 C++实现的类名称
* 1,0 是版本信息;MyItems 是 MyButton 所属的模块名称
*/
qmlRegisterType<MyButtonItem>("MyItems", 1, 0, "MyButton");
viewer.setSource(QString("qml/MyButton/main.qml"));
viewer.show();
return app.exec();
}
上面我们在 QML 中 MyButton 对象,有自己的属性、方法以及信号,其实现均
来自 Qt C++。Qt C++需要作以下工作:
1. 首先要定义与 QML 中 MyButton 对象相对应的 C++ 类 MyButtonItem,它
必须继承自QDeclarativeItem。
2. 为了让 MyButton 对象能够使用其 color 属性,MyButtonItem 类需要利用
QT 的 PROPERTY 属性声明color 。
3. 为了让 MyButton 对象能够使用其 myColor 方法,MyButtonItem 类需要声
明该方法,并标记为 Q_INVOKABLE (另外一种方案是将 myColor声明为槽)。
4. 为了让 MyButton对象能够接收 C++所 emit 的信号,在C++类 MyButtonIt
em中需要声明 mySignals 信号。并且在QML中使用 onMySignals接收信号。
#ifndef MYBUTTONITEM_H
#define MYBUTTONITEM_H
#include <QDeclarativeItem>
class MyButtonItem : public QDeclarativeItem
{
Q_OBJECT
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
signals:
void colorChanged();
void mySignals();
public:
MyButtonItem(QDeclarativeItem *parent = 0);
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget = 0);
//Alternatives for myColor to be called from QML
//public slots
public:
//QColor myColor() const;
const QColor color() const;
private:
void setColor(const QColor &newColor);
QColor m_color;
};
Q_INVOKABLE QColor myColor() const;
#endif // MYBUTTONITEM_H
二.C++与 QML 交互
C++ 与 QML 的交互是通过注册 C++ 对象给 QML 环境得以实现的:
在 C++实现中,非可视化的类型均为 QObject 的子类,可视化的类型均为 QDecla
rativeItem 的子类。注意: QDeclarativeItem 等同于 QML 的Item 类。
(一)导出 C++类到 QML
具体导出过程:假设我们要导出一个 MyButtonItem 类
1. 那么就要考虑怎样的类他才可以导出呢? 他需要符合一定的条件:
(1) 继承自 Qobject 或 QDeclarativeItem
(2) 有默认构造函数
2. 如何导出呢? 通过函数
int qmlRegisterType(const char *uri, int versionMajor, int versionMinor,
const char *qmlName)导出该类,要使用该函数必须包含头文件
#include <QtDeclarative/QDeclarativeComponent>
例:/*
* MyButtonItem 是与 QML 中 MyButton 相对应的 C++实现的类名称
* 1,0 是版本信息;MyItems 是 MyButton 所属的模块名称
*/
qmlRegisterType<MyButtonItem>("MyItems", 1, 0, "MyButton");
含义: 向 QML 中导出 MyButtonItem 类,这个类在 MyItems 包中,在 QML 中
需要使MyButtonItem 类的话就必须包含 MyItems 包,通过 import MyItems 1.0
来包含,之后在 QML 中就可以使用 MyButton 来创建对象。
(二)导出 C++ 类中的成员方法
导出方法:
1. 使用 Q_INVOKABLE声明函数
2. 使用槽机制
为了让 MyButton 对象能够使用其 myColor 方法,MyButtonItem 类需要声明
该方法,并标记为 Q_INVOKABLE (另外一种解决方案是将 myColor声明为槽)。
(三)导出 C++ 类中的属性
导出方法: 使用 Q_PROPERTY 宏声明它的属性,参考了一下 qt 文档关于
Q_PROPERTY()宏,定义格式如下:
Q_PROPERTY( type name
READ getFunction
[WRITE setFunction]
[RESET resetFunction]
[NOTIFY notifySignal]
[DESIGNABLE bool]
[SCRIPTABLE bool]
[STORED bool]
[USER bool]
[CONSTANT]
[FINAL])
Q_PROPERTY() 是一个宏,用来在一个类中声明一个属性 property,由于该宏
是 qt 特有的,需要用 moc 进行编译,故必须继承于 QObject 类。在外界看来,pro
perty 跟类中数据成员没有什么区别,但是还是有几点不一样,参考 qt 文档,主要
有以下几点:
(1).必须有一个 read 函数。
(2).有一个可选的 write 函数。
(3).NOTIFY 声明一个信号。
(4).一个 reset 函数能够把 property 设置成其默认状态。
(5) .一个"desinable"属性表明该 property 能在 GUI builder(一般为 Qt Desig
ner)可见。
(6).如果定义了"stored"属性表明这是一直存在的。
注意:
1. 当需要实现属性变化其他引用到此属性的属性也跟着变化的情况的话,需
要设置属性相应的信号。
2. QML 中设置属性的时候,使用的类型必须是已经导出到 QML 中的类型。
(四)QML 获取此 C++对象的数据
步骤:
1. viewer.rootContext()->setContextProperty("ps",QColor(Qt::red));
2. 在 QML 中使用数据 ps.
QML 组件在 QDeclarativeContext 中实例化。一个上下文(context)允许程序
暴露数据给 QML 组件实例。一个 QDeclarativeContext 可用于创建应用程序中
用到的所有对象实例,如果需要精确控制为每个实例暴露的数据,可以创建多个
QDeclarativeContex。向 QML 组件实例暴露数据,通过 QML 属性绑定(Property
Bindings)和 JavaScript 对象访问应用程序设置上下文属性(context properties)。
下面的例子展示了如何通过 QDeclarativeView 暴露背景颜色给 QML 文件:
// main.cpp
#include <QApplication>
#include <QDeclarativeView>
#include <QDeclarativeContext>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QDeclarativeView view;
view.rootContext()->setContextProperty("bgColor",QColor(Qt::yellow));
view.setSource(QUrl::fromLocalFile("main.qml"));
view.show();
return app.exec();
}
如果你只希望在 main.cpp 里创建组件,不想显示在 QDeclarativeView 中,需
要使用 QdeclarativeEngine::rootContext() 来创建 QDeclarativeContext 实例:
QDeclarativeContext *windowContext = new QDeclarativeContext(engine.rootContext());
windowContext->setContextProperty("bgColor", QColor(Qt::yellow));
QDeclarativeComponent component(&engine, "main.qml");
QObject *window = component.create(windowContext);
(五) C++中调用 QML 中的函数和设置 QML 中的属性
用例子讲解:qmlc++
三. 开发时要尽量避免使用的 QML 元素
1. 渐变元素 Gradient 不能使用,需要使用渐变时可以使用图片代替;
2. 任何元素中的 radius 和 border 属性不能使用;
3. BorderImage 和 Image 元素中不能使用 smooth 属性;
4. BorderImage 和 image 元素中 fillmode 属性不能用,设置变长图片时直接
设置其width 和 height 元素即可;
5. state 元素不能使用,可以使用 binding 元素代替或者 javascript 表达式。
四. Demo 讲解
五.QML 的基本语法
QML 看起来像这样
import Qt 4.7
Rectangle
{
200
height: 200
color: "blue"
Image
{
source: "pics/logo.png"
anchors.centerIn: parent
}
}
对象是通过类型而直接被指定的,紧随其后的是一对大括号。对象类型总是
以大写字母开头。在上面的例子中,存在两个对象 Rectangle 和 Image。在大括
号之间, 我们可以指定对象的相关信息,例如它的属性。属性是通过“property: va
lue”这样的方式被呈现的。在上面的例子中,我们可以看到Image 拥有一个属性
叫做 source,它被分配了一个值叫 做"pics/logo.png"。属性和值被冒号分隔。
属性可以被一行行指定:
Rectangle
{
100
height: 100
}
同时也可以在一个单行上指定多个属性:
Rectangle { 100; height: 100 }
当多个 property/value 对被指定在一个单行上时,他们需要通过分号来分隔。
import 声明是为了导入包含所有标准的 QML 元素的 Qt 模块。没有这个导
入声明,Rectangle和 Image 元素都将不能被使用。
表达式
除了直接给属性指定值之外,你也可以像在 JavaScript 中一样通过表达式来
指定。
Rotation{ angle: 360 * 3 }
这些表达式可以包含其他对象和属性,这样子的操作将会产生一个绑定关系,
当这个表达式的值发生改变时,已经通过表达式指定了的属性的值也会自动更
新到那个值。
Item
{
Text
{
id: text1
text: "Hello World"
}
Text
{
id: text2
text: text1.text
}
}
在上面的这个例子中,text2 对象将会显示和 text1 一样的文字.如果
text1 改变了,text2 也会自动变化为相同的值。在引用其他对象时,可以
通过对象的 id 值,来进行引用。
QML 注释
QML 中的注释方式和 Javascript 的相同。单行注释使用//;多行注释
使用 /* ... */ 。
Text
{
text: "Hello world!"/*print text*/
//opacity: 0.5
}
属性
属性命名: 属性通常以小写字符开头(附加属性除外)
属性类型: QML 支持多种属性(具体参看 QML Basic Types)。基本的
属性包括 int, real, bool, string, color 和 lists。
Item
{
x: 10.5
// a 'real' property
...
state: "details"
focus: true
// a 'string' property
// a 'bool' property
}
QML 的属性是属于类型安全的,也就是说属性的类型必须和所分配的值是相
同的。例如,Item 的 x 属性是一个 real,如果你想要给他分配一个string 值,就会
得到一个错误。
Item { x: "hello" // illegal! }
id 属性
每个对象都可以指定一个专门的属性叫做 id,这个属性的值必须是唯一的。
在同一个 QML文档中不能有相同的 id 值。通过指定一个唯一可用的 id,对象就
可以被其他对象和脚本引用。
Item
{
Rectangle
{
id: myRect
100
}
Rectangle
{
myRect.width
}
}
id 必须以小写字母或者下划线开头,并且不能包含除字母、数字和下划线
以外的其他字符