1. Introduction to the Meta-Object System
The Qt Meta-Object System provides runtime type information and reflection capabilities for <span><span>QObject</span></span> derived classes, including:
-
Names, types, and read/write capabilities of properties (
<span><span>Q_PROPERTY</span></span>); -
Signatures and parameter types of methods (including slots and signals,
<span><span>Q_INVOKABLE</span></span>); -
Names and entries of enumerations (
<span><span>Q_ENUM</span></span>/<span><span>Q_ENUMS</span></span>); -
Runtime method invocation (
<span><span>QMetaObject::invokeMethod</span></span>) and accessing properties using<span><span>QVariant</span></span>(<span><span>QObject::property/setProperty</span></span>); -
Connections and name lookups for signals/slots.
The core entry point is <span><span>QMetaObject</span></span> (each class with <span><span>Q_OBJECT</span></span> generates a static <span><span>metaObject()</span></span>) and <span><span>QMetaProperty</span></span> / <span><span>QMetaMethod</span></span> / <span><span>QMetaEnum</span></span>, etc.
2. Common Classes and APIs
QMetaObject Class
// Get class name
const char* className() const;
// Get parent class's meta-object
const QMetaObject* superClass() const;
// Method related
int methodCount() const;
int indexOfMethod(const char* method) const;
int indexOfSignal(const char* signal) const;
int indexOfSlot(const char* slot) const;
QMetaMethod method(int index) const;
// Property related
int propertyCount() const;
int indexOfProperty(const char* name) const;
QMetaProperty property(int index) const;
// Enumeration related
int enumeratorCount() const;
int indexOfEnumerator(const char* name) const;
QMetaEnumerator enumerator(int index) const;
// Class information
int classInfoCount() const;
int indexOfClassInfo(const char* name) const;
QMetaClassInfo classInfo(int index) const;
QMetaMethod Class
// Method type
QMetaMethod::MethodType methodType() const;
// Method signature
QByteArray methodSignature() const;
QByteArray name() const;
// Parameter types
int parameterCount() const;
QList<QByteArray> parameterTypes() const;
QList<QByteArray> parameterNames() const;
// Return type
QByteArray typeName() const;
int returnType() const;
// Invoke method
bool invoke(QObject* object, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0 = QGenericArgument(nullptr), QGenericArgument val1 = QGenericArgument(), ...) const;
QMetaProperty Class
// Property information
const char* name() const;
QVariant::Type type() const;
const char* typeName() const;
// Read/write capabilities
bool isReadable() const;
bool isWritable() const;
bool isResettable() const;
// Access property value
QVariant read(const QObject* object) const;
bool write(QObject* object, const QVariant& value) const;
void reset(QObject* object) const;
// Notify signal
QMetaMethod notifySignal() const;
bool hasNotifySignal() const;
QMetaEnum Class
// Enumeration information
const char* name() const;
const char* scope() const;
// Key-value pairs
int keyCount() const;
const char* key(int index) const;
int value(int index) const;
// Convert value to string
const char* valueToKey(int value) const;
int keyToValue(const char* key) const;
Dynamic Properties
// Set and get dynamic properties
void setProperty(const char* name, const QVariant& value);
QVariant property(const char* name) const;
// Check dynamic properties
bool dynamicPropertyNames() const;
Signal-Slot Connections
// Static connection
QMetaObject::Connection connect(const QObject* sender, const char* signal, const QObject* receiver, const char* method, Qt::ConnectionType type = Qt::AutoConnection);
// Disconnect
void disconnect(const QObject* sender, const char* signal, const QObject* receiver, const char* method);
// Connection based on QMetaMethod
QMetaObject::Connection connect(const QObject* sender, const QMetaMethod& signal, const QObject* receiver, const QMetaMethod& method, Qt::ConnectionType type = Qt::AutoConnection);
Dynamic Method Invocation
// Using QMetaMethod to invoke
QMetaMethod method = ...;
QGenericReturnArgument ret;
QGenericArgument arg0;
method.invoke(object, Qt::AutoConnection, ret, arg0);
// Using QMetaObject to invoke
QMetaObject::invokeMethod(object, "methodName", Qt::AutoConnection, Q_RETURN_ARG(QString, result), Q_ARG(int, 123));
Constructing Objects
// Create instance via meta-object
QObject* object = metaObject->newInstance();
// Constructor with parameters
QObject* object = metaObject->newInstance(Q_ARG(int, 100), Q_ARG(QString, "test"));
3. ExampleHeader File MyWidget.h
// MyWidget.h
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QWidget>
#include <QMetaEnum>
class MyWidget : public QWidget {
Q_OBJECT
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)
Q_PROPERTY(int value READ value WRITE setValue RESET resetValue NOTIFY valueChanged)
Q_CLASSINFO("Author", "QtUser")
Q_CLASSINFO("Version", "1.0")
public:
explicit MyWidget(QWidget* parent = nullptr);
QString title() const { return m_title; }
void setTitle(const QString& t) {
if (m_title != t) {
m_title = t;
emit titleChanged(m_title);
}
}
int value() const { return m_value; }
void setValue(int v) {
if (m_value != v) {
m_value = v;
emit valueChanged(m_value);
}
}
void resetValue() { m_value = 0; }
signals:
void titleChanged(const QString& newTitle);
void valueChanged(int newValue);
public slots:
void doSomething(int x, const QString& msg);
QString compute(int a, int b);
private:
QString m_title = "Default";
int m_value = 0;
public:
enum Status {
Idle = 0,
Running = 1,
Finished = 2
};
Q_ENUM(Status)
};
#endif // MYWIDGET_H
Source Code MyWidget.cpp
// MyWidget.cpp
#include "MyWidget.h"
#include <QDebug>
MyWidget::MyWidget(QWidget* parent) : QWidget(parent) {}
void MyWidget::doSomething(int x, const QString& msg) {
qDebug() << "doSomething called with:" << x << "," << msg;
}
QString MyWidget::compute(int a, int b) {
return QString("Result: %1").arg(a + b);
}
<span><span>QMetaObject</span></span> Related API Examples
<span>className()</span>
const QMetaObject* meta = obj->metaObject();
qDebug() << "Class name:" << meta->className(); // Output: "MyWidget"
<span>superClass()</span>
const QMetaObject* super = meta->superClass();
qDebug() << "Super class:" << super->className(); // Output: "QWidget"
<span>methodCount()</span> / <span>method(index)</span>
qDebug() << "Total methods:" << meta->methodCount(); // Includes signals, slots, invokable, constructors, etc.
for (int i = 0; i < meta->methodCount(); ++i) {
QMetaMethod m = meta->method(i);
qDebug() << i << ":" << m.name() << "(" << m.methodSignature() << ");";
}// Output similar to:
// 0 : destroyed (destroyed(QObject*))
// 1 : objectNameChanged (objectNameChanged(QString))
// ...
// N : doSomething (doSomething(int,QString))
// N+1 : compute (compute(int,int))
<span>indexOfMethod()</span>, <span>indexOfSignal()</span>, <span>indexOfSlot()</span>
int idx1 = meta->indexOfMethod("doSomething(int,QString)");
int idx2 = meta->indexOfSignal("valueChanged(int)");
int idx3 = meta->indexOfSlot("doSomething(int,QString)");
qDebug() << "doSomething index:" << idx1; // >=0
qDebug() << "valueChanged signal index:" << idx2; // >=0
qDebug() << "doSomething slot index:" << idx3; // Same as idx1 (because it's a slot)
<span>propertyCount()</span> / <span>property(index)</span> / <span>indexOfProperty()</span>
qDebug() << "Property count:" << meta->propertyCount();
int titleIdx = meta->indexOfProperty("title");
if (titleIdx != -1) {
QMetaProperty prop = meta->property(titleIdx);
qDebug() << "Property 'title' type:" << prop.typeName(); // Output: "QString"
}
<span>enumeratorCount()</span> / <span>enumerator(index)</span> / <span>indexOfEnumerator()</span>
int enumIdx = meta->indexOfEnumerator("Status");
if (enumIdx != -1) {
QMetaEnum enu = meta->enumerator(enumIdx);
qDebug() << "Enum name:" << enu.name(); // "Status"
qDebug() << "Key count:" << enu.keyCount(); // 3
for (int i = 0; i < enu.keyCount(); ++i) {
qDebug() << enu.key(i) << "=" << enu.value(i);
}
// Output:
// "Idle" = 0
// "Running" = 1
// "Finished" = 2
qDebug() << "Value 1 -> key:" << enu.valueToKey(1); // "Running"
qDebug() << "Key 'Finished' -> value:" << enu.keyToValue("Finished"); // 2
}
<span>classInfoCount()</span> / <span>classInfo(index)</span> / <span>indexOfClassInfo()</span>
int authorIdx = meta->indexOfClassInfo("Author");
if (authorIdx != -1) {
QMetaClassInfo info = meta->classInfo(authorIdx);
qDebug() << "ClassInfo -" << info.name() << "=" << info.value(); // Output: "Author = QtUser"
}
<span><span>QMetaMethod</span></span> Example
Assuming we retrieve the <span>compute</span> method:
int methodIdx = meta->indexOfMethod("compute(int,int)");
QMetaMethod method = meta->method(methodIdx);
qDebug() << "Method name:" << method.name(); // "compute"
qDebug() << "Signature:" << method.methodSignature(); // "compute(int,int)"
qDebug() << "Return type:" << method.typeName(); // "QString"
qDebug() << "Parameter count:" << method.parameterCount(); // 2
qDebug() << "Param types:" << method.parameterTypes(); // ("int", "int")
qDebug() << "Param names:" << method.parameterNames(); // ("a", "b")
qDebug() << "Method type:" << method.methodType(); // QMetaMethod::Slot (or Method)
// Invoke method
QString result;
// obj is the instance object
method.invoke(obj, Qt::DirectConnection, Q_RETURN_ARG(QString, result), Q_ARG(int, 5), Q_ARG(int, 3));
qDebug() << "Invoked result:" << result; // "Result: 8"
<span>QMetaProperty</span> Example
int propIdx = meta->indexOfProperty("value");
QMetaProperty prop = meta->property(propIdx);
qDebug() << "Property name:" << prop.name(); // "value"
qDebug() << "Type:" << prop.typeName(); // "int"
qDebug() << "Readable:" << prop.isReadable(); // true
qDebug() << "Writable:" << prop.isWritable(); // true
qDebug() << "Resettable:" << prop.isResettable(); // true
// Read/Write
prop.write(obj, 42);
QVariant val = prop.read(obj);
qDebug() << "Read value:" << val.toInt(); // 42
prop.reset(obj);
qDebug() << "After reset:" << prop.read(obj).toInt(); // 0
// Notify signal
if (prop.hasNotifySignal()) {
QMetaMethod sig = prop.notifySignal();
qDebug() << "Notify signal:" << sig.name(); // "valueChanged"
}
Dynamic Properties
obj->setProperty("dynamicColor", "red");
obj->setProperty("priority", 5);
QVariant color = obj->property("dynamicColor");
QVariant prio = obj->property("priority");
qDebug() << "Dynamic color:" << color.toString(); // "red"
qDebug() << "Priority:" << prio.toInt(); // 5
// Get all dynamic property names
QList<QByteArray> dynNames = obj->dynamicPropertyNames();
for (const QByteArray& name : dynNames) {
qDebug() << "Dynamic prop:" << name << "=" << obj->property(name);
}
Signal-Slot Connections (Meta-Object Method)
Static String Connection (Old Style, Not Recommended but Compatible)
// Connect valueChanged -> doSomething (requires parameter matching)
connect(obj, SIGNAL(valueChanged(int)), obj, SLOT(doSomething(int,QString))); // ❌ Parameters do not match! Will not connect successfully
// Correct example: using lambda or adapter
connect(obj, &MyWidget::valueChanged, obj, [=](int v) { obj->doSomething(v, "from signal");});
// Connection based on QMetaMethod (Advanced Usage)
QMetaMethod signal = meta->method(meta->indexOfSignal("valueChanged(int)"));
QMetaMethod slot = meta->method(meta->indexOfSlot("doSomething(int,QString)"));
// Note: Parameters must be compatible! Here they are not, just for syntax demonstration
// In practice, it is recommended to use new-style connect or invokeMethod
// Dynamic method invocation using QMetaObject::invokeMethod
// Call void slot
QMetaObject::invokeMethod(obj, "doSomething", Qt::DirectConnection, Q_ARG(int, 100), Q_ARG(QString, "Hello"));
// Call method with return value
QString res;
QMetaObject::invokeMethod(obj, "compute", Qt::DirectConnection, Q_RETURN_ARG(QString, res), Q_ARG(int, 10), Q_ARG(int, 20));
qDebug() << "Computed via invokeMethod:" << res; // "Result: 30"
// Constructing Objects (newInstance) Modify MyWidget constructor:
// MyWidget.h
explicit MyWidget(int val, const QString& title, QWidget* parent = nullptr);
Q_INVOKABLE explicit MyWidget(int val, const QString& title);
// MyWidget.cpp
MyWidget::MyWidget(int val, const QString& title, QWidget* parent) : QWidget(parent), m_value(val), m_title(title) {}
// Overloaded (for newInstance)
MyWidget::MyWidget(int val, const QString& title) : MyWidget(val, title, nullptr) {}
// Then:
const QMetaObject* mo = &MyWidget::staticMetaObject;
QObject* newObj = mo->newInstance(Q_ARG(int, 99), Q_ARG(QString, "New Instance"));
if (newObj) {
MyWidget* w = qobject_cast<MyWidget*>(newObj);
qDebug() << "New instance title:" << w->title(); // "New Instance"
qDebug() << "New instance value:" << w->value(); // 99
delete w;
} else {
qDebug() << "Failed to create instance!";
}