Qt属性系统详细使用教程

此文较长,例子较多,可以结合右侧目录进行查看。

何为属性?人有名字,年龄,性别,这就是人的属性。

同样,面向对象编程的世界里,一切皆对象,对象也该有自己特定的属性。

在Qt中,QObject实现了对于属性的支持,那么派生于QObject的对象,都可以很容易的拥有自己的属性。

注:

使用Qt属性,则必须继承于QObject

一、属性的声明

让我们看看帮助文档中关于属性声明的定义:

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])

简单捋一下,声明属性的必备条件:

  • 继承于QObject
  • 使用Q_OBJECT宏
  • 使用Q_PROPERTY宏

Q_PROPERTY属性宏,参数分为必须部分和可选部分,接下来讲解,具体各部分含义。

二、属性声明之必须部分

其声明形式如下:

type name 
(READ getFunction [WRITE setFunction] |
MEMBER memberName [(READ getFunction | WRITE setFunction)])

翻译过来,就是有2种形式,带MEMBER和不带MEMBER字段;

1. 不带MEMBER字段

形式为:类型+属性名+READ+get函数+WRITE+set函数,其中WRITE+set函数为可选。

我们定义MyObject类,声明age属性,可以像如下书写:

class MyObject : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int age READ getAge WRITE setAge)
    
public:
    int getAge() const
    {
        return _age;
    }
    void setAge(int value)
    {
        _age = value;
    }
private:
    int _age;
};

访问age属性,当调用setProperty设置属性时,setAge()函数会被调用,以将18保存到_age变量;调用property获取属性时,getAge()函数会被调用,以获取_age变量值。可以说属性实际承载者是我们定义的成员变量。

QObject *obj = new MyObject();
obj->setProperty("age", 18);  // _age == 18
int value = obj->property("age").toInt(); // value == 18

若不写WRITE字段,如下:

Q_PROPERTY(int age READ getAge)

则调用setProperty()设置属性时,不会调用setAge(),此时_age值不变,新属性值被丢弃。
而调用property()时与原来一致。

注:

READ字段为必填项。

2. 带MEMBER字段

形式为:类型+属性名+MEMBER+成员变量名+READ+get函数+WRITE+set函数,其中READ+get函数+WRITE+set函数为可选。

我们仍以MyObject类为例进行说明,声明score属性,可以像如下书写:

class MyObject : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int score MEMBER _score)
    
private:
    int _score;
};

后续设置属性就是指setProperty(),获取属性是指property(),下面不再赘述。

这样写,我们设置和获取属性都是成功的。小伙伴可能会觉得奇怪,此处并没有指定get、set函数,值是如何设置到_score变量,并还能正常获取的呢?

这是因为我们使用MEMBER关键字,Qt会自动生成一对getter/setter函数用于访问_score。

下面,如果加上READ、WRITE字段,如下:

Q_PROPERTY(int score MEMBER _score READ getScore WRITE setScore)

那么我们设置属性时就会调用setScore(),获取属性时就会调用getScore()。

如果只加READ,如下:

Q_PROPERTY(int score MEMBER _score READ getScore)

则设置属性时不调setScore(),调用默认生成的setter();获取属性时调getScore()。

如果只加WRITE,如下:

Q_PROPERTY(int score MEMBER _score WRITE setScore)

则设置属性时调setScore(),获取属性时不调getScore(),调用默认生成的getter()。

这个Qt属性声明还是相当灵活的。

基本属性使用,我们已经get到了,接下来,我们来锦上添花。

三、属性声明之可选部分

可选字段如下:

[RESET resetFunction]
[NOTIFY notifySignal]
[REVISION int]
[DESIGNABLE bool]
[SCRIPTABLE bool]
[STORED bool]
[USER bool]
[CONSTANT]
[FINAL]

1. NOTIFY字段

当属性值发生改变后,会发射一个NOTIFY字段声明的信号。

我们可以像如下书写:

class MyObject : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int age READ getAge WRITE setAge NOTIFY ageChanged)
    
public:
    int getAge() const
    {
        return _age;
    }
    void setAge(int value)
    {
        _age = value;
        emit ageChanged(value);
    }
signals:
    void ageChanged(int value);
private:
    int _age;
};

当age属性值发生改变后,发射ageChanged信号。由于信号是我们手动发射,所以对信号的参数没有限制。

另外我们还可以让信号由Qt系统自动发射,即使用MEMBER字段。如下:

class MyObject : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int score MEMBER _score NOTIFY scoreChanged)
signals:
    void scoreChanged(int value);
private:
    int _score;
};

当score属性值发生改变后,自动发射scoreChanged信号。但是对信号定义有限制,即信号参数个数只能为0个或者1个,且参数类型必须与属性类型相同,参数保存的是属性改变后的新值。

其他的可选字段,不是很常用,大家就自行研究吧。

四、支持的属性类型

Qt属性类型可以是QVariant支持的任何类型,或者是用户自定义的类型。

1. 内置类型属性

如下为QVariant支持的内置类型。

enum Type {
    Invalid = QMetaType::UnknownType,
    Bool = QMetaType::Bool,
    Int = QMetaType::Int,
    UInt = QMetaType::UInt,
    LongLong = QMetaType::LongLong,
    ULongLong = QMetaType::ULongLong,
    Double = QMetaType::Double,
    Char = QMetaType::QChar,
    Map = QMetaType::QVariantMap,
    List = QMetaType::QVariantList,
    String = QMetaType::QString,
    StringList = QMetaType::QStringList,
    ByteArray = QMetaType::QByteArray,
    BitArray = QMetaType::QBitArray,
    Date = QMetaType::QDate,
    Time = QMetaType::QTime,
    DateTime = QMetaType::QDateTime,
    Url = QMetaType::QUrl,
    Locale = QMetaType::QLocale,
    Rect = QMetaType::QRect,
    RectF = QMetaType::QRectF,
    Size = QMetaType::QSize,
    SizeF = QMetaType::QSizeF,
    Line = QMetaType::QLine,
    LineF = QMetaType::QLineF,
    Point = QMetaType::QPoint,
    PointF = QMetaType::QPointF,
    RegExp = QMetaType::QRegExp,
    RegularExpression = QMetaType::QRegularExpression,
    Hash = QMetaType::QVariantHash,
    EasingCurve = QMetaType::QEasingCurve,
    Uuid = QMetaType::QUuid,
#if QT_CONFIG(itemmodel)
    ModelIndex = QMetaType::QModelIndex,
    PersistentModelIndex = QMetaType::QPersistentModelIndex,
#endif
    LastCoreType = QMetaType::LastCoreType,

    Font = QMetaType::QFont,
    Pixmap = QMetaType::QPixmap,
    Brush = QMetaType::QBrush,
    Color = QMetaType::QColor,
    Palette = QMetaType::QPalette,
    Image = QMetaType::QImage,
    Polygon = QMetaType::QPolygon,
    Region = QMetaType::QRegion,
    Bitmap = QMetaType::QBitmap,
    Cursor = QMetaType::QCursor,
    KeySequence = QMetaType::QKeySequence,
    Pen = QMetaType::QPen,
    TextLength = QMetaType::QTextLength,
    TextFormat = QMetaType::QTextFormat,
    Matrix = QMetaType::QMatrix,
    Transform = QMetaType::QTransform,
    Matrix4x4 = QMetaType::QMatrix4x4,
    Vector2D = QMetaType::QVector2D,
    Vector3D = QMetaType::QVector3D,
    Vector4D = QMetaType::QVector4D,
    Quaternion = QMetaType::QQuaternion,
    PolygonF = QMetaType::QPolygonF,
    Icon = QMetaType::QIcon,
    LastGuiType = QMetaType::LastGuiType,

    SizePolicy = QMetaType::QSizePolicy,

    UserType = QMetaType::User,
    LastType = 0xffffffff // need this so that gcc >= 3.4 allocates 32 bits for Type
};

除了容器类型外,其他的类型基本都可以直接定义使用,如下:

Q_PROPERTY(QDate date READ getDate WRITE setDate)

注意:

Q_PROPERTY字符串不能包含逗号,因为逗号会分割宏的参数。

所以,对于QMap、QList和QHash等容器类型属性,必须使用QMap作为属性的类型而不是QMap<QString,QVariant>,同样,也只能使用QList和QHash,而不是QList和QHash<QString,QVariant>。

2. 自定义枚举属性

属性类型为自定义枚举时,可能有以下2种需求:

  • 有些枚举是单值,如:
enum Priority { High, Low, VeryHigh, VeryLow };
xxx(High);
  • 而有的可以将多个枚举值进行或运算,多值,如:
enum Align
{
    Left = 0x01,
    Right = 0x02,
    HCenter = 0x04,
    Top = 0x08,
    Bottom = 0x10,
    VCenter = 0x20
};
xxx(Left|Top);

接下来,分别举例说明。

(1)使用Q_ENUM注册单值枚举

若属性类型为自定义枚举,则必须使用Q_ENUM()注册到元对象系统中。

使用Q_ENUM()注册枚举类型,如下:

class MyObject : public QObject
{
    Q_OBJECT
    Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged) 

public:
    enum Priority { High, Low, VeryHigh, VeryLow };
    Q_ENUM(Priority)

    void setPriority(Priority priority)
    {
        _priority = priority;
        emit priorityChanged(priority);
    }
    Priority priority() const
    {
        return _priority;
    }

signals:
    void priorityChanged(Priority priority);

private:
    Priority _priority;
};

由于已经注册在元对象系统中,所以在setProperty()时,可以直接用枚举值的字符串形式作为实参,如下:

QObject *object = new MyObject();
object->setProperty("priority", "VeryHigh"); // _priority == MyObject::VeryHigh
QVariant temp = object->property("priority");
MyObject::Priority pri = temp.value<MyObject::Priority>(); // pri == MyObject::VeryHigh

此时,字符串"VeryHigh"会在内部自动转换为枚举值VeryHigh,并通过调用setPriority(),将m_priority设置为枚举值VeryHigh。

如果枚举类型在其它类中声明,那么需要使用枚举的全名(例如:OtherClass::Priority),而且这个类也必须从QObject派生,并且使用Q_ENUM()宏注册枚举类型。

(2)使用Q_FLAG注册多值枚举

比注册单值枚举要稍微麻烦一点。有以下几个注意事项:

  • 使用Q_DECLARE_FLAGSQ_FLAGQ_DECLARE_OPERATORS_FOR_FLAGS三个宏。
  • 将枚举值定义为2的n次方,保证或操作不会出现交叉,如 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20。
  • 不使用原枚举类型,而是使用Q_FLAG(xx)定义的xx类型。

代码如下:

class MyObject : public QObject
{
    Q_OBJECT
    Q_PROPERTY(Aligns aligns READ aligns WRITE setAligns NOTIFY alignsChanged) // 测试多值枚举

public:
    enum Align
    {
        Left = 0x01,
        Right = 0x02,
        HCenter = 0x04,
        Top = 0x08,
        Bottom = 0x10,
        VCenter = 0x20
    };
    Q_DECLARE_FLAGS(Aligns, Align)
    Q_FLAG(Aligns)

    void setAligns(Aligns aligns)
    {
        _aligns = aligns;
        emit alignsChanged(aligns);
    }
    Aligns aligns() const
    {
        return _aligns;
    }

signals:
    void alignsChanged(Aligns align);

private:
    Aligns _aligns;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(MyObject::Aligns)

测试代码如下:

QObject *object = new MyObject();
object->setAligns(MyObject::Left|MyObject::Bottom); // _aligns == MyObject::Left|MyObject::Bottom
object->setProperty("aligns", "Left|Top"); // _aligns == MyObject::Left|MyObject::Top
QVariant temp = object->property("aligns");
MyObject::Aligns aligns = temp.value<MyObject::Aligns>(); // aligns == MyObject::Left|MyObject::Top

使用方式与Q_ENUM()类似,也是注册枚举类型,但它是把枚举类型作为一个flag集合,也就是,值可以用或操作来合并。

3. 自定义类型属性

若属性类型为自定义类型,则必须使用Q_DECLARE_METATYPE宏注册该类型,使其能够放入QVariant,如下:

class Person
{
public:
    int varA;
    float varB;
    QString varC;
};
Q_DECLARE_METATYPE(Person)

然后,声明此类型属性如下:

class MyObject : public QObject
{
    Q_OBJECT
    Q_PROPERTY(Person person READ getPerson WRITE setPerson NOTIFY personChanged)

public:
    Person getPerson() const
    {
        return _person;
    }
    void setPerson(const Person &person)
    {
        _person = person;
        emit personChanged(person);
    }

signals:
    void personChanged(Person person);

private:
    Person _person;
};

测试代码,如下:

Person person;
person.varA = 10;
person.varB = 5.0;
person.varC = "test";

QObject *object = new MyObject();
object->setProperty("person", QVariant::fromValue(person)); // _person.varA==10,_person.varB==5.0,_person.varC=="test"
QVariant temp = object->property("person");
Person result = temp.value<Person>(); // result.varA==10,result.varB==5.0,result.varC=="test"

注意:

如果需要使用MEMBER字段声明自定义类型属性,则需要为自定义类型重载!=操作符,可能是在设置或者获取属性时,调用Qt自身生成的setter/getter,会间接调用!=进行比较,所以必须要实现,否则编译报错:

在这里插入图片描述

添加重载!=操作符后的,代码如下:

class Person
{
public:
    Person() { }

    bool operator!=(const Person& person)
    {
        if (person.varA == varA &&
            person.varB == varB &&
            person.varC == varC)
            return false;
        else
            return true;
    }

    int varA;
    float varB;
    QString varC;
};
Q_DECLARE_METATYPE(Person)

此时,再使用MEMBER关键字声明Person类型属性,则不会再报错,可以正常运行。声明属性代码如下:

class MyObject : public QObject
{
    Q_OBJECT

    Q_PROPERTY(Person person MEMBER _person NOTIFY personChanged)

public:

signals:
    void personChanged(Person person);

private:
    Person _person;
};

测试代码,如下:

Person person;
person.varA = 10;
person.varB = 5.0;
person.varC = "test";

QObject *object = new MyObject();
object->setProperty("person", QVariant::fromValue(person)); // _person.varA==10,_person.varB==5.0,_person.varC=="test"
QVariant temp = object->property("person");
Person result = temp.value<Person>(); // result.varA==10,result.varB==5.0,result.varC=="test"

五、静态属性与动态属性

使用Q_PROPERTY宏定义的属性,就是静态属性。

后期使用setProperty()动态添加的属性,就是动态属性。

无论静态还是动态,都可以按如下方式,获取所有的属性名和属性值。如下:

QObject *object = ...
const QMetaObject *metaobject = object->metaObject();
int count = metaobject->propertyCount();
for (int i=0; i<count; ++i) 
{
    QMetaProperty metaproperty = metaobject->property(i);
    const char *name = metaproperty.name();
    QVariant value = object->property(name);
    ...
}

参考链接:

《Qt 之属性系统》

《枚举与 QFlags》

《Qt中的枚举变量》

《Qt 中的属性系统》



若对你有帮助,欢迎点赞、收藏、评论,你的支持就是我的最大动力!!!

同时,阿超为大家准备了丰富的学习资料,欢迎关注公众号“超哥学编程”,即可领取。

本文涉及工程代码,公众号回复:40Property,即可下载。

在这里插入图片描述

  • 20
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Qt Creator是一个跨平台的集成开发环境(IDE),用于开发Qt应用程序。下面是Qt Creator 5.9.0的完整使用教程概述。 1. 安装Qt Creator 5.9.0:从Qt官方网站(www.qt.io)下载Qt Creator 5.9.0安装程序,并按照安装向导的指示进行安装。安装完成后,启动Qt Creator。 2. 创建新项目:在Qt Creator中,选择"新建项目",然后选择你要开发的应用类型。例如,选择Qt Widgets应用程序,然后为你的项目命名,选择保存位置。 3. 设计用户界面:通过拖放Qt Creator的设计工具箱中的控件来设计用户界面。调整控件的属性,并使用布局管理器将它们放置在合适的位置。 4. 编写代码:在Qt Creator的源编辑器中,打开生成的源文件。根据你的需求,编写应用程序的逻辑和功能。 5. 构建和运行:在Qt Creator中,选择"构建"菜单中的"构建项目"选项,以编译你的代码并生成可执行文件。然后,点击"运行"按钮来启动你的应用程序。 6. 调试:如果你的应用程序出现错误或异常,可以使用Qt Creator的调试工具来调试代码并查找问题。你可以设置断点、观察变量值等。 7. 部署应用程序:在开发完成后,使用Qt Creator的构建工具生成可执行文件和所需的依赖文件。然后,将这些文件复制到目标计算机上,即可运行。 8. 使用版本控制:Qt Creator集成了一些流行的版本控制系统(如Git),使你能够轻松管理代码。你可以对项目进行版本控制、提交更改和更新等。 9. 资源和文档:Qt Creator提供了许多有用的资源和文档,可帮助你学习和使用Qt框架。你可以访问Qt官方网站和Qt Creator的帮助文档。 总结:Qt Creator提供了一个友好和强大的开发环境,使开发者能够轻松地设计、编码、构建和部署Qt应用程序。通过学习和使用这个教程,你可以更好地利用Qt Creator来开发高质量的软件。 ### 回答2: Qt Creator是一款功能强大的跨平台集成开发环境(IDE),用于开发Qt应用程序。以下是Qt Creator 5.9.0的完整使用教程: 1. 安装:下载并安装Qt Creator 5.9.0,根据操作系统选择64位或32位版本。安装完成后,打开Qt Creator。 2. 创建项目:点击“新建文件或项目”按钮,选择新项目模板。选择应用程序模板,并填写项目名称和存储位置。 3. 设计界面:在创建的项目中,双击打开.ui扩展名的文件,使用Qt Designer设计应用程序界面。拖拽控件到界面上,设置属性和布局。 4. 编写代码:在Qt Creator的源文件中,编写C++代码来实现应用程序的逻辑功能。使用Qt的类和函数来处理用户交互、数据处理等功能。 5. 构建项目:点击Qt Creator工具栏中的“构建”按钮,编译项目并生成可执行文件。确保没有编译错误。 6. 调试应用程序:在Qt Creator中点击“调试”按钮,启动应用程序。可以在调试器中设置断点、查看变量的值,并进行单步调试。 7. 部署和发布:在Qt Creator的发布向导中配置应用程序的部署设置,生成可执行文件和所需的库文件。可以选择静态或动态链接库。 8. 版本控制:Qt Creator集成了版本控制工具,如Git和Subversion。可以对项目进行版本控制,管理源代码的修改和更新。 9. 插件和扩展:Qt Creator支持丰富的插件和扩展,可以通过Qt Marketplace下载安装。这些插件提供额外的功能和工具。 10. 学习资源:Qt Creator有详细的官方文档和教程,可供学习和参考。在线论坛和社区也是学习和解决问题的好地方。 以上是Qt Creator 5.9.0的完整使用教程。掌握了这些基本操作和功能,您可以使用Qt Creator开发高效、可靠的Qt应用程序。 ### 回答3: Qt Creator是一个集成开发环境(IDE),专为Qt应用程序开发而设计。它提供了一套工具和功能,帮助开发人员更轻松地创建和调试Qt应用程序。 以下是Qt Creator 5.9.0的完整使用教程的介绍: 第一步是下载和安装Qt Creator 5.9.0。可以从Qt官方网站上下载适合您操作系统的安装程序。安装过程中,请确保选择安装Qt Creator。 安装完成后,打开Qt Creator。在启动界面上,可以选择“新建项目”或“打开现有项目”。如果是第一次使用,选择“新建项目”。 选择项目类型,例如“Qt Widgets应用程序”或“Qt Quick应用程序”。然后,输入项目的名称和位置,并指定保存新项目的目录。 在项目设置中,可以选择Qt版本和编译器。确保选择与您正在开发的应用程序兼容的Qt版本。 在项目结构面板中,可以看到项目目录的文件结构。源代码、资源文件和其他项目文件都可以在这里进行管理和编辑。 在源代码编辑器中,可以创建和编辑源代码文件。Qt Creator提供了代码自动补全、语法突出显示和代码导航等功能,使编写代码更加高效。 通过按下F5或单击工具栏上的绿色播放按钮,可以编译和运行项目。Qt Creator会自动构建项目并运行应用程序。 在调试过程中,可以使用Qt Creator提供的调试工具来跟踪和修复错误。调试工具包括断点、监视器和变量查看器等。 此外,Qt Creator还提供了其他功能,如版本控制、项目管理、界面设计器和自动测试等。您可以根据自己的需要使用这些功能。 总结起来,Qt Creator 5.9.0是一个功能强大的IDE,能够帮助开发人员轻松创建和调试Qt应用程序。通过该教程,您将学习如何下载、安装和使用Qt Creator,并了解到其提供的各种功能和工具。希望这个简要的教程对您有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

百里杨

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

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

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

打赏作者

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

抵扣说明:

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

余额充值