QT之Q_PROPERTY介绍以及在QWidget中的用法
目录
一、 Q_PROPERTY介绍
1、 Q_PROPERTY是什么
2、Q_PROPERTY 的语法
3、Q_PROPERTY 的作用
二、在QWidget中的用法
1、示例1:自带属性
2、 示例2:自建属性第一种
3、 示例3:自建属性第二种
4、动态属性
三、注意事项
四、总结
一、 Q_PROPERTY介绍
1、 Q_PROPERTY是什么
Q_PROPERTY 是 Qt 框架中用于声明类的属性的宏,它允许将类的属性暴露给 Qt 的元对象编译器(moc),从而支持动态属性访问、信号与槽机制、属性动画以及 QML 绑定等功能。Q_PROPERTY 通常用于定义可以在 Qt 的元对象系统中识别的属性,方便在运行时通过 QObject::property() 和 QObject::setProperty() 动态访问或修改。
2、Q_PROPERTY 的语法
Q_PROPERTY 的基本语法如下:
Q_PROPERTY(type name READ getFunction[WRITE setFunction][RESET resetFunction][NOTIFY notifySignal][DESIGNABLE true|false][SCRIPTABLE true|false][STORED true|false][USER true|false][CONSTANT] [FINAL])
- type: 属性的数据类型(如 int、QString、QColor 等)。
- name: 属性的名称。
- READ: 指定获取属性值的 getter 函数(必须)。
- WRITE: 指定设置属性值的 setter 函数(可选)。
- RESET: 指定重置属性值的函数(可选)。
- NOTIFY: 指定当属性值改变时发出的信号(可选)。
- DESIGNABLE: 指示属性是否可以在 Qt Designer 中编辑(默认为 true)。
- SCRIPTABLE: 指示属性是否可以被脚本引擎访问(默认为 true)。
- STORED: 指示属性是否存储在对象中(默认为 true)。
- USER: 指示属性是否是用户可编辑的主要属性(默认为 false)。
- CONSTANT: 指示属性是只读常量(不能有 WRITE 或 NOTIFY)。
- FINAL: 指示属性不可被子类重写。
下面是官方给出的对应属性(详见QT帮助文档中<the property system>):
- 属性的行为类似于类的数据成员,但它具有通过元对象系统访问的其他特性。
- 如果没有指定成员变量,则需要一个读访问器函数。它用于读取属性值。理想情况下,const函数用于此目的,它必须返回属性的类型或指向该类型的const引用。例如,QWidget::focus是一个只读属性,具有读取函数QWidget::hasFocus()。
- 写访问器函数是可选的。它用于设置属性值。它必须返回void,而且必须只有一个参数,要么是属性的类型,要么是指向该类型的指针或引用。例如,QWidget::enabled具有写函数QWidget::setEnabled()。只读属性不需要写函数。例如,QWidget::focus没有写功能。
- 如果没有指定读访问器函数,则必须关联成员变量。这使得给定的成员变量可读可写,而无需创建读写访问器函数。如果你需要控制变量访问,除了关联成员变量之外,也可以使用读写访问器函数(但不能同时使用两者)。
- 重置功能是可选的。它用于将属性设置回其上下文特定的默认值。例如,QWidget::cursor具有典型的读写函数,QWidget::cursor()和QWidget::setCursor(),它还有一个重置函数,QWidget::unsetCursor(),因为没有调用QWidget::setCursor()可能意味着重置上下文特定的游标。RESET函数必须返回void且不接受任何参数。
- 通知信号是可选的。如果定义了,它应该指定类中存在的一个信号,每当属性的值发生变化时,该信号就会发出。成员变量的通知信号必须接受零个或一个参数,该参数的类型必须与属性相同。这个参数将接受属性的新值。NOTIFY信号只应该在属性真正发生更改时发出,以避免在QML中不必要地重新计算绑定。当需要的成员属性没有显式设置方法时,Qt会自动发出这个信号。
- 版本号是可选的。如果包含,它将定义在特定版本的API(通常用于QML)中使用的属性及其通知器信号。如果不包含,则默认为0。
- DESIGNABLE属性表示该属性是否应该在GUI设计工具(例如Qt Designer)的属性编辑器中可见。大多数属性都是可设计的(默认为true)。除了true或false之外,还可以指定一个布尔成员函数。
- SCRIPTABLE属性表示脚本引擎是否可以访问该属性(默认为true)。除了true或false之外,还可以指定一个布尔成员函数。
- STORED属性表示该属性是独立存在的,还是依赖于其他值。它还指示在存储对象的状态时是否必须保存属性值。大多数属性都是被存储的(默认值为true),但例如QWidget::minimumWidth()被存储为false,因为它的值是从属性QWidget::minimumSize()的宽度组件中获取的,这是一个QSize。
- USER属性指示该属性是指定为类的面向用户属性还是用户可编辑属性。通常,每个类只有一个USER属性(默认为false)。例如,QAbstractButton::checked是(可检查)按钮的用户可编辑属性。注意,QItemDelegate获取和设置小部件的USER属性。
- 常量属性的出现表明该属性的值是常量。对于给定的对象实例,常量属性的READ方法必须每次调用都返回相同的值。这个常量的值可能因对象的不同实例而不同。常量属性不能有写方法或通知信号。
- 最终属性的存在表明该属性不会被派生类覆盖。这在某些情况下可用于性能优化,但moc并不强制执行。必须注意,永远不要覆盖FINAL属性。
- REQUIRED属性的出现表明该属性应该由类的用户设置。这不是moc强制的,并且对暴露给QML的类非常有用。在QML中,除非设置了所有必需的属性,否则具有必需属性的类无法实例化。
- READ、WRITE和RESET函数可以继承。它们也可以是虚拟的。在使用多重继承的类中继承时,它们必须来自第一个继承的类。
- 属性类型可以是QVariant支持的任何类型,也可以是用户定义的类型。
3、Q_PROPERTY 的作用
- 动态属性访问:通过 QObject 的 property() 和 setProperty() 方法动态读写属性。
- 信号与槽:通过 NOTIFY 指定的信号,属性变化时可以触发槽函数。
- QML 集成:Q_PROPERTY 允许 C++ 类的属性直接在 QML 中访问和绑定。
- Qt Designer 支持:在 Qt Designer 中,Q_PROPERTY 声明的属性可以显示并编辑。
- 属性动画:属性可以被 Qt 的动画框架使用。
二、在QWidget中的用法
1、示例1:自带属性
新建一个QWidget Application,在UI界面中添加几个QLineEdit和QPushButton,如下面所示:
// Widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_btn1_clicked();private:Ui::Widget *ui;
};
#endif // WIDGET_H// Widget.cpp
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);}Widget::~Widget()
{delete ui;
}void Widget::on_btn1_clicked()
{auto str = ui->lineEdit->property("text").toString();ui->lineEdit_2->setText(str);ui->lineEdit_3->setProperty("text",str);
}
通过QObject::property("text").toString()获取属性值,通过QObject::setProperty("text",str);达到设置属性值的作用,与QObject::setText(str);达到了同样的效果。
点击按键,运行结果如下:
2、 示例2:自建属性第一种
// Widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QObject>QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTQ_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)public:Widget(QWidget *parent = nullptr);~Widget();QString name() const { return m_name; }void setName(const QString &name){if (m_name != name) {m_name = name;emit nameChanged(m_name);}}signals:void nameChanged(const QString text);private slots:void on_btn1_clicked();private:Ui::Widget *ui;QString m_name;
};
#endif // WIDGET_H// Widget.cpp
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);connect(this, &Widget::nameChanged, ui->label, &QLabel::setText, Qt::AutoConnection);}Widget::~Widget()
{delete ui;
}void Widget::on_btn1_clicked()
{this->setProperty("name", ui->lineEdit->text());ui->lineEdit_2->setText(this->property("name").toString());ui->lineEdit_3->setProperty("text",this->property("name").toString());
}
创建一个属性:
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
其中:READ name是读属性值,WRITE setName是设置属性值,NOTIFY nameChanged是发送信号。
当点击按键后,首先设置自定义属性值,然后依次调用方法设置文本,调用属性设置文本,以及通过信号和槽绑定属性修改Label的文本,结果如下:
3、 示例3:自建属性第二种
// Widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QObject>QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTQ_PROPERTY(QString name MEMBER m_name READ name2 WRITE setName2 NOTIFY nameChanged)public:Widget(QWidget *parent = nullptr);~Widget();QString name() const { return m_name; }void setName(const QString &name){if (m_name != name) {m_name = name;emit nameChanged(m_name);}}QString name2() const { return m_name; }void setName2(const QString &name){m_name = name + " name2";emit nameChanged(m_name);}signals:void nameChanged(const QString text);private slots:void on_btn1_clicked();void on_btn2_clicked();private:Ui::Widget *ui;QString m_name;
};
#endif // WIDGET_H// Widget.cpp
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);connect(this, &Widget::nameChanged, ui->label, &QLabel::setText, Qt::AutoConnection);
}Widget::~Widget()
{delete ui;
}void Widget::on_btn1_clicked()
{this->setName(ui->lineEdit->text());ui->lineEdit_2->setText(name());ui->lineEdit_3->setProperty("text",name());
}void Widget::on_btn2_clicked()
{this->setProperty("name", ui->lineEdit->text());ui->lineEdit_2->setText(this->property("name").toString());ui->lineEdit_3->setProperty("text",name());
}
创建一个属性:
Q_PROPERTY(QString name MEMBER m_name READ name2 WRITE setName2 NOTIFY nameChanged) 其中 :MEMBER m_name指定变量m_name, READ name2是读属性值,WRITE setName2是设置属性值,NOTIFY nameChanged是发送信号。
当点击按键后,首先设置自定义属性值,然后依次调用方法设置文本,调用属性设置文本,以及通过信号和槽绑定属性修改Label的文本。当按下Start1结果如下:
当按下Start2,运行结果如下:
通过上述方法可以实现复杂的属性设置。
4、动态属性
动态属性可以在代码中添加或者是在Qt Designer中进行添加,如图所示:
接着可以输入属性名称及类型。
代码中添加动态属性如下:
// Widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QObject>QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTQ_PROPERTY(QString name MEMBER m_name READ name WRITE setName NOTIFY nameChanged)public:Widget(QWidget *parent = nullptr);~Widget();QString name() const { return m_name; }void setName(const QString &name){if (m_name != name) {m_name = name;emit nameChanged(m_name);}}signals:void nameChanged(const QString text);private slots:void on_btn1_clicked();private:Ui::Widget *ui;QString m_name;
};
#endif // WIDGET_H// Widget.cpp#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);connect(this, &Widget::nameChanged, ui->label, &QLabel::setText, Qt::AutoConnection);ui->lineEdit->setProperty("string","this is string");
}Widget::~Widget()
{delete ui;
}void Widget::on_btn1_clicked()
{this->setProperty("name",ui->lineEdit->property("string").toString());ui->lineEdit_2->setText(ui->lineEdit->property("string").toString());ui->lineEdit_3->setProperty("text",ui->lineEdit->property("string").toString());
}
运行结果如下:
三、注意事项
- moc 编译:Q_PROPERTY 依赖 Qt 的元对象编译器(moc),确保类中包含 Q_OBJECT 宏。
- 属性类型:属性类型必须是 Qt 支持的类型(如基本类型、QString、QColor)或已注册的自定义类型(通过 Q_DECLARE_METATYPE)。
- 性能:频繁更改属性可能导致多次信号发射和重绘,需优化 setter 逻辑。
- QML 集成:如果计划在 QML 中使用,确保属性类型和信号与 QML 兼容。
四、总结
Q_PROPERTY 是 Qt 中强大的工具,用于声明和管理类的属性,特别适合在 QWidget 自定义控件中扩展功能。通过 Q_PROPERTY,可以实现:
- 动态属性访问和修改。
- 属性变化的信号通知。
- 与 Qt Designer 和 QML 的无缝集成。
- 支持动画和脚本化操作。
在 QWidget 中使用 Q_PROPERTY 的典型场景包括自定义控件的外观(如颜色、大小)、状态(如启用/禁用)或其他用户可配置的属性。通过合理设计 getter、setter 和信号,可以让自定义控件更灵活且易于集成到 Qt 生态中。