【Qt】动态属性详解

🔥博客主页 小羊失眠啦.
🎥系列专栏《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 宏所定义的成员中

相同点:

访问方式相同(读属性,写属性)

  • 17
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小羊失眠啦.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值