QObject 是所有支持元对象系统类的基类。
1 QObject
使用元对象系统需要满足两个条件:
(1)直接或间接继承 QObject
(2)类声明开头包含 Q_OBJECT 宏
| 类型 | 函数 | 功能 | |
公 有 函 数 | 元对 象 | QMetaObject *metaObject() | 返回元对象,每个QObject及其子类实例都有一个元对象,这个元对象是自动创建的。 |
类型 信息 | bool inherits() | 是否继承某个类 | |
动态 翻译 | QString tr() | 返回字符串的翻译版本 | |
对 象 树 | QObjectList &children() | 返回子对象列表 | |
| QObject *parent() | 返回父对象指针 | ||
| void setParent() | 设置父对象 | ||
| T findChild() | 返回可转换为类型 T 的子对象 | ||
| QList<T> findChildren() | 返回符合名称和类型条件的子对象列表 | ||
信 号 与 槽 | QMetaObject::Connection connect() | 建立信号与槽的连接 | |
| bool disconnect() | 断开信号与槽的连接 | ||
| bool blockSignals() | 阻止对象发射信号 | ||
| bool signalsBlocked() | 是否阻止对象发射信号 | ||
属 性 系 统 | QList<QByteArray> dynamicPropertyNames() | 返回动态属性列表 | |
| bool setProperty() | 设置属性值,或添加动态属性 | ||
| QVariant property() | 返回属性值 | ||
静态公 有函数 | QMetaObject staticMetaObject() | 类的元对象 | |
构建项目时, MOC 会读取 C++源文件,当它发现类的定义里有 Q_OBJECT 宏时,它就会为这个类生成另一个包含元对象支持代码的 C++源文件,这个生成的源文件连同类的实现文件一起被标准 C++编译器编译和连接。(MOC:元对象系统编译器)
2 信号与槽
2.1 常见连接方式
使用connect的静态函数建立信号与槽的连接
(1)方法1:(Qt4语法)使用宏SIGNAL()和SLOT()
connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));(2)方法2:(Qt5语法)使用函数指针
// 一般形式 connect(lineEdit, &QLineEdit::textChanged, this, &Widget::do_textChanged); // Lambda表达式 connect(lineEdit, &QLineEdit::textChanged, this, [=](){/* doing */}); // 信号重载 connect(comboBox,static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),this,[=](int index){/* doing */});1. 一个信号可以连接多个槽
2. 一个信号可以连接另一个信号
3. 多个信号可以连接同一个槽
4. 信号的参数大于等于槽的参数,且参数类型应对应
未连接成功:
1. 是否继承 QObject、是否包含宏 Q_OBJECT
2. 建立连接时,发射者和接收者是否均已实例化
2. 信号参数是否与声明一致,槽函数参数是否与信号对应
2.2 断开连接的方式
使用disconnect断开信号与槽的连接
(1)解除与一个发射者所有信号的连接
disconnect(myObject, nullptr, nullptr, nullptr); //静态函数形式 myObject->disconnect(); //成员函数形式(2)解除一个特定信号的所有连接
disconnect(myObject, SIGNAL(mySignal()), nullptr, nullptr); //静态函数形式 myObject->disconnect(SIGNAL(mySignal())); //成员函数形式(3)解除与一个特定接收者的所有连接
disconnect(myObject, nullptr, myReceiver, nullptr); //静态函数形式 myObject->disconnect(myReceiver); //成员函数形式(4)解除特定的一个信号与槽的连接(函数指针)
disconnect(lineEdit, &QLineEdit::textChanged, label, &QLabel::setText); //静态函数形式个人很少见到程序中会主动断开信号与槽连接的
2.3 获取信号发射者
使用sender() 函数获取信号发射者
void Widget::on_btnProperty_clicked() { QPushButton *btn= qobject_cast<QPushButton*>(sender()); //获取信号的发射者 bool isFlat= btn->property("flat").toBool(); btn->setProperty("flat", !isFlat); }3 属性系统
3.1 Q_PROPERTY
使用宏 Q_PROPERTY 定义属性
Q_PROPERTY(type name READ getFunction [WRITE setFunction] [MEMBER memberName] [RESET resetFunction] [NOTIFY notifySignal] [DESIGNABLE bool] [STORED bool] [USER bool] [CONSTANT] [FINAL])| 类型 | 关键字 | 功能 |
| 定义 | type | 定义一个属性 |
| 访问控制 | READ | 读取函数 |
| WRITE | 写入函数 | |
| MEMBER | 绑定一个成员变量,Qt 会自动生成读写逻辑 | |
| 信号与重置 | NOTIFY | 绑定一个信号,属性值变化时发射信号 |
| RESET | 重置函数,将属性重置为默认值 | |
| UI设计 | DESIGNABLE | Qt Designer 的属性编辑器中是否可见,默认 ture |
| STORED | 是否持久保存,默认 true。若为false,通常表示该属性值依赖于其他状态,如QWidget::pos依赖于 geometry | |
| USER | 用户是否可编辑,默认 false。面向用户交互的属性,一个类最多只有1个。如 QPushButton 的 checked 状态。 | |
| 关键字 | CONSTANT | 属性值是一个常数,不能有 WRITE 和 NOTIFY 关键字。 |
| FINAL | 不能被子类重载 |
示例
Q_PROPERTY(bool focus READ hasFocus) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)1. 可读可写的属性,读取函数名与属性名相同,写入函数名一般是在属性名前面加“ set ”
2. QObject 类提供了两个函数直接通过属性名字符串来访问属性,函数 QObject::property()读取属性值,函数 QObject::setProperty设置属性值
3. 函数 QObject::setProperty()设置属性值时,如果属性名称不存在,就会为对象定义一个新的属性并设置属性值,这时定义的属性称为动态属性。动态属性是针对类的实例定义的,所以只能使用函数 QObject::property()读取动态属性的属性值
3.2 Q_CLASSINFO
使用宏 Q_CLASSINFO 定义类信息。
class QMyClass : public QObject { Q_OBJECT Q_CLASSINFO("author", "Wang") Q_CLASSINFO("company", "UPC") Q_CLASSINFO("version ", "3.0.1") };获取类信息的函数
char *QMetaClassInfo::name() //返回类信息的名称 char *QMetaClassInfo::value() //返回类信息的值类信息程序中基本见不到,很少用。
附录1 QMetaObject
每个 QObject 及其子类的实例都有一个自动创建的元对象,元对象是 QMetaObject 类型的实例,存储了类的实例所属类的各种元数据,包括类信息元数据、方法元数据、属性元数据等。
| 类型 | 函数 | 功能 | |
公 有 函 数 | 类 信 息 | char *className() | 返回类名称 |
| QMetaType metaType() | 返回元对象的元类型 | ||
| QMetaObject *superClass() | 返回上层父类的元对象 | ||
| bool inherits(QMetaObject *metaObject) | 是否继承 metaObject 描述的类 | ||
| QObject *newInstance() | 创建一个新实例 | ||
| 类信息元数据 | QMetaClassInfo classInfo(int index) | 返回序号为 index 的一条类信息的元数据,类信息是在类中用宏 Q_CLASSINFO 定义的一条信息 | |
| int indexOfClassInfo(char *name) | 返回名称为 name 的类信息序号,序号可用于 classInfo | ||
| int classInfoCount() | 返回类信息个数 | ||
| int clasInfoOffset | 返回第一条类信息的序号 | ||
| 构造函数元数据 | int constructorCount() | 返回构造函数个数 | |
| QMetaMethod constructor(int index) | 返回序号为 index 的构造函数的元数据 | ||
| 方法元数据 | int indexOfConstructor(char *constructor) | 返回构造函数的序号 | |
| QMetaMethod method(int index) | 返回序号为 index 的方法的元数据 | ||
| int methodCount() | 返回方法的个数 | ||
| int methodOffset() | 返回第一个方法的序号 | ||
| int indexOfMethod(char *method) | 返回名称为 method 的方法的序号 | ||
| 枚举类型元数据 | QMetaEnum enumerator(int index) | 返回序号为 index 的枚举的元数据 | |
| int enumeratorCount() | 返回枚举类型的个数 | ||
| int enumeratorOffset() | 返回第一个枚举类型的序号 | ||
| int indexOfEnumerator(char *name) | 返回名称为 name 的枚举类型的序号 | ||
| 属性元数据 | QMetaProperty property(int index) | 返回序号为 index 的属性的元数据 | |
| int propertyCount() | 返回属性的个数 | ||
| int propertyOffset() | 返回第一个属性的序号 | ||
| int indexOfProperty(char *name) | 返回名称为 name 的属性的序号 | ||
| 信号与槽 | int indexOfSignal(char *signal) | 返回名称为 signal 的信号的序号 | |
| int indexOfSlot(char *slot) | 返回名称为 slot 的槽函数的序号 | ||
| 静态公有函数 | bool checkConnectArgs() | 检查信号与槽函数的参数是否兼容 | |
| voidconnectSlotByName(QObject *object) | 迭代搜索 object 的所有子对象,将匹配的信号和槽连接起来 | ||
| void invokeMethod() | 运行 QObject 对象的某个方法 | ||
| QByteArray normalizedSignature(char *method) | 将方法 method 的名称和参数字符串正则化,去除多余空格。函数返回的结果可用于 checkConnectArgs()、indexOfConstructor()等函数 | ||
个人理解:元对象在调试和打印时可能会使用,但程序中真的很少见,如知道枚举的个数、方法的个数或某个信号的序号好像并没有什么用。
附录2 英文缩写
| 缩写 | 全称 | 中文 |
| MOC | Meta-Object Compiler | 元对象编译器 |
| RTTI | run time type information | 运行时类型信息 |