🔥博客主页: 小羊失眠啦.
🎥系列专栏:《C语言》 《数据结构》 《C++》 《Linux》《MySQL》《Qt》
❤️感谢大家点赞👍收藏⭐评论✍️
一、Qt的动态属性的概念
1.1 C++对属性的封装
C++为了实现封装,对“属性”一般处理方法:
定义一个 private 变量
定义一个 public 函数,实现读(get 方法)
定义一个 public 函数,实现写(set 方法)
效果:用户不需要了解 get 方法和 set 方法的内部具体实现,只需要了解这两个接口的函数名,就可以实现对属性的读写。
1.2 Qt的动态属性机制
Qt 框架,为了更方便的处理“属性”,使用“动态属性”机制。
效果:用户不需要知道 get 方法和 set 方法, 只需要了解“属性名”,就可以实现对属性的 读写。
二、Qt动态属性的基础用法
MyBoy.h
#ifndef MYBOY_H
#define MYBOY_H
#include <QWidget>
#include <QObject>
//使用动态属性,需要继承自QObject,另外需要使用Q_OBJECT
class MyBoy : public QObject
{
Q_OBJECT
// 属性类型 属性名 读方法 写方法 信号
Q_PROPERTY(QString love READ love WRITE setLove NOTIFY lovechanged)
// WRITE 可选
// setLove 设置属性的方法,可选
// NOTIFY 可选
// NOTIFY 后面跟该类中已经定义的一个信号函数
// 只要该属性的值发生更改,就会发出该信号
// 这个信号函数必须采用零个或一个参数,该参数必须与属性的类型相同。
// loveChanged 可选, 指定的信号
public:
explicit MyBoy(QObject *parent = nullptr);
QString love() const;
void setLove(QString strLove);
signals:
void loveChanged(QString strLove);
private:
QString m_love;
};
#endif // MYBOY_H
MyBoy.cpp
#include "myboy.h"
MyBoy::MyBoy(QObject *parent) : QObject(parent)
{
}
QString MyBoy::love() const
{
return m_love;
}
void MyBoy::setLove(QString strLove)
{
m_love = strLove;
emit loveChanged(strLove);
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
public slots:
void loveChanged(QString strLove);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include "myboy.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
MyBoy* boy = new MyBoy;
connect(boy, &MyBoy::loveChanged, this, &Widget::loveChanged);
// connect(boy, SIGNAL(loveChanged(QString)), this, SLOT(loveChanged(QString)));
//1. 老的方式,访问属性 boy->setLove("踢足球"); qDebug()<<boy->love();
//2. 使用对象本身,来访问属性
boy->setProperty("love", "打篮球");//使用动态属性的方式来修改
qDebug() << boy->property("love").toString();/使用动态属性的方式来获取
//3. 通过父类对象的指针(已经指向子类对象),访问属性
MyBoy* girl = new MyBoy;
girl->setProperty("love", "k歌");
qDebug() << girl->property("love").toString();
QObject* obj = boy;
qDebug() << obj->property("love").toString();
obj = girl;
qDebug() << obj->property("love").toString();
}
Widget::~Widget()
{
delete ui;
}
void Widget::loveChanged(QString strLove)
{
qDebug() << "爱好更改为: " << strLove;
}
三、只读属性
省略 WRITE 和其后的 write 方法,即为只读属性
car.h
#ifndef CAR_H
#define CAR_H
#include <QWidget>
#include <QObject>
class Car : public QObject
{
Q_OBJECT
Q_PROPERTY(QString trademark READ getTrademark)
public:
explicit Car(QString mark, QObject *parent = nullptr);
QString getTrademark();
signals:
private:
QString m_trademark;
};
#endif // CAR_H
car.cpp
#include "car.h"
Car::Car(QString mark, QObject *parent) : QObject(parent)
{
m_trademark = mark;
}
QString Car::getTrademark()
{
return m_trademark;
}
main.cpp
#include "widget.h"
#include "car.h"
#include <QDebug>
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
Car car("奔驰");
qDebug() << car.property("trademark").toString();
// 对只读属性写操作,无效,返回false, 但是并不报错
qDebug() << car.property("trademark", "宝马");
return a.exec();
}
四、最简单的属性
不想写 get 方法和 set 方法,但是又想使用属性系统,可以使用 MEMBER. 属性名和 MEMBER 后面的成员变量名,可以相同!
car.h
#ifndef CAR_H
#define CAR_H
#include <QWidget>
#include <QObject>
class Car : public QObject
{
Q_OBJECT
Q_PROPERTY(int oil MEMBER oil)
public:
explicit Car(QObject *parent = nullptr);
signals:
private:
int oil;
};
#endif // CAR_H
car.cpp
#include "car.h"
Car::Car(QObject *parent) : QObject(parent)
{
}
main.cpp
#include "widget.h"
#include "car.h"
#include <QDebug>
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
Car car;
car.setProperty("oil", 200);
qDebug() << car.property("oil").toString();
car.setProperty("oil", 300);
qDebug() << car.property("oil").toInt();
return a.exec();
}
五、访问类的所有的属性
可以通过 metaObject 等方法,获取一个类(对象)的所有属性。
myboy.h
#ifndef MYBOY_H
#define MYBOY_H
#include <QWidget>
#include <QObject>
class MyBoy : public QObject
{
Q_OBJECT
Q_PROPERTY(QString love READ love WRITE setLove NOTIFY loveChanged)
Q_PROPERTY(QString nickName READ nickName WRITE setNickName)
public:
explicit MyBoy(QObject *parent = nullptr);
QString love() const;
void setLove(QString strLove);
QString nickName() const;
void setNickName(QString strName);
signals:
void loveChanged(QString strLove);
QString m_nickName;
private:
QString m_love;
};
#endif // MYBOY_H
myboy.cpp
#include "myboy.h"
MyBoy::MyBoy(QObject *parent) : QObject(parent)
{
}
QString MyBoy::love() const
{
return m_love;
}
void MyBoy::setLove(QString strLove)
{
m_love = strLove;
emit loveChanged(strLove);
}
QString MyBoy::nickName() const
{
return m_nickName;
}
void MyBoy::setNickName(QString strName)
{
m_nickName = strName;
}
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include "myboy.h"
#include <QDebug>
#include <QMetaProperty>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
MyBoy *boy = new MyBoy;
connect(boy, SIGNAL(loveChanged(QString)), this, SLOT(loveChanged(QString)));
//connect(boy, &MyBoy::loveChanged, this, &MainWindow::loveChanged);
//1. 老的方式,访问属性
boy->setLove("踢足球");
qDebug()<<boy->love();
//2. 使用对象本身,来访问属性
boy->setProperty("love", "打篮球"); //使用动态属性的方式来修改
qDebug() << boy->property("love").toString();//使用动态属性的方式来获取
//3. 通过父类对象的指针(已经指向子类对象),访问属性
MyBoy *boy2 = new MyBoy;
boy2->setProperty("love", "K 歌");
qDebug() << boy2->property("love").toString();//使用动态属性的方式来获取
QObject *obj = boy;
qDebug() << obj->property("love").toString();
obj = boy2;
qDebug() << obj->property("love").toString();
const QMetaObject *ob = boy->metaObject();
int count = ob->propertyCount(); //属性个数
for(int i=0; i<count; i++)
{
//需要包含头文件: #include <QMetaProperty>
QMetaProperty mp = ob->property(i);
qDebug() << mp.name();
}
}
Widget::~Widget()
{
delete ui;
}
void Widget::loveChanged(QString strLove)
{
qDebug() << "爱好更改为: " << strLove;
}
六、在运行时添加新属性
以上我们学习的“属性”,其实都是“静态属性”(也是使用 Q_PROPERTY 在类的定义中添加的属性)
在程序运行时,也可以添加新属性,这种属性称为:动态属性。
使用 setProperty 直接添加属性。 car->setProperty(“Color”, “Red”); 如果 car 没有属性“Color”,那么就添加一个新的动态属性并设置值为“Red” 如果 car 已经有属性“Color”,那么就覆盖原来的属性值。
实例:
car.h
class Car : public QObject
{
Q_OBJECT
public:
explicit Car(QObject *parent = nullptr);
signals:
private:
int oil;
};
car.cpp
#include "car.h"
Car::Car(QObject *parent) : QObject(parent)
{
}
widget.h
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
public slots:
void buttonClicked();
signals:
private:
Ui::Widget *ui;
QPushButton* button;
QLineEdit* lineEditName;
QLineEdit* lineEditValue;
Car* car;
};
widget.cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton* btn = new QPushButton(this);
btn->setGeometry(100,100,100,40);
btn->setText("添加动态属性");
connect(btn, SIGNAL(clicked()), this, SLOT(btnClicked()));
lineEditName = new QLineEdit(this);
lineEditName->setGeometry(100, 20, 100, 40);
lineEditValue = new QLineEdit(this);
lineEditValue->setGeometry(250, 20, 100, 40);
car = new Car;
}
Widget::~Widget()
{
delete ui;
}
void Widget::buttonClicked()
{
car->setProperty(lineEditName->text().toStdString().c_str(), lineEditValue->text());
//而不是 car->metaObject(), 获取静态属性使用 car->metaObject()
auto names = car->dynamicPropertyNames();
int count = names.count();
qDebug() << "count="<<count;
for(auto name : names)
{
qDebug() << name << car->property(name).toString();
}
}
静态属性和动态属性的区别:
不同点:
定义时机不同
定义方法不同
访问所有静态属性、访问所有动态属性的方式不同
保存的位置不同。
动态属性保存在父类 QObject 中定义的成员中 静态属性保存在这个类本身的 Q_OBJECT 宏所定义的成员中
相同点:
访问方式相同(读属性,写属性)