需求
qt 中 property
和 Q_PROPERTY
如何使用?
解决
qml
在 qml 中使用 property
来定义一个对象的属性。具体语法如下:
[default] [required] [readonly] property <propertyType> <propertyName>
特性
- 类似于成员变量,不同的是可以初始化,并且没有public、private、 protected等限制。
- 可以使用
onXXXChnaged
作为这个属性的信号处理函数.property string someText onSomeTextChanged: console.log("The someText will be: " + someText)
- properName以一个小写字母开头,只能包括字母、数字和下划线。
propertyType可以是QML基本类型,enumeration以int来代替,也可以是QML对象类型,神奇的var类型是泛型的,支持任何类型的属性值.
Item { property int theNumber property string theString property url theUrl property Item someItem property Rectangle someRectangle property var someNumber: 1.5 property var someString: "abc" property var someBool: true property var someList: [1, 2, "three", "four"] property var someObject: Rectangle { width: 100; height: 100; color: "red" } }
- 属性值可以被初始化,也可以使用JavaScript表达式来赋值,通过这两种方式赋值时,可以是一个静态值,也可以是一个与其它属性绑定的值。
Rectangle { id: rootRect property color theColor: "green" property color previousColor: rootRect.color property color nextColor width: 100; height: 100 color: "red" Component.onCompleted: { rootRect.nextColor = rootRect.color console.log(theColor, previousColor, nextColor, rootRect.color) } } QtObject { id: root property QtObject viewer: MainView{} }
分类
- 自定义属性
property <propertyType> <propertyName> [ : <value> ]
- 列表属性, children属性就是一个列表属性,包含在一对方括号中,里面的元素必须是QML对象类型而不能是QML基本类型,并以逗号分隔。列表内的元素可通过数组下标[index]访问,元素个数由length属性提供。若列表内只有一个元素,方括号可省略。
Item { children: [ Text { text: "textOne" }, Text { text: "textTwo" }, Text { text: "textThree" } ] Component.onCompleted: { for(var i = 0; i < children.length; i++) console.log("text of lable", i, ":", children[i].text) } }
- 自定义列表属性
Item { property list<Rectangle> siblingRects property list<Rectangle> childRects: [ Rectangle { color: "red" }, Rectangle { color: "green" }, Rectangle { color: "blue"} ] Component.onCompleted: { for(var i = 0; i < childRects.length; i++) console.log("color of lable", i, ":", childRects[i].color) } }
- 分组属性, 在某些情况下,属性可以是由若干个子属性构成的一个逻辑组,我们可以用“.”符号或者分组符号对其进行赋值。
Text { //dot notation font.pixelSize: 12 font.bold: true } Text { //group notation font { pixelSize: 12; bold: true } }
- 属性别名, 属性别名引用其它的属性,语法如下:
[default] property alias <name>: <alias reference>
- 默认属性, 任何基于Item的类型都有一个data列表属性,这个属性就是该类型的默认属性,保存了所有的孩子对象,其中可视的孩子对象又保存在了 children列表属性中,不可视的孩子对象保存在了resources列表属性中,在添加子对象时children、resources属性可写可不写,都会自动添加到对应的属性列表中。
- 只读属性, 只读属性必须初始化,且不能修改,也不能是default属性和alias
属性.
readonly property <propertyType> <propertyName> : <initialValue>
- 附加属性, QML语言还包括了一些附加属性和信号处理器,由相应的附加类型提供,使用语法如下:
<AttachingType>.<propertyName> <AttachingType>.on<SignalName>
cpp
QObject 及其子类中用 Q_PROPERTY 宏就可以使用属性系统的代码如下所示:
Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)
上面这一行代码就声明了一个 cursor 属性,指明了它是 QCursor 类型的,而且指明了需要用自己的 cursor() 函数来读取这个属性值,指明了用自己的 setCursor() 函数来修改属性值,还指明了用自己的 unsetCursor() 函数进行默认值设置。一行语句就把一个属性声明好了,代码还算是很简洁的。
常用的关键字
READ
,WRITE
, 后面连接具体的对于属性的读写函数MEMBER
, 申明属性是类的成员变量RESET
,NOTIFY
, 属性更新时发出的信号
读写
-
read, write 使用时,属性可以不是类的成员变量。只需要有相应的函数即可。
class Widget : public QObject { Q_OBJECT Q_PROPERTY(bool focus READ hasFocus WRITE setFocus NOTIFY focusChanged) public: Widget(QObject *parent = 0); ~Widget(); bool hasFocus() const { return m_focus; } void setFocus(bool on) { m_focus = on; } signals: void focusChanged(); private: bool m_focus; }
Widget *w = new Widget; w->setFocus(true);
-
member 使用时,必须是类的成员变量,可以不需要有相应的函数。
Class Widget : public QObject { Q_PROPERTY(bool focus MEMBER m_focus) Q_OBJECT private: bool m_focus; }
Widget *w = new Widget; w->property("focus"); w->setProperty("focus", true);
关联信号
除了 NOTIFY 之外还有 RESET、REVISION、DESIGNABLE、SCRIPTABLE、STORED、 USER、CONSTANT、FINAL。这些关键字的含义及用法参考官方文档 The Property System。
Class Widget : public QObject
{
Q_PROPERTY(bool focus MEMBER m_focus NOTIFY focusChanged)
Q_OBJECT
public:
bool hasFocus() const;
void setFocus(bool on);
signals:
void focusChanged();
private:
bool m_focus;
}
获取属性名称和值
MEMBER
是需要知道类中有这个属性值的,那么如果不知道类中有什么属性值时,可以通过 Qt 元对象系统中的 QMetaObject
, QMetaProperties
来获取属性和值。
Widget *w = new Widget;
const QMetaObject *metaobject = w->metaObject();
int count = metaobject->propertyCount();
for (int i = 0; i < count; ++i) {
QMetaProperty metaproperty = metaobject->property(i);
const char *name = metaproperty.name();
QVariant value = w->property(name);
...
}
运行中临时添加属性
QObject::setProperty()
函数也可以用于在运行期添加新的属性。如果对象中已经存在该属性,则新添加的属性值会更改原有值并返回true;如果对象中不存在该属性,那么会自动添加到QObject中,注意了,此时返回值仍有可能是 false。所以根据返回值是不能确定属性值是否被设置成功。- 在已知类中存在某属性的情况下,可以根据返回值判断是否设置成功。如果添加新的属性值,就不能用返回值判断是否设置成功。
- 因为是在程序运行过程中新增的属性,所以这个属性可以理解为是“临时的”。它们只会加到 QObject 实例中,不会加到最为核心的 QMetaObject 实例中。就好比一个公司已经发展起来了,后来新入职的员工就不是核心人员。
- 那么要想删除这个“临时的”属性,只需要掉用 QObject::setProperty() 函数将空的 QVariant 值传进去即可。
自定义属性类型
自定义的属性类型,需要用 Q_DECLARE_METATYPE 宏进行注册,这样就可以存储在QVariant中了。
给类添加额外的属性信息
除了正规常用的属性外,我们还可以用 Q_CLASSINFO 宏给类添加额外的属性信息,语法就是“键值-对”形式。例如: Q_CLASSINFO("Version", "3.0.0")
,
那么在程序运行的过程中,随时都可以调用 QMetaObject::classInfo() 函数来获取这些额外属性信息。