【Qt编程】- 信号槽

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语言项目

【Qt编程】- 信号槽_第1张图片

【Qt编程】- 信号槽_第2张图片


【Qt编程】- 信号槽_第3张图片




【Qt编程】- 信号槽_第4张图片


main.c

#include <stdio.h>

int main(void)
{
    printf("Hello World!\n");
    return 0;
}

qtc.pro 项目文件

TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt

SOURCES += main.c

ctrl+B 生成项目,ctrl+R 运行项目



第二个例子 非qt项目的c++项目

【Qt编程】- 信号槽_第5张图片


main.cpp

#include <iostream>

using namespace std;

int main()
{
    cout << "Hello World!" << endl;
    return 0;
}


qtcpp.pro项目文件

TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt

SOURCES += main.cpp


可以看出来几乎和上面一样:


但是这个时候,我要添加一个Counter类,来测试moc的功能

【Qt编程】- 信号槽_第6张图片


【Qt编程】- 信号槽_第7张图片

【Qt编程】- 信号槽_第8张图片

qtcpp.pro里面多了一行

HEADERS += \
    Counter.h

Counter.h的代码和signal&slot的经典例子的Counter几乎一样

#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

修改qtcpp.pro告知使用moc生成moc代码和添加qt头文件路径

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.

修改一下main.cpp使用signal和slot

#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;
}

先看看moc生成的元对象代码:

/****************************************************************************
** 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_Counterqt_meta_stringdata_Counter,一个是索引一个字符串表,大概可以在运行时知道方法和对象的名字。

头文件里面声明的signal也被翻译成了一个函数,里面会去调用

QMetaObject::activate()


按F5开始用cdb调试一把看看这些逻辑都是怎么起作用的。


跟进到src/corelib/kernel/qobject.cpp

【Qt编程】- 信号槽_第9张图片


跟进到

bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index,
                                 const QObject *receiver, int method_index,
                                 const QMetaObject *rmeta, int type, int *types)
{

创建一个新的connection添加到列表

    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;
    }

注册完毕,接下来调用setValue() 触发一个a对象的signal,而这个a对象的signal是如何触发b对象的slot的呢?

【Qt编程】- 信号槽_第10张图片


【Qt编程】- 信号槽_第11张图片


跟进到

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

直接在setValue里面F9断点一下,看看调用堆栈就知道了。


原来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: ;
        }
    }
}


最终的slot调用,b对象的

【Qt编程】- 信号槽_第12张图片


我注意到,b对象里面也会emit一个signal,valueChanged, 但是,呵呵 ,这个b对象的signal,我可没有实现做connect到任何其他的slot。

一个坏主意,留给大家思考,如果我把b的signal在连接到a的slot,那是不是就死循环了


运行结果:

【Qt编程】- 信号槽_第13张图片


这一节主要是调试signal和slot基本原理的过程,下一节,按部就班的生成一个标准的GUI例子。



你可能感兴趣的:(qt,Signal,SLOT,emit,moc)