---
title: framework-cpp-qt-05-元对象系统
EntryName: framework-cpp-qt-05-mos
date: 2020-04-09 17:11:44
categories:
tags:
- qt
- c/c++
---

章节描述:

Qt 是一个用标准 C++ 编写的跨平台开发类库,它对标准 C++ 进行了扩展,引入了元对象系统、信号与槽、属性等特性,使应用程序的开发变得更高效。

Qt 的元对象系统

Qt 的元对象系统(Meta-Object System)提供了对象之间通信的信号与槽机制运行时类型信息动态属性系统

元对象系统由以下三个基础组成:

  1. QObject 类是所有使用元对象系统的类的基类。
  2. 在一个类的 private 部分声明 Q_OBJECT宏,使得类可以使用元对象的特性,如动态属性、信号与槽。
  3. MOC(元对象编译器)为每个 QObject 的子类提供必要的代码来实现元对象系统的特性。

元对象提供的一些功能

除了信号与槽机制外,元对象还提供如下一些功能:

  • QObject::metaObject() 函数返回类关联的元对象,元对象类 QMetaObject 包含了访问元对象的一些接口函数。例如 QMetaObject::className() 函数可在运行时返回类的名称字符串。
  QObject *obj = new QPushButton;
obj->metaObject()->className (); //返回"QPushButton"
  • QMetaObject::newInstance() 函数创建类的一个新的实例。

  • QObject::inherits(const char *className) 函数判断一个对象实例是否是名称为 className 的类或 QObject 的子类的实例。例如:

  QTimer *timer = new QTimer; // QTimer 是 QObject 的子类
timer->inherits ("QTimer"); // 返回 true
timer->inherits ("QObject"); // 返回 true
timer->inherits ("QAbstractButton");//返回 false,不是 QAbstractButton 的子类
  • QObject::tr()QObject::trUtf8() 函数可翻译字符串,用于多语言界面设计。

  • QObject::setProperty()QObject::property() 函数用于通过属性名称动态设置和获取属性值。

QObject 的动态转换

对于 QObject 及其子类,还可以使用 qobject_cast() 函数进行动态转换(dynamic cast)。

例如,假设 QMyWidgetQWidget 的子类并且在类定义中声明了 Q_OBJECT 宏。

创建实例使用下面的语句:

QObject *obj = new QMyWidget;

变量 obj 定义为 QObject 指针,但它实际指向 QMyWidget 类,所以可以正确转换为 QWidget,即:

QWidget *widget = qobject_cast<QWidget *>(obj);

从 QObject 到 QWidget 的转换是成功的,因为 obj 实际是 QMyWidget 类,是 QWidget 的子类。也可以将其成功转换为 QMyWidget,即:

QMyWidget *myWidget = qobject_cast<QMyWidget *>(obj);

转换为 QMyWidget 是成功的,因为 qobject_cast() 并不区分 Qt 内建的类型和用户自定义类型。但是,若要将 obj 转换为 QLabel 则是失败的,即:

QLabel * label = qobject_cast<QLabel *>(obj);

这样转换是失败的,返回指针 label 为 NULL,因为 QMyWidget 不是 QLabel 的子类。

使用动态转换,使得程序可以在运行时对不同的对象做不同的处理。

属性系统

属性定义

Qt 提供一个 Q_PROPERTY() 宏可以定义属性,它也是基于元对象系统实现的。Qt 的属性系统与 C++ 编译器无关,可以用任何标准的 C++ 编译器编译定义了属性的 Qt C++ 程序。

在 QObject 的子类中,用宏 Q_PROPERTY() 定义属性,其使用格式如下:

Q_PROPERTY(type name (READ getFunction [WRITE setFunction] | MEMBER meznberName [(READ getFunction | WRITE setFunction)])
[RESET resetFunction]
[NOTIFY notifySignal]
[REVISION int]
[DESIGNABLE bool]
[SCRIPTABLE bool]
[STORED bool]
[USER bool]
[CONSTANT]
[FINAL])

Q_PROPERTY 宏定义一个返回值类型为 type,名称为 name 的属性,用 READ、WRITE 关键字定义属性的读取、写入函数,还有其他的一些关键字定义属性的一些操作特性。属性的类型可以是 QVariant 支持的任何类型,也可以用户自定义类型。

Q_PROPERTY 宏定义属性的一些主要关键字的意义如下:

  • READ 指定一个读取属性值的函数,没有 MEMBER 关键字时必须设置 READ。
  • WRITE 指定一个设定属性值的函数,只读属性没有 WRITE 设置。
  • MEMBER 指定一个成员变量与属性关联,成为可读可写的属性,无需再设置 READ 和 WRITE。
  • RESET 是可选的,用于指定一个设置属性缺省值的函数。
  • NOTIFY 是可选的,用于设置一个信号,当属性值变化时发射此信号。
  • DESIGNABLE 表示属性是否在 Qt Designer 里可见,缺省为 true。
  • CONSTANT 表示属性值是一个常数,对于一个对象实例,READ 指定的函数返回值是常数,但是每个实例的返回值可以不一样。具有 CONSTANT 关键字的属性不能有 WRITE 和 NOTIFY 关键字。
  • FINAL 表示所定义的属性不能被子类重载。

QWidget 类定义属性的一些例子如下:

Q_PROPERTY(bool focus READ hasFocus)
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)

属性的使用

不管是否用 READ 和 WRITE 定义了接口函数,只要知道属性名称,就可以通过 QObject::property() 读取属性值,并通过 QObject::setProperty() 设置属性值。例如:

QPushButton *button = new QPushButton;
QObject *object = button;
object->setProperty("flat", true);
bool isFlat= object->property ("flat");

动态属性

QObject::setProperty() 函数可以在运行时为类定义一个新的属性,称之为动态属性。动态属性是针对类的实例定义的。

动态属性可以使用 QObject::property() 查询,就如在类定义里用 Q_PROPERTY 宏定义的属性一样。

例如,在数据表编辑界面上,一些字段是必填字段,就可以在初始化界面时为这些字段的关联显示组件定义一个新的 required 属性,并设置值为“true”,如:

editName->setProperty("required", "true");
comboSex->setProperty("required", "true");
checkAgree->setProperty("required", "true");

然后,可以应用下面的样式定义将这种必填字段的背景颜色设置为亮绿色。

*[required="true"]{background-color:lime}

类的附加信息

属性系统还有一个宏 Q_CLASSINFO(),可以为类的元对象定义“名称——值”信息,如:

class QMyClass:public QObject {
Q_OBJECT
Q_CLASSINFO("author", "xx")
Q_CLASSINFO ("company", "UPC")
Q_CLASSINFO("version ", "1.0.1")
public:
...
};

Q_CLASSINFO() 宏定义附加类信息后,可以通过元对象的一些函数获取类的附加信息,如 classlnfo(int) 获取某个附加信息,函数原型定义如下:

QMetaClassInfo QMetaObject::classInfo(int index) const

返回值是 QMetaClassInfo 类型,有 name() 和 value() 两个函数,可获得类附加信息的名称和值。

综合例程

Object.h

#ifndef OBJECT_H
#define OBJECT_H #include <QObject>
#include <QString>
#include <QDebug> class Object : public QObject
{
Q_OBJECT
Q_PROPERTY(int age READ age WRITE setAge NOTIFY ageChanged)
Q_PROPERTY(int score READ score WRITE setScore NOTIFY scoreChanged)
Q_CLASSINFO("Author", "Scorpio")
Q_CLASSINFO("Version", "1.1")
Q_ENUMS(Level)
protected:
QString m_name;
QString m_level;
int m_age;
int m_score;
public:
enum Level
{
Basic,
Middle,
Advanced
}; public:
explicit Object(QString name)
{
m_name = name;
setObjectName(m_name);
connect(this, SIGNAL(ageChanged(int)), this, SLOT(onAgeChanged(int)));
connect(this, SIGNAL(scoreChanged(int)), this, SLOT(onScoreChanged(int)));
} int age() const
{
return m_age;
} void setAge(const int& age)
{
m_age = age;
emit ageChanged(m_age);
} int score()const { return m_score; } void setScore(const int& score)
{
m_score = score;
emit scoreChanged(m_score);
} signals:
void ageChanged(int age);
void scoreChanged(int score); public slots:
void onAgeChanged(int age)
{
qDebug() << "age changed:" << age;
}
void onScoreChanged(int score)
{
qDebug() << "score changed:" << score;
}
}; #endif // OBJECT_H

Main.cpp

#include <QCoreApplication>
#include "myclass.h"
#include <QMetaObject>
#include <QMetaClassInfo> int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Object ob("object"); //设置属性age
ob.setProperty("age", QVariant(30));
qDebug() << "age: " << ob.age();
qDebug() << "property age: " << ob.property("age").toInt(); //设置属性score
ob.setProperty("score", QVariant(90));
qDebug() << "score: " << ob.score();
qDebug() << "property score: " << ob.property("score").toInt(); //内省intropection,运行时查询对象信息
qDebug() << "object name: " << ob.objectName();
qDebug() << "class name: " << ob.metaObject()->className();
qDebug() << "isWidgetType: " << ob.isWidgetType();
qDebug() << "inherit: " << ob.inherits("QObject"); // 获取附加信息
qDebug()<<ob.metaObject()->classInfo(0).name();
qDebug()<<ob.metaObject()->classInfo(0).value();
qDebug()<<ob.metaObject()->classInfo(1).name();
qDebug()<<ob.metaObject()->classInfo(1).value(); return a.exec();
}

Q_INVOKABLE

用于定义一个成员函数可以被元对象系统调用,Q_INVOKABLE宏必须写在函数的返回类型之前。如下:

Q_INVOKABLE void invokableMethod();

invokableMethod()函数使用了Q_INVOKABLE宏声明,invokableMethod()函数会被注册到元对象系统中,可以使用 QMetaObject::invokeMethod()调用。

Q_INVOKABLE与QMetaObject::invokeMethod均由元对象系统唤起,在Qt C++/QML混合编程、跨线程编程、Qt Service Framework以及 Qt/ HTML5混合编程以及里广泛使用。

在跨线程编程中的使用

如何调用驻足在其他线程里的QObject方法呢?Qt提供了一种非常友好而且干净的解决方案:向事件队列post一个事件,事件的处理将以调用所感兴趣的方法为主(需要线程有一个正在运行的事件循环)。而触发机制的实现是由MOC提供的内省方法实现的。因此,只有信号、槽以及被标记成Q_INVOKABLE的方法才能够被其它线程所触发调用。如果不想通过跨线程的信号、槽这一方法来实现调用驻足在其他线程里的QObject方法。另一选择就是将方法声明为Q_INVOKABLE,并且在另一线程中用invokeMethod唤起。

Qt Service Framework

Qt服务框架是Qt Mobility 1.0.2版本推出的,一个服务(service)是一个独立的组件提供给客户端(client)定义好的操作。

客户端可以通过服务的名称,版本号和服务的对象提供的接口来查×××。 查找到服务后,框架启动服务并返回一个指针。

服务通过插件(plug-ins)来实现。为了避免客户端依赖某个具体的库,服务必须继承自QObject,保证QMetaObject 系统可以用来提供动态发现和唤醒服务的能力。要使QmetaObject机制充分的工作,服务必须满足,其所有的方法都是通过 signal、slot、property或invokable method和Q_INVOKEBLE来实现。

QServiceManager manager;
QObject *storage ;
storage = manager.loadInterface("com.nokia.qt.examples.FileStorage");
if(storage)
QMetaObject::invokeMethod(storage, "deleteFile", Q_ARG(QString, "/tmp/readme.txt"));

上述代码通过service的元对象提供的invokeMethod方法,调用文件存储对象的deleteFile() 方法。客户端不需要知道对象的类型,因此也没有链接到具体的service库。 当然在服务端的deleteFile方法,一定要被标记为Q_INVOKEBLE,才能够被元对象系统识别。

Qt服务框架的一个亮点是它支持跨进程通信,服务可以接受远程进程。在服务管理器上注册后,进程通过signalslotinvokable methodproperty来通信,就像本地对象一样。服务可以设定为在客户端间共享,或针对一个客户端。

QT学习:05 元对象系统的更多相关文章

  1. Qt元对象系统简介

    在Qt中提供了c++的扩展,提供了一种元对象系统的机制,(meta-object-system)的机制.其中包含了信号与槽的内部机制,能够访问到QObject子类的元对象信息的功能. Q_OBJECT ...

  2. Qt对象模型之二:对象树与元对象系统

    一.对象树的概念 Qt中使用对象树(object tree)来组织和管理所有的QObject类及其子类的对象.当创建一个QObject时,如果使用了其他的对象作为其父对象(parent),那么这个 Q ...

  3. qt 元对象系统

    元对象系统 Qt中的元对象系统是用来处理对象间通讯的信号/槽机制.运行时的类型信息和 动态属性系统. 它基于下列三类: QObject类: 类声明中的私有段中的Q_OBJECT宏: 元对象编译器(mo ...

  4. 自定义类型与Qt元对象系统

    个人发现一篇关于在Qt中使用元对象系统支持自定义类型的好博文,记录下防止丢失(如有侵权,烦请告知删除).博文原地址:http://qtdebug.com/qtbook-misc-qvariant/ Q ...

  5. QtCore是Qt的精髓(包括五大模块:元对象系统,属性系统,对象模型,对象树,信号槽)

    作者:小豆君的干货铺链接:https://www.zhihu.com/question/27040542/answer/218384474来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业 ...

  6. 深入了解Qt(二)之元对象系统(Meta-Object System)

    深入了解Qt主要内容来源于Inside Qt系列,本文做了部分删改,以便于理解.在此向原作者表示感谢! 在Qt Meta Object System-元对象系统这篇文章中,从底层实现的源码剖析了元对象 ...

  7. Qt 元对象系统(Meta-Object System)

    (转自:http://blog.csdn.net/aladdina/article/details/5496891) Qt的元对象系统基于如下三件事情: 类:QObject,为所有需要利用原对象系统的 ...

  8. Qt笔记——元对象系统

    Qt元对象系统提供了对象间的通信机制:信号和槽.以及执行类形信息和动态属性系统的支持.是标注C++的一个扩展,它使得Qt可以更好的实现GUI图形用户界面编程.Qt的元对象系统不支持C++模板.虽然模板 ...

  9. 解析Qt元对象系统(五) Q_INVOKABLE与invokeMethod(automatic connection从Qt4.8开始的解释已经与之前不同,发送对象驻足于哪一个线程并不重要,起到决定作用的是接收者对象所驻足的线程以及发射信号(该信号与接受者连接)的线程是不是在同一个线程)good

    概述查看Qt源码可知,Q_INVOKABLE是个空宏,目的在于让moc识别. 使用Q_INVOKABLE来修饰成员函数,目的在于被修饰的成员函数能够被元对象系统所唤起. Q_INVOKABLE与QMe ...

  10. 解析Qt元对象系统(四) 属性系统(确实比较方便)

    官方解释 我们在Qt源码中可以看到一个QObject的子类经常会用到一些Q_开头的宏,例如QMainWindow类开始部分代码是这样的: Q_PROPERTY(QSize iconSize READ ...

随机推荐

  1. The instance of entity type 'Model' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked.

    The instance of entity type 'Model' cannot be tracked because another instance with the same key val ...

  2. 一个可一键生成短视频的AI大模型,亲测可用

    大家好,我是 Java陈序员. 自从 OpenAI 发布 Sora 文本生成视频模型后,文本生成视频的 AI 技术引起了无数圈内圈外人士的关注和实验. 今天,给大家介绍一个大模型,可一键生成短视频. ...

  3. 如果win报错无法加载文件 C:\Users\xx\AppData\Roaming\npm\pnpm.ps1,因为在此系统上禁止运行脚本

    点击查看代码 Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass

  4. unity 新input system 鼠标点在ui上检测的两种方法

    哪种有用就用哪种.EventSystem.current.IsPointerOverGameObject()有可能不好使. using System.Collections.Generic; usin ...

  5. 实用!!!!springBoot加入微信扫码支付功能,有一说一还是挺有意思的

    基于springboot微服务加入微信支付的功能 在线微信支付开发文档: https://pay.weixin.qq.com/wiki/doc/api/index.html native扫码支付方式: ...

  6. 慢查询SQL优化

    记一次慢查询的SQL优化 测试表结构 MariaDB [shoppings]> desc login_userinfo; +------------+-------------+------+- ...

  7. Java8新特性default关键字,引出Java多继承问题

    概述 最近在看JDK集合的源码时,在Collection接口中发现了default关键字,并且惊奇的发现接口中的方法居然包含方法体,这顿时让我产生兴趣,为此我也稍微研究了一下default关键字. 很 ...

  8. .NET6之MiniAPI(九):基于角色的身份验证和授权

    身份验证是这样一个过程:由用户提供凭据,然后将其与存储在操作系统.数据库.应用或资源中的凭据进行比较. 在授权过程中,如果凭据匹配,则用户身份验证成功,可执行已向其授权的操作. 授权指判断允许用户执行 ...

  9. 用Python脚本迁移MongoDB数据到金仓-kingbase数据库

    1.首先需要明确MongoDB与kingbase的对应关系,collection相当于table,filed相当于字段,根据这个对应关系创建表: 此次迁移的MongoDB里的数据字段是:_id(自动生 ...

  10. cmake之find_library使用问题

    附上工程源码 demo工程 PS:这个工程用于导出库 CMakeLists.txt cmake_minimum_required(VERSION 3.5) project(demo LANGUAGES ...