版本Qt5.12.3 moc_test.cpp位于可执行文件目录下,其余源代码都位于Qt5.12.3\5.12.3\Src\qtbase\src\corelib\kernel

一. MOC

1.1 简介

元对象编译器moc(Meta-Object Compiler)是Qt对C++的扩展。可以先把它看作一个代码生成器。以test类为例,构建项目时,moc读取C++头文件。如果在test.h中找到Q_OBJECT宏,它将生成一个moc_test.cpp,其中包含test类的元对象(metaObject)代码。这个新的文件和test.cpp一起参与编译,最终被链接到二进制代码中去。

Qt 将源代码交给标准 C++编译器,如gcc之前,需要事先将扩展的语法(Q_OBJECT,SLOT,SIGNAL等)展开来。完成这一操作的就是moc。

可以在命令行下输入moc test.h -o moc_test.cpp手动生成。

1.2 宏

Q_OBJECT,SLOT,SIGNAL,emit, Q_INVOKABLE等宏是Qt扩展的语法,它们其实定义在qobjectdefs.h中,编译时被moc展开。

二. moc_test.cpp分析

先贴一下代码:

test.h

class test : public QObject
{Q_OBJECT
public:explicit test(QObject *parent = nullptr);int getValue(){return m_value;}Q_INVOKABLE void identifyByMoc();signals:void testSignal1();void testSignal2();void testSignal3();void valueChanged(int newValue);public slots:void testSlot1();void testSlot2();void setValue(int value);private:int m_value;};

test.cpp

#include "test.h"test::test(QObject *parent) : QObject(parent)
{m_value = 0;
}void test::identifyByMoc(){qDebug()<<"Identified By Moc";
}void test::testSlot1(){qDebug()<<"Invoke testSlot1";
}void test::testSlot2(){qDebug()<<"Invoke testSlot2";
}void test::setValue(int value)
{m_value = value;emit valueChanged(value);
}

main.cpp

#include “test.h”void test_value()
{test a,b;QObject::connect(&a,SIGNAL(valueChanged(int)),&b,SLOT(setValue(int)));QObject::connect(&a,SIGNAL(valueChanged(int)),&a,SLOT(testSlot1()));qDebug()<<SIGNAL(valueChanged(int))<<SLOT(setValue(int));qDebug()<<"before: b.getValue ="<<b.getValue();a.setValue(12);qDebug()<<"after: b.getValue ="<<b.getValue();
}

moc_test.cpp

/****************************************************************************
** Meta object code from reading C++ file 'test.h'
**
** Created by: The Qt Meta Object Compiler version 67 (Qt 5.12.3)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/
#include "../../mocTest/test.h"
#include <QtCore/qbytearray.h>
#include <QtCore/qmetatype.h>
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'test.h' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 67
#error "This file was generated using the moc from 5.12.3. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
#endifQT_BEGIN_MOC_NAMESPACE
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED//此处省略见下文QT_WARNING_POP
QT_END_MOC_NAMESPACE

moc_test.cpp看起来很复杂,分三部分来看:

2.1 类信息

位于moc_test.cpp头部

struct qt_meta_stringdata_test_t { //定义保存类信息的结构体QByteArrayData data[12];char stringdata0[113];
};

QT_MOC_LITERAL宏的作用是为stringdata0中保存的每个函数名都创建一个QByteArrayData,宏参数为函数的索引值,偏移量,函数名长度。

#define QT_MOC_LITERAL(idx, ofs, len \
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
qptrdiff(offsetof(qt_meta_stringdata_test_t, stringdata0) + ofs \
- idx * sizeof(QByteArrayData)) \
)
static const qt_meta_stringdata_test_t
qt_meta_stringdata_test = { //初始化保存类信息的结构体
{QT_MOC_LITERAL(0, 0, 4), // "test"
QT_MOC_LITERAL(1, 5, 11), // "testSignal1"
QT_MOC_LITERAL(2, 17, 0), // ""
QT_MOC_LITERAL(3, 18, 11), //"testSignal2"
QT_MOC_LITERAL(4, 30, 11), //"testSignal3"
QT_MOC_LITERAL(5, 42, 12), // "valueChanged"
QT_MOC_LITERAL(6, 55, 8), //"newValue"
QT_MOC_LITERAL(7, 64, 9), //"testSlot1"
QT_MOC_LITERAL(8, 74, 9), //"testSlot2"
QT_MOC_LITERAL(9, 84, 8), //"setValue"
QT_MOC_LITERAL(10, 93, 5), //"value"
QT_MOC_LITERAL(11, 99, 13) //"identifyByMoc
},
"test\0testSignal1\0\0testSignal2\0testSignal3\0"
"valueChanged\0newValue\0testSlot1\0"
"testSlot2\0setValue\0value\0identifyByMoc"
};
#undef QT_MOC_LITERALstatic const uint qt_meta_data_test[] = {//保存了信号,槽及moc识别的其他函数的个数,函数索引,返回类型,参数个数,参数类型等信息。//content:8,       // revision0,       // classname0,    0, // classinfo8,   14, // methods0,    0, // properties0,    0, // enums/sets0,    0, // constructors0,       // flags4,       // signalCount//signals: name, argc, parameters, tag, flags1,    0,   54,   2, 0x06 /* Public */,3,    0,   55,   2, 0x06 /* Public */,4,    0,   56,   2, 0x06 /* Public */,5,    1,   57,   2, 0x06 /* Public */,//slots: name, argc, parameters, tag, flags7,    0,   60,   2, 0x0a /* Public */,8,    0,   61,   2, 0x0a /* Public */,9,    1,   62,   2, 0x0a /* Public */,//methods: name, argc, parameters, tag, flags11,    0,   65,   2, 0x02 /* Public */,//signals: parametersQMetaType::Void,QMetaType::Void,QMetaType::Void,QMetaType::Void, QMetaType::Int,   6,//slots: parametersQMetaType::Void,QMetaType::Void,QMetaType::Void, QMetaType::Int,  10,//methods: parametersQMetaType::Void,0        // eod
};

2.2 signals实现

位于moc_test.cpp底部:

// SIGNAL 0
void test::testSignal1()
{QMetaObject::activate(this, &staticMetaObject, 0, nullptr);
}// SIGNAL 1
void test::testSignal2()
{QMetaObject::activate(this, &staticMetaObject, 1, nullptr);
}// SIGNAL 2
void test::testSignal3()
{QMetaObject::activate(this, &staticMetaObject, 2, nullptr);
}// SIGNAL 3
void test::valueChanged(int _t1)
{void *_a[] = { nullptr,const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };QMetaObject::activate(this, &staticMetaObject, 3, _a);
}

test.h中构造函数一个,普通函数一个(getValue),signal四个,slot三个,Q_INVOKABLE修饰的方法一个。其中三个槽函数需要自己在test.cpp里实现,四个信号函数由moc自动在moc_test.cpp中实现,在注释可以看到索引根据声明顺序分别为0,1,2,3。

2.3 Q_OBJECT宏的展开

查找qobjectdefs.h,可以找到Q_OBJECT宏的定义

#define Q_OBJECT \
public: \QT_WARNING_PUSH \Q_OBJECT_NO_OVERRIDE_WARNING \static const QMetaObject staticMetaObject; \virtual const QMetaObject *metaObject() const; \virtual void *qt_metacast(const char *); \virtual int qt_metacall(QMetaObject::Call, int, void **); \QT_TR_FUNCTIONS \
private: \Q_OBJECT_NO_ATTRIBUTES_WARNING \Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *,QMetaObject::Call, int, void **); \QT_WARNING_POP \
struct QPrivateSignal {}; \
QT_ANNOTATE_CLASS(qt_qobject,
"")

正对应moc_test.cpp里的函数等,我们取重要的几个:
位于moc_test.cpp中部:

2.3.1 qt_static_metacall

void test::qt_static_metacall(QObject *_o,QMetaObject::Call _c, int _id, void **_a)
{if (_c == QMetaObject::InvokeMetaMethod) {auto *_t = static_cast<test *>(_o);Q_UNUSED(_t)switch (_id) {case 0: _t->testSignal1(); break;case 1: _t->testSignal2(); break;case 2: _t->testSignal3(); break;case 3: _t->valueChanged((*reinterpret_cast< int(*)>(_a[1])));break;case 4: _t->testSlot1(); break;case 5: _t->testSlot2(); break;case 6: _t->setValue((*reinterpret_cast< int(*)>(_a[1])));break;case 7: _t->identifyByMoc(); break;default: ;}}else if (_c == QMetaObject::IndexOfMethod) {int *result = reinterpret_cast<int *>(_a[0]);{using _t = void (test::*)();if (*reinterpret_cast<_t *>(_a[1]) ==static_cast<_t>(&test::testSignal1)) {*result = 0;return;}}{using _t = void (test::*)();if (*reinterpret_cast<_t*>(_a[1]) == static_cast<_t>(&test::testSignal2)) {*result = 1;return;}}{using _t = void (test::*)();if (*reinterpret_cast<_t *>(_a[1]) ==
static_cast<_t>(&test::testSignal3)) {*result = 2;return;}}{using _t = void (test::*)(int );if (*reinterpret_cast<_t *>(_a[1]) ==
static_cast<_t>(&test::valueChanged)) {*result = 3;return;}}}
}

根据函数索引调用槽函数,在这里可以看出信号函数也可以当作槽函数一样被调用,这也是信号槽调用过程的最后一步(先留个印象)

2.3.2 staticMetaObject

test类的元对象(QMetaObject),保存了test类的信息。保存的数据qt_meta_stringdata_test.data及qt_meta_data_test在moc文件的顶部定义并初始化。

QT_INIT_METAOBJECT const QMetaObject test::staticMetaObject = { {&QObject::staticMetaObject,qt_meta_stringdata_test.data,qt_meta_data_test,qt_static_metacall,nullptr,nullptr
} };

2.3.3 metaObject

返回当前的QMetaObject,一般是返回staticMetaObject,即2介绍的。

const QMetaObject *test::metaObject() const
{return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}

2.3.4 qt_metacast

类型转换

void *test::qt_metacast(const char *_clname)
{if (!_clname) return nullptr;if (!strcmp(_clname, qt_meta_stringdata_test.stringdata0))return static_cast<void*>(this);return QObject::qt_metacast(_clname);
}

2.3.5 qt_metacall

int test::qt_metacall(QMetaObject::Call _c,int _id, void **_a)
{_id = QObject::qt_metacall(_c, _id, _a);if (_id < 0)return _id;if (_c == QMetaObject::InvokeMetaMethod) {if (_id < 8)qt_static_metacall(this, _c, _id, _a);_id -= 8;}else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {if (_id < 8)*reinterpret_cast<int*>(_a[0]) = -1;_id -= 8;}return _id;
}

在内部调用了qt_static_metacall

总结:Moc的作用就是把Q_OBJECT SIGNAL Q_INVOKABLE等宏展开,并保存类中特定函数(signals,slots标签下的函数及Q_INVOKABLE修饰的函数等)的信息,创建函数的回调。

三. connect

要使用Qt的信号-槽机制,必须要connect这一步。我们查看QObject::connect的源码:
QObject.cpp

QMetaObject::Connection
QObject::connect(const QObject *sender, const char *signal,constQObject *receiver, const char *method, Qt::ConnectionType type)
{//此处省略了函数参数检查,信号及槽函数索引获取,connect类型处理等代码。
//最后其实调用了QMetaObjectPrivate::connectQMetaObject::Connection handle = QMetaObject::Connection(QMetaObjectPrivate::connect(sender, signal_index, smeta, receiver, method_index_relative, rmeta ,type, types));return handle;
}

QMetaObjectPrivate::connect同样位于QObject.cpp

QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender,int signal_index, const QMetaObject *smeta,const QObject *receiver, int method_index,const QMetaObject *rmeta, int type, int *types)
{QObject *s = const_cast<QObject *>(sender);QObject *r = const_cast<QObject *>(receiver);int method_offset = rmeta ? rmeta->methodOffset() : 0;Q_ASSERT(!rmeta || QMetaObjectPrivate::get(rmeta)->revision >= 6);QObjectPrivate::StaticMetaCallFunction callFunction =rmeta ? rmeta->d.static_metacall : 0;QOrderedMutexLocker locker(signalSlotLock(sender),signalSlotLock(receiver));if (type & Qt::UniqueConnection) {QObjectConnectionListVector *connectionLists =QObjectPrivate::get(s)->connectionLists;if (connectionLists && connectionLists->count() >signal_index) {const QObjectPrivate::Connection *c2 = (*connectionLists)[signal_index].first;int method_index_absolute = method_index + method_offset;while (c2) {if (!c2->isSlotObject && c2->receiver == receiver && c2->method() == method_index_absolute)return 0;c2 = c2->nextConnectionList;}}type &= Qt::UniqueConnection - 1;}// QObjectPrivate::Connection实例化,
//存储了信号-槽链接的信息QScopedPointer<QObjectPrivate::Connection> c(new QObjectPrivate::Connection);c->sender = s;c->signal_index = signal_index;c->receiver = r;c->method_relative = method_index;c->method_offset = method_offset;c->connectionType = type;c->isSlotObject = false;c->argumentTypes.store(types);c->nextConnectionList = 0;c->callFunction = callFunction;//addConnection为信号发送者s保存了这个信号-槽链接,具体保存了什么,还需要//分析QObjectPrivate::Connection以及QObjectPrivate::addConnectionQObjectPrivate::get(s)->addConnection(signal_index, c.data());locker.unlock();QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index);if (smethod.isValid())s->connectNotify(smethod);return c.take();
}

qobject_p.h中定义了class QObjectPrivate
它的几个成员如下:

struct Connection
{QObject *sender;QObject *receiver;union {StaticMetaCallFunction callFunction;QtPrivate::QSlotObjectBase *slotObj;};// The next pointer for the singly-linked ConnectionListConnection *nextConnectionList;//senders linked listConnection *next;Connection **prev;ushort method_offset;ushort method_relative;uint signal_index : 27; // In signal range (see QObjectPrivate::signalIndex())ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking//省略部分代码
};

Connection结构体保存了一个连接。其中的信息包括信号发送者指针,信号接收者指针以及指向下一个Connection的指针,信号索引,连接类型等

一个信号可以对应多个槽函数,这里用ConnectionList保存一个信号对应的所有连接。它是一个单向链表,每个节点都是一个Connection,通过它内部的nextConnectionList指针指向下一个Connection。在这里仅保存头尾指针即可。

struct ConnectionList {ConnectionList() : first(nullptr), last(nullptr) {}Connection *first;Connection *last;
};

connectionLists保存此对象作为信号发送者所对应的所有连接。这个向量里每个元素都是一个ConnectionList单链表

QObjectConnectionListVector *connectionLists;

QObject.cpp里定义了QObjectConnectionListVector

class QObjectConnectionListVector : public
QVector<QObjectPrivate::ConnectionList>
{public:bool orphaned; //the QObject owner of this vector has been destroyed while the vector was inUsebool dirty; //some Connection have been disconnected (their receiver is 0) but not removed from the list yetint inUse; //number of functions that are currently accessing this object or its connectionsQObjectPrivate::ConnectionList allsignals;QObjectConnectionListVector(): QVector<QObjectPrivate::ConnectionList>(), orphaned(false),
dirty(false), inUse(0){}QObjectPrivate::ConnectionList &operator[](int at){if (at < 0)return allsignals;return QVector<QObjectPrivate::ConnectionList>::operator[](at);}
};

Connection *next和Connection**prev是此对象作为信号接收者时,保存发送者的双向链表的操作指针,这里的内容待以后补充。

接下来看最后的addConnection, 位于QObject.cpp

void QObjectPrivate::addConnection(int signal, Connection *c)
{Q_ASSERT(c->sender == q_ptr);if (!connectionLists)connectionLists = new QObjectConnectionListVector();if (signal >= connectionLists->count())connectionLists->resize(signal + 1);//根据信号索引取得此信号所对应的链表,并把此连接加入链表中。ConnectionList &connectionList = (*connectionLists)[signal];if (connectionList.last) {connectionList.last->nextConnectionList = c;}else {connectionList.first = c;}connectionList.last = c;cleanConnectionLists();//下面是对Connection* QObjectPrivate::senders双向链表的操作c->prev = &(QObjectPrivate::get(c->receiver)->senders);c->next = *c->prev;*c->prev = c;if (c->next)c->next->prev = &c->next;if (signal < 0) {connectedSignals[0] = connectedSignals[1] = ~0;}else if (signal < (int)sizeof(connectedSignals) * 8) {connectedSignals[signal >> 5] |= (1 << (signal & 0x1f));}
}

四. activate

连接完成后,我们调用信号函数,从moc_test.cpp里对信号函数的实现可知,其实是调用了QMetaObject::activate(this, &staticMetaObject, 3, _a);

// SIGNAL 3
void test::valueChanged(int _t1)
{void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };QMetaObject::activate(this, &staticMetaObject, 3, _a);
}

QMetaObject::activate源码在QObject.cpp中

void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index, void **argv)
{activate(sender, QMetaObjectPrivate::signalOffset(m), local_signal_index, argv);
}

下面的activate函数省略了部分代码,方便理解。

void QMetaObject::activate(QObject *sender, int signalOffset, int local_signal_index, void **argv)
{int signal_index = signalOffset + local_signal_index;//判断是否有与该信号相连接的接收对象if(!sender->d_func()->isSignalConnected(signal_index))return; // nothing connected to these signals, and no spy//给信号量加锁,因为在connectionLists里所有的操作都是线程安全的QMutexLocker locker(signalSlotLock(sender));//获取与该信号的ConnectionList链表QObjectConnectionListVector *connectionLists = sender->d_func()->connectionLists;const QObjectPrivate::ConnectionList *list = &connectionLists->at(signal_index);QObjectPrivate::Connection *c = list->first;if (!c) continue;QObjectPrivate::Connection *last = list->last;//循环执行该信号对应的所有槽函数do {if (!c->receiver)continue;QObject * const receiver = c->receiver;const bool receiverInSameThread = QThread::currentThreadId() == receiver->d_func()->threadData->threadId;// 决定该连接是马上响应还是把它放到事件队列中if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)|| (c->connectionType == Qt::QueuedConnection)) {queued_activate(sender, signal_index, c, argv);continue;} else if (c->connectionType == Qt::BlockingQueuedConnection) {continue;}QConnectionSenderSwitcher sw;if (receiverInSameThread)sw.switchSender(receiver, sender, signal_index);const QObjectPrivate::StaticMetaCallFunction callFunction = c->callFunction;const int method_relative = c->method_relative;//下面if-else结构包含三种调用槽函数的方式。if (c->isSlotObject) {c->slotObj->ref();QScopedPointer<QtPrivate::QSlotObjectBase,QSlotObjectBaseDeleter>obj(c->slotObj);locker.unlock();Q_TRACE(QMetaObject_activate_begin_slot_functor, obj.data());//一,通过call调用接收者中的槽函数obj->call(receiver, argv ? argv : empty_argv);Q_TRACE(QMetaObject_activate_end_slot_functor, obj.data());obj.reset();locker.relock();} else if (c->callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) {const int methodIndex = c->method();const int method_relative = c->method_relative;const auto callFunction = c->callFunction;locker.unlock();if(qt_signal_spy_callback_set.slot_begin_callback != 0)qt_signal_spy_callback_set.slot_begin_callback(receiver, methodIndex, argv ? argv : empty_argv);Q_TRACE(QMetaObject_activate_begin_slot, receiver, methodIndex);//二,callFunction即moc_test.cpp里的qt_static_metacallcallFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv);Q_TRACE(QMetaObject_activate_end_slot, receiver, methodIndex);if(qt_signal_spy_callback_set.slot_end_callback != 0)qt_signal_spy_callback_set.slot_end_callback(receiver, methodIndex);locker.relock();} else {const int method = c->method_relative + c->method_offset;locker.unlock();if(qt_signal_spy_callback_set.slot_begin_callback != 0) {qt_signal_spy_callback_set.slot_begin_callback(receiver, method,argv ? argv : empty_argv);}Q_TRACE(QMetaObject_activate_begin_slot, receiver, method);//三,metacall即moc_test.cpp里的qt_metacallmetacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);Q_TRACE(QMetaObject_activate_end_slot, receiver, method);if(qt_signal_spy_callback_set.slot_end_callback != 0)qt_signal_spy_callback_set.slot_end_callback(receiver,method);locker.relock();}        // 检查该对象没有被槽函数删除if (connectionLists->orphaned) break;} while (c != last && (c = c->nextConnectionList) != 0);
}

第二种方法解析:
qobjectdefs.h

struct Q_CORE_EXPORT QMetaObject
{struct { // private dataconst QMetaObject *superdata;const QByteArrayData *stringdata;const uint *data;typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);StaticMetacallFunction static_metacall;const QMetaObject * const *relatedMetaObjects;void *extradata; //reserved for future use
} d;
}

moc_test.cpp 用qt_static_metacall实例化d.static_metacall

QT_INIT_METAOBJECT const QMetaObject
test::staticMetaObject = { {&QObject::staticMetaObject,qt_meta_stringdata_test.data,qt_meta_data_test,qt_static_metacall,nullptr,nullptr
} };

qobject.cpp

QObjectPrivate::Connection *QMetaObjectPrivate::connect()
{QObjectPrivate::StaticMetaCallFunction callFunction = rmeta ? rmeta->d.static_metacall : 0;
}void QMetaObject::activate()
{const QObjectPrivate::StaticMetaCallFunction callFunction = c->callFunction;
callFunction(...);
//最后调用callFunction其实就是调用qt_static_metacall,还记的文章前边说的留个印象么,现在就到了调用的最后一步。
}

第三种方法解析:
qmetaobject.cpp

int QMetaObject::metacall(QObject *object, Call cl, int idx, void **argv)
{if (object->d_ptr->metaObject)return object->d_ptr->metaObject->metaCall(object, cl, idx,
argv);elsereturn object->qt_metacall(cl, idx, argv);
}

moc_test.cpp

int test::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{_id = QObject::qt_metacall(_c, _id, _a);if (_id < 0)return _id;if (_c == QMetaObject::InvokeMetaMethod) {if (_id < 8)qt_static_metacall(this, _c, _id, _a);_id -= 8;}else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {if (_id < 8)*reinterpret_cast<int*>(_a[0]) = -1;_id -= 8;}return _id;
}

qobject.cpp

void QMetaObject::activate()
{metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
}

metacall其实就是调用qt_metacall,在内部最终还是调用了qt_static_metacall

五. 总结

5.1 解析

Moc帮我们解析头文件中Qt特有的宏,将函数信息保存在数组中,构建信号函数的实现,槽函数的回调。

5.2 保存

QObject::connect将连接信息保存在结构体Connection中,一个信号可对应多个槽函数,这就形成了单链表ConnectionList,然后以信号在test类的MetaObject中的索引作为向量的索引保存所有链表,形成QObjectConnectionListVector* connectionLists.

5.3 调用

调用信号函数时,其实是调用activate函数,最终回到qt_static_metacall,调用槽函数。

Qt Moc及信号-槽源代码解析相关推荐

  1. Qt / Moc 和信号 - 槽解析

    目录 一. MOC 二. moc_test.cpp 分析 三. connect 四. activate 五. 总结 版本 Qt5.12.3 moc_test.cpp 位于可执行文件目录下,其余源代码都 ...

  2. Qt线程间信号槽传递自定义数据类型(qRegisterMetaType的使用)

    Qt线程间信号槽传递自定义数据类型(qRegisterMetaType的使用) #include <QMetaType> CFileDataModel::CFileDataModel(QO ...

  3. 玩转Qt(6)-认清信号槽的本质

    简介 猫和老鼠的故事 对象之间的通信机制 尝试一:直接调用 尝试二:回调函数+映射表 观察者模式 Qt的信号-槽 信号-槽简介 信号-槽分两种 信号-槽的实现 元对象编译器moc moc的本质-反射 ...

  4. Qt学习笔记-----信号槽

    Qt提供signals and slots mechanism(信号槽机制)来保证两个对象之前的关联(connection). 所谓信号槽,简单理解就是两部分,一个是某对象发出的信号,一个是某对象接收 ...

  5. Qt中绑定信号槽之后,信号槽无效

    下面程序编译没有错误,运行却未达到想要的效果,最后调试发现,是信号槽绑定后无效,即槽函数没有受到信号的触发.具体代码如下: main.cpp #include <QtCore> #incl ...

  6. Qt多线程间信号槽传递非QObject类型对象的参数

    一.以前就发现过这个问题: 在Qt项目中,有时候为了让自己的类,可以重载操作符 '=','<<','>>'. 也有时候需要用一个类进行文件的读写,所以很多C++类还是要简单化的 ...

  7. QT C++ 《信号-槽 基本操作》

    目录 <信号-槽 基本操作> 信号-槽连接 信号-槽断开连接 信号-槽连接 connect(ui.pushButton_SIGNAL, SIGNAL(clicked()), Client_ ...

  8. QT:PushButton+信号槽+Label简单使用

    实现内容 创建一个简单的QT GUI项目,实现点击按钮修改Label的内容 创建QT GUI 项目:ButtonTest 创建后的目录为: 添加界面布局 在界面添加两个PushButton和一个Lab ...

  9. qt 多线程、信号槽、moveToThread等机制之拨乱反正

    之所以要"拨乱反正",是因为很多教科书上的说法,还有网页上的说法,都是错误的. 我没有看过qt源码,看过一些书籍,做过一些实验,说下我的理解.如有谬误,还请讨论. 首先来看看教科书 ...

  10. Qt中取消信号槽的绑定关系

    Qt中取消对象的信号与槽的关联关系 (1)关于对象取消跟其它任何对象的关联关系 实例: QObject * obj = new QObject; disconnect(obj,0,0,0); 或者 o ...

最新文章

  1. Java基于自定义注解的面向切面的实现
  2. 将信息写入TXT文本中(java)
  3. AliCloudDenoise 语音增强算法:助力实时会议系统进入超清音质时代
  4. ABAP SOAMANAGER暴露的函数function module,以web service方式执行的运行时细节
  5. Cloud一分钟 | 一脚踏入云计算2.0时代,京东云这回拼的是“朋友 圈”;8400万元私有云招标...
  6. 【英语学习】【English L06】U07 Jobs L4 What do you think of our service?
  7. 单链表的创建、测长、打印、插入、删除、排序及逆置
  8. transition css3 渐变效果
  9. windbg使用教程: 具体实例
  10. 2015年1月c语言程序设 第二版,2015年电大1253+C语言程序设计A(2010年1月)小抄参考.doc...
  11. ASP.NET Core MVC 入门到精通 - 1. 开发必备工具 (2021)
  12. 认知突围--读书笔记二
  13. 【word】双栏写作如何调整为写满左栏再写右栏
  14. [bzoj1233]干草堆tower
  15. 安卓手机刷入面具Magisk
  16. 猫喜欢吃鱼,可猫不会游泳,鱼喜欢吃蚯蚓,可鱼不会上岸;上帝给了你很多诱惑,却不会让你轻易实现!...
  17. (新SOTA)UNETR++:轻量级的、高效、准确的共享权重的3D医学图像分割
  18. 图像处理(九)-图片和数组的转换
  19. 拓扑结构和几何结构的区别
  20. Python中拼音库PyPinyin

热门文章

  1. 智库说 | 杨宁:从城市管理走向城市治理 大数据将发挥更大作用
  2. 新手播音小白,想学播音从什么地方开始?
  3. 英特尔cpu与主板芯片组对应关系(包含12代)
  4. 全球及中国26二氟苯磺酰氯行业发展状况与前景趋势分析报告2022-2028年
  5. 对Si446x驱动库的一些修改
  6. 记升级springboot1.X 到springboot2.3.5踩的坑
  7. Greenplum Python工具库gpload学习——gpload类
  8. Java8流式操作-根据集合的某个属性值取交集
  9. java算法——通过身份证号获取出生的年月日
  10. python数据拟合之scipy.optimize.curve_fit