标题一、Q_PROPERTY的简介
Q_PROPERTY与信号槽(signals/slots)同属Qt Meta Object System-元对象系统,前者提供了动态属性封装,后者提供了方便的对象间通信功能。所以想要实现Q_PROPERTY功能也要继承QObject类并添加Q_OBJECT关键字。
二、Q_PROPERTY的用处
Q_PROPERTY的第一个显而易见的用处是它在代码文件与UI文件之间建立了联系,查看Qt源文件也可以看到,QWidget中定义的大部分属性,也可以在UI文件中找的到,且基本都是可以设置的。
不过在实际工作中,对于自定义的属性还需要手动在UI文件中键入关键字,无法自动生成,存在一定错误的风险。
Q_PROPERTY的第二个作用是提供给脚本(比如QtScript,QML)和元对象系统(比如QObject::property/setProperty)使用。[①]
三、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])
常用关键字解释如下:[②]
-
READ、WRITE、MEMBER 三个关键字指定了属性的读写性。Q_PROPERTY声明必须有一个READ或MEMBER关键字,否则moc时将会告警。
-
READ关键字用来读取属性值。因此用const限定。它的返回值类型必须为属性类型或者属性类型的引用或者指针,不能是其他类型,例如:QWidget::hasFocus()。
-
WRITE关键字可选,它用来设置属性值,它的返回值必须为void型,而且必须要含有一个参数,例如:QWidget::setEnabled()。
-
MEMBER 用于将类中已有的成员变量设置为属性值,在你偷懒不想编写读写函数时可以使用。
-
RESET函数能够把property设置成其默认状态,它也是可选的。复位功能必须返回void,并且不带参数。
-
一个NOTIFY信号是可选的,如果定义,它要求提供一个信号。这个信号在值发生改变时会自动被触发,如QWidget::windowTitleChanged。
-
如果定义了"STORED"属性表明这是一直存在的,如QWidget::minimumSize。
-
"DESIGNABLE"属性表明该property能在GUI builder(一般为Qt Designer)可见,不带该参数时默认可见,如QWidget::visible。
-
USER 属性,表明是否可以被用户所编辑,如QLineEdit::text
-
CONSTANT 设定属性是不可修改的 所以不能跟WRITE或者NOTIFY同时出现
-
FINAL 表明该属性不会被派生类中重写。
四、Q_PROPERTY的使用实例
项目中经常会遇到一类样式如下,要求程序对密码输入框进行检查,如果输入不合法则输入框的边框会变红。也就是说输入框实际存在两种状态——正常和错误,那我们用property搭配qss的属性选择器来实现此功能。
我们新建一个CheckLineEdit类,继承于QLineEdit,并添加status属性
#pragma once
#include <QObject>
#include "QLineEdit"
class CheckLineEdit : public QLineEdit
{
Q_OBJECT
Q_PROPERTY(QString status MEMBER checkStatus WRITE setCheckStatus READ getCheckStatus)
public:
CheckLineEdit(QWidget *parent);
~CheckLineEdit();
void setCheckStatus(const QString&checkStatus);
QString getCheckStatus() const { return this->checkStatus; }
private:
QString checkStatus;
private slots:
void slotSanityCheck();
};
在cpp文件中,我们以6个字符为阈值,设定自定义的检查规则如下:
#include "CheckLineEdit.h"
#include <QStyle>
CheckLineEdit::CheckLineEdit(QWidget *parent)
: QLineEdit(parent)
{
connect(this, SIGNAL(textChanged(QString)), this, SLOT(slotSanityCheck()));
}
CheckLineEdit::~CheckLineEdit()
{
}
void CheckLineEdit::setCheckStatus(const QString&checkStatus)
{
this->checkStatus = checkStatus;
}
void CheckLineEdit::slotSanityCheck()
{
if (this->text().length() < 6)
{
this->setProperty("status", "Normal");
}
else
{
this->setProperty("status", "Warning");
}
this->style()->polish(this);
}
之后在其他QWidget中放置并提升一个CheckLineEdit,并设置自定义的qss样式如下:
QLineEdit[status = "Normal"]
{
border: 1px solid #CCCCCC;
}
QLineEdit[status = "Warning"]
{
border: 1px solid #FF3E33;
}
运行查看效果如下:
可以看到,输入框的颜色跟随输入文本的长度变化,我们实现了需要的功能。
之后,我们做一个小尝试,尝试在UI文件中设置自定义属性并让其实时生效
打开已经提升过的QWidget文件,在属性编辑器中加入我们之前设置好的属性“status”,之后修改它的值为“Warning”看看会发生什么。
可以看到,这里设置了动态属性后,UI实时预览了我们自定义的属性样式,根源是我们在UI样式中使用了对应的属性选择器,又新增了动态属性status。
此时我们再次运行程序后,setCheckStatus函数将会被调用,且输入框直接显示Warning时的样式,这样再次印证了我们前文所述的“代码与UI文件的联系”。
最后碍于篇幅,这里将C++与QML脚本语言交互的内容[⑤]按下不表,有兴趣的读者可以自行查看参考资料五,此二者交互中也体现出了Q_PROPERTY的妙用。
五、参考资料
① QT之Qt之Q_PROPERTY宏理解 - 我来乔23 - 博客园 (cnblogs.com)
② 传说中的Q_PROPERTY怎么使用_qq303103757的博客-CSDN博客_q_property
③【Qt】Q_PROPERTY():属性系统_郭老二的博客-CSDN博客_q_property