http://woboq.com/blog/how-qt-signals-slots-work.html
http://ntcore.com/files/qtrev.htm
http://qt-project.org/doc/qtcreator-2.6/creator-targets.html
第一个例子 非qt项目的纯c语言项目
main.c
#include <stdio.h> int main(void) { printf("Hello World!\n"); return 0; }
TEMPLATE = app CONFIG += console CONFIG -= app_bundle CONFIG -= qt SOURCES += main.c
第二个例子 非qt项目的c++项目
main.cpp
#include <iostream> using namespace std; int main() { cout << "Hello World!" << endl; return 0; }
TEMPLATE = app CONFIG += console CONFIG -= app_bundle CONFIG -= qt SOURCES += main.cpp
但是这个时候,我要添加一个Counter类,来测试moc的功能
qtcpp.pro里面多了一行
HEADERS += \ Counter.h
#ifndef COUNTER_H #define COUNTER_H #include <QObject> class Counter : public QObject{ Q_OBJECT public: Counter(){m_value = 0;} ~Counter(){} int value() const {return m_value;} public slots: void setValue(int v){ if (v != m_value){ m_value = v; emit valueChanged(v); } } signals: void valueChanged(int v); private: int m_value; }; #endif // COUNTER_H
TEMPLATE = app CONFIG += console CONFIG += moc CONFIG -= app_bundle CONFIG += qt SOURCES += main.cpp HEADERS += \ Counter.h
15:39:37: 为项目qtcpp执行步骤 ... 15:39:37: 配置没有改变, 跳过 qmake 步骤。 15:39:38: 正在启动 "C:\Qt\qtcreator-3.1.1\bin\jom.exe" C:\Qt\qtcreator-3.1.1\bin\jom.exe -f Makefile.Debug C:\Qt\4.8.6\bin\moc.exe -DUNICODE -DWIN32 -DQT_DLL -DQT_GUI_LIB -DQT_CORE_LIB -DQT_HAVE_MMX -DQT_HAVE_3DNOW -DQT_HAVE_SSE -DQT_HAVE_MMXEXT -DQT_HAVE_SSE2 -DQT_THREAD_SUPPORT -I"..\..\4.8.6\include\QtCore" -I"..\..\4.8.6\include\QtGui" -I"..\..\4.8.6\include" -I"..\..\4.8.6\include\ActiveQt" -I"debug" -I"..\qtcpp" -I"." -I"..\..\4.8.6\mkspecs\win32-msvc2008" -D_MSC_VER=1500 -DWIN32 ..\qtcpp\Counter.h -o debug\moc_Counter.cpp cl -c -nologo -Zm200 -Zc:wchar_t- -Zi -MDd -GR -EHsc -W3 -w34100 -w34189 -DUNICODE -DWIN32 -DQT_DLL -DQT_GUI_LIB -DQT_CORE_LIB -DQT_HAVE_MMX -DQT_HAVE_3DNOW -DQT_HAVE_SSE -DQT_HAVE_MMXEXT -DQT_HAVE_SSE2 -DQT_THREAD_SUPPORT -I"..\..\4.8.6\include\QtCore" -I"..\..\4.8.6\include\QtGui" -I"..\..\4.8.6\include" -I"..\..\4.8.6\include\ActiveQt" -I"debug" -I"..\qtcpp" -I"." -I"..\..\4.8.6\mkspecs\win32-msvc2008" -Fodebug\ @C:\Users\ADMINI~1\AppData\Local\Temp\moc_Counter.obj.10572.156.jom moc_Counter.cpp link /LIBPATH:"c:\Qt\4.8.6\lib" /NOLOGO /DYNAMICBASE /NXCOMPAT /DEBUG /SUBSYSTEM:CONSOLE "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" /MANIFEST /MANIFESTFILE:"debug\qtcpp.intermediate.manifest" /OUT:debug\qtcpp.exe @C:\Users\ADMINI~1\AppData\Local\Temp\qtcpp.exe.10572.1139.jom LINK : debug\qtcpp.exe not found or not built by the last incremental link; performing full link mt.exe -nologo -manifest "debug\qtcpp.intermediate.manifest" -outputresource:debug\qtcpp.exe;1 15:39:39: 进程"C:\Qt\qtcreator-3.1.1\bin\jom.exe"正常退出。 15:39:39: Elapsed time: 00:02.
#include <iostream> using namespace std; #include "Counter.h" int main() { Counter a, b; QObject::connect(&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue(int))); a.setValue(12); cout << "a.value=" << a.value() << ", b.value="<<b.value()<<endl; a.setValue(24); cout << "a.value=" << a.value() << ", b.value="<<b.value()<<endl; cout << "Hello World!" << endl; return 0; }
/**************************************************************************** ** Meta object code from reading C++ file 'Counter.h' ** ** Created by: The Qt Meta Object Compiler version 63 (Qt 4.8.6) ** ** WARNING! All changes made in this file will be lost! *****************************************************************************/ #include "../../qtcpp/Counter.h" #if !defined(Q_MOC_OUTPUT_REVISION) #error "The header file 'Counter.h' doesn't include <QObject>." #elif Q_MOC_OUTPUT_REVISION != 63 #error "This file was generated using the moc from 4.8.6. It" #error "cannot be used with the include files from this version of Qt." #error "(The moc has changed too much.)" #endif QT_BEGIN_MOC_NAMESPACE static const uint qt_meta_data_Counter[] = { // content: 6, // revision 0, // classname 0, 0, // classinfo 2, 14, // methods 0, 0, // properties 0, 0, // enums/sets 0, 0, // constructors 0, // flags 1, // signalCount // signals: signature, parameters, type, tag, flags 11, 9, 8, 8, 0x05, // slots: signature, parameters, type, tag, flags 29, 9, 8, 8, 0x0a, 0 // eod }; static const char qt_meta_stringdata_Counter[] = { "Counter\0\0v\0valueChanged(int)\0setValue(int)\0" }; void Counter::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) { if (_c == QMetaObject::InvokeMetaMethod) { Q_ASSERT(staticMetaObject.cast(_o)); Counter *_t = static_cast<Counter *>(_o); switch (_id) { case 0: _t->valueChanged((*reinterpret_cast< int(*)>(_a[1]))); break; case 1: _t->setValue((*reinterpret_cast< int(*)>(_a[1]))); break; default: ; } } } const QMetaObjectExtraData Counter::staticMetaObjectExtraData = { 0, qt_static_metacall }; const QMetaObject Counter::staticMetaObject = { { &QObject::staticMetaObject, qt_meta_stringdata_Counter, qt_meta_data_Counter, &staticMetaObjectExtraData } }; #ifdef Q_NO_DATA_RELOCATION const QMetaObject &Counter::getStaticMetaObject() { return staticMetaObject; } #endif //Q_NO_DATA_RELOCATION const QMetaObject *Counter::metaObject() const { return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; } void *Counter::qt_metacast(const char *_clname) { if (!_clname) return 0; if (!strcmp(_clname, qt_meta_stringdata_Counter)) return static_cast<void*>(const_cast< Counter*>(this)); return QObject::qt_metacast(_clname); } int Counter::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 < 2) qt_static_metacall(this, _c, _id, _a); _id -= 2; } return _id; } // SIGNAL 0 void Counter::valueChanged(int _t1) { void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) }; QMetaObject::activate(this, &staticMetaObject, 0, _a); } QT_END_MOC_NAMESPACE
注意qt_meta_data_Counter和qt_meta_stringdata_Counter,一个是索引一个字符串表,大概可以在运行时知道方法和对象的名字。
头文件里面声明的signal也被翻译成了一个函数,里面会去调用
QMetaObject::activate()
按F5开始用cdb调试一把看看这些逻辑都是怎么起作用的。
跟进到src/corelib/kernel/qobject.cpp
跟进到
bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index, const QObject *receiver, int method_index, const QMetaObject *rmeta, int type, int *types) {
QObjectPrivate::Connection *c = new QObjectPrivate::Connection; c->sender = s; c->receiver = r; c->method_relative = method_index; c->method_offset = method_offset; c->connectionType = type; c->argumentTypes = types; c->nextConnectionList = 0; c->callFunction = callFunction; QT_TRY { QObjectPrivate::get(s)->addConnection(signal_index, c); } QT_CATCH(...) { delete c; QT_RETHROW; }
跟进到
void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index, void **argv) {
原来QMetaObject::activate()会搜索啊,搜索到符合条件的connection,开始调用signal的receiver的slot
QT_TRY { callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv); } QT_CATCH(...) {
void Counter::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) { if (_c == QMetaObject::InvokeMetaMethod) { Q_ASSERT(staticMetaObject.cast(_o)); Counter *_t = static_cast<Counter *>(_o); switch (_id) { case 0: _t->valueChanged((*reinterpret_cast< int(*)>(_a[1]))); break; case 1: _t->setValue((*reinterpret_cast< int(*)>(_a[1]))); break; default: ; } } }
我注意到,b对象里面也会emit一个signal,valueChanged, 但是,呵呵 ,这个b对象的signal,我可没有实现做connect到任何其他的slot。
一个坏主意,留给大家思考,如果我把b的signal在连接到a的slot,那是不是就死循环了?
运行结果:
这一节主要是调试signal和slot基本原理的过程,下一节,按部就班的生成一个标准的GUI例子。