QT学习:05 元对象系统
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)提供了对象之间通信的信号与槽机制、运行时类型信息和动态属性系统。
元对象系统由以下三个基础组成:
QObject
类是所有使用元对象系统的类的基类。- 在一个类的 private 部分声明
Q_OBJECT
宏,使得类可以使用元对象的特性,如动态属性、信号与槽。 - 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)。
例如,假设 QMyWidget
是 QWidget
的子类并且在类定义中声明了 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服务框架的一个亮点是它支持跨进程通信,服务可以接受远程进程。在服务管理器上注册后,进程通过signal
、slot
、invokable method
和property
来通信,就像本地对象一样。服务可以设定为在客户端间共享,或针对一个客户端。
QT学习:05 元对象系统的更多相关文章
- Qt元对象系统简介
在Qt中提供了c++的扩展,提供了一种元对象系统的机制,(meta-object-system)的机制.其中包含了信号与槽的内部机制,能够访问到QObject子类的元对象信息的功能. Q_OBJECT ...
- Qt对象模型之二:对象树与元对象系统
一.对象树的概念 Qt中使用对象树(object tree)来组织和管理所有的QObject类及其子类的对象.当创建一个QObject时,如果使用了其他的对象作为其父对象(parent),那么这个 Q ...
- qt 元对象系统
元对象系统 Qt中的元对象系统是用来处理对象间通讯的信号/槽机制.运行时的类型信息和 动态属性系统. 它基于下列三类: QObject类: 类声明中的私有段中的Q_OBJECT宏: 元对象编译器(mo ...
- 自定义类型与Qt元对象系统
个人发现一篇关于在Qt中使用元对象系统支持自定义类型的好博文,记录下防止丢失(如有侵权,烦请告知删除).博文原地址:http://qtdebug.com/qtbook-misc-qvariant/ Q ...
- QtCore是Qt的精髓(包括五大模块:元对象系统,属性系统,对象模型,对象树,信号槽)
作者:小豆君的干货铺链接:https://www.zhihu.com/question/27040542/answer/218384474来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业 ...
- 深入了解Qt(二)之元对象系统(Meta-Object System)
深入了解Qt主要内容来源于Inside Qt系列,本文做了部分删改,以便于理解.在此向原作者表示感谢! 在Qt Meta Object System-元对象系统这篇文章中,从底层实现的源码剖析了元对象 ...
- Qt 元对象系统(Meta-Object System)
(转自:http://blog.csdn.net/aladdina/article/details/5496891) Qt的元对象系统基于如下三件事情: 类:QObject,为所有需要利用原对象系统的 ...
- Qt笔记——元对象系统
Qt元对象系统提供了对象间的通信机制:信号和槽.以及执行类形信息和动态属性系统的支持.是标注C++的一个扩展,它使得Qt可以更好的实现GUI图形用户界面编程.Qt的元对象系统不支持C++模板.虽然模板 ...
- 解析Qt元对象系统(五) Q_INVOKABLE与invokeMethod(automatic connection从Qt4.8开始的解释已经与之前不同,发送对象驻足于哪一个线程并不重要,起到决定作用的是接收者对象所驻足的线程以及发射信号(该信号与接受者连接)的线程是不是在同一个线程)good
概述查看Qt源码可知,Q_INVOKABLE是个空宏,目的在于让moc识别. 使用Q_INVOKABLE来修饰成员函数,目的在于被修饰的成员函数能够被元对象系统所唤起. Q_INVOKABLE与QMe ...
- 解析Qt元对象系统(四) 属性系统(确实比较方便)
官方解释 我们在Qt源码中可以看到一个QObject的子类经常会用到一些Q_开头的宏,例如QMainWindow类开始部分代码是这样的: Q_PROPERTY(QSize iconSize READ ...
随机推荐
- Treap,Splay & LCT 学习笔记
从二叉搜索树到平衡树 二叉搜索树(Binary Search Tree)是一种二叉树的树形数据结构,它维护一个集合,并保证它的中序遍历按照递增顺序给出了这个集合的所有元素.由此,可以完成插入,删除,查 ...
- SATA与PCI-E速度对比
SATA SATA接口已经发展到了第三代,理论上的最大速度达到600MB/s.平时大家见到的SATA SSD使用的都是SATA三代,实际测试速度在550MB/s左右,这比普通的机械硬盘的速度100MB ...
- CMD程序_WordCount_博客改
程序简介 这个程序只能以命令行的方式启动,在启动时要输入相应的命令.程序的功能是对文本文件的字符数,单词数,行数进行统计,将结果输出到默认文件或指定文件. 码云项目链接:WordCount 程序结构 ...
- Java面试题:SpringBoot异常捕获,让程序“免疫”一切错误!
在Spring Boot应用程序中,捕获全局异常是一个重要的方面,它可以帮助我们处理在应用程序运行时可能发生的各种错误情况.通过适当地捕获和处理这些异常,我们可以改善用户体验并及时采取必要的措施. 使 ...
- dubbo 的三种引用
第一种:SpringBoot 整合 Dubbo 进行分布式开发https://www.cnblogs.com/gdufs/p/9414331.html?share_token=8cc709f1-99d ...
- c#事件的实际应用场景
最简单的定义事件的语法 public event Action<bool> Refreash; 先介绍这个Action 这个Action是委托的快速实现方式,我用.net framewor ...
- Python Pandas 数据分组
在数据处理中,分箱.分组是一种常见的技术,用于将连续数据的间隔分组到"箱"或"桶"中.我们将讨论以下两种方法: 使用 Pandas 的 between 和 lo ...
- Android 13 - Media框架(21)- ACodec(三)
关注公众号免费阅读全文,进入音视频开发技术分享群! 这一节我们一起来了解 ACodec 是如何通过 configureCodec 方法配置 OMX 组件的,因为 configureCodec 代码比较 ...
- 恭喜PaddleOCRSharp开源项目通过PaddleOCR社区常规赛优秀项目首次评选
PaddleOCR优秀社区项目推荐: PaddleOCR社区常规赛首次评选结果已于日前出炉,本次优秀项目推广为大家带来的是[部署篇]:️ PaddleOCR的.NET调用库:包含文本识别.文本检测.基 ...
- nginx的11个阶段
nginx处理请求的11个阶段 阶段 模块 第一阶段 POST_READ realip 第二阶段 SERVER_REWRITE rewrite 第三阶段 FIND_CONFIG 第四阶段 REWRIT ...