打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
QT分析之消息事件机制(四)
userphoto

2012.12.26

关注
QT分析之消息事件机制(四)

2010-01-17 14:31:14 阅读92 评论0 字号:大中小


目前还有两个疑问:一个是自定义的信号(SIGNAL)如何跟Windows的消息关联的;另一个是信号和槽(SLOT)是如何关联的。根据前面的分析,对第一个问题的猜测是在event()函数里增加相应处理;对第二个问题,应该有一个函数指针表使之关联。带着这些问题我们接着分析mouseReleaseEvent()。

void QAbstractButton::mouseReleaseEvent(QMouseEvent *e)
{
Q_D(QAbstractButton);
if (e->button() != Qt::LeftButton) {
e->ignore();
return;
}

if (!d->down) {
e->ignore();
return;
}

if (hitButton(e->pos())) {
d->repeatTimer.stop();
d->click(); // 调用QAbstractButtonPrivate::click()
e->accept();
} else {
setDown(false);
e->ignore();
}
}
进一步看QAbstractButtonPrivate::click()的实现代码:

void QAbstractButtonPrivate::click()
{
Q_Q(QAbstractButton);

down = false;
blockRefresh = true;
bool changeState = true;
if (checked && queryCheckedButton() == q) {
// the checked button of an exclusive or autoexclusive group cannot be unchecked
#ifndef QT_NO_BUTTONGROUP
if (group ? group->d_func()->exclusive : autoExclusive)
#else
if (autoExclusive)
#endif
changeState = false;
}

QPointer<QAbstractButton> guard(q);
if (changeState) {
q->nextCheckState();
if (!guard)
return;
}
blockRefresh = false;
refresh();
q->repaint(); //flush paint event before invoking potentially expensive operation
QApplication::flush();
if (guard)
emitReleased();
if (guard)
emitClicked();
}
主要就是调用emitReleased()和emitClicked(),我们看其中QAbstractButtonPrivate::emitClicked()的实现

void QAbstractButtonPrivate::emitClicked()
{
Q_Q(QAbstractButton);
QPointer<QAbstractButton> guard(q);
emit q->clicked(checked); // 这里调用的是QAbstractButton::clicked()
#ifndef QT_NO_BUTTONGROUP
if (guard && group) {
emit group->buttonClicked(group->id(q));
if (guard && group)
emit group->buttonClicked(q);
}
#endif
}

上面调用的函数实体,在moc_QAbstractButton.cpp里可以看到。这个文件实际是由QT的MOC工具自动产生的。

void QAbstractButton::clicked(bool _t1)
{
void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
QMetaObject::activate(this, &staticMetaObject, 2, 3, _a); // 和SLOT的调用关系应该是这里实现的。
}

我们先来看看QMetaObject类的定义:

struct Q_CORE_EXPORT QMetaObject
{

……

struct { // private data
const QMetaObject *superdata;
const char *stringdata;
const uint *data;
const void *extradata;
} d;
};

再看QMetaObjectPrivate的定义:

struct QMetaObjectPrivate
{
int revision;
int className;
int classInfoCount, classInfoData;
int methodCount, methodData;
int propertyCount, propertyData;
int enumeratorCount, enumeratorData;
int constructorCount, constructorData;
};

实际上QMetaObject::d.data指向的就是QMetaObjectPrivate结构体
我们看看staticMetaObject对象的定义:(同样在moc_QAbstractButton.cpp文件中)

const QMetaObject QAbstractButton::staticMetaObject = {
{ &QWidget::staticMetaObject, qt_meta_stringdata_QAbstractButton,
qt_meta_data_QAbstractButton, 0 }
};

这是一个常对象,除设定父类的staticMetaObject外,还设定了两个全局变量:qt_meta_stringdata_QAbstractButton和qt_meta_data_QAbstractButton。

所有的奥秘都在这两个变量里面了。根据前面分析qt_meta_data_QAbstractButton实际是QMetaObjectPrivate结构。

static const uint qt_meta_data_QAbstractButton[] = {

// content:
2, // revision
0, // classname
0, 0, // classinfo
11, 12, // methods
11, 67, // properties
0, 0, // enums/sets
0, 0, // constructors

// signals: signature, parameters, type, tag, flags

17, 16, 16, 16, 0x05,
// 17 -- 指的是stringdata中No.17字节开始的字符串,结合下面定义实际就是pressed()
27, 16, 16, 16, 0x05, // released()
46, 38, 16, 16, 0x05, // clicked(bool)
60, 16, 16, 16, 0x25, // clicked()
70, 38, 16, 16, 0x05, // toggled(bool)

// slots: signature, parameters, type, tag, flags
89, 84, 16, 16, 0x0a, // setIconSize(QSize)
113, 108, 16, 16, 0x0a,
131, 16, 16, 16, 0x2a,
146, 16, 16, 16, 0x0a,
154, 16, 16, 16, 0x0a,
163, 16, 16, 16, 0x0a,

// properties: name, type, flags
188, 180, 0x0a0, // text
199, 193, 0x, // icon
210, 204, 0x,
232, 219, 0x4c0,
246, 241, 0x0,
38, 241, 0x0,
256, 241, 0x0,
267, 241, 0x0,
285, 281, 0x0,
301, 281, 0x0,
320, 241, 0x0,

// properties: notify_signal_id
0,
0,
0,
0,
0,
4,
0,
0,
0,
0,
0,

0 // eod
};

static const char qt_meta_stringdata_QAbstractButton[] = {
"QAbstractButton\0\0pressed()\0released()\0"
"checked\0clicked(bool)\0clicked()\0"
"toggled(bool)\0size\0setIconSize(QSize)\0"
"msec\0animateClick(int)\0animateClick()\0"
"click()\0toggle()\0setChecked(bool)\0"
"QString\0text\0QIcon\0icon\0QSize\0iconSize\0"
"QKeySequence\0shortcut\0bool\0checkable\0"
"autoRepeat\0autoExclusive\0int\0"
"autoRepeatDelay\0autoRepeatInterval\0"
"down\0"
};

我们接着看QMetaObject::activate()的代码:

void QMetaObject::activate(QObject *sender, const QMetaObject *m,
int from_local_signal_index, int to_local_signal_index, void **argv)
{
int offset = m->methodOffset(); // 指向qt_meta_data_QAbstractButton[27]字节,也就是clicked(bool)
int from_signal_index = offset + from_local_signal_index; // 27 + 2 = 29
int to_signal_index = offset + to_local_signal_index; // 27 + 3 = 30
if (to_signal_index < 32
&& !qt_signal_spy_callback_set.signal_begin_callback
&& !qt_signal_spy_callback_set.signal_end_callback) {
uint signal_mask = (1 << (to_signal_index + 1)) - 1;
signal_mask ^= (1 << from_signal_index) - 1;

// sender指的是QPushButton,下面指向的是:QPushButtonPrivate::connectedSignals
if ((sender->d_func()->connectedSignals & signal_mask) == 0)
// nothing connected to these signals, and no spy
return;
}
activate(sender, from_signal_index, to_signal_index, argv);
}
可以看到,在判断本信号是否连接有槽之后,就调用了activate的重载函数:

void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv)
{
if (sender->d_func()->blockSig)
return;

void *empty_argv[] = { 0 };
if (qt_signal_spy_callback_set.signal_begin_callback != 0) {
qt_signal_spy_callback_set.signal_begin_callback(sender, from_signal_index,
argv ? argv : empty_argv);
}

QMutexLocker locker(&sender->d_func()->threadData->mutex);
QThreadData *currentThreadData = QThreadData::current();

QObjectConnectionListVector *connectionLists = sender->d_func()->connectionLists;
if (!connectionLists) {
if (qt_signal_spy_callback_set.signal_end_callback != 0)
qt_signal_spy_callback_set.signal_end_callback(sender, from_signal_index);
return;
}
++connectionLists->inUse;

// emit signals in the following order: from_signal_index <= signals <= to_signal_index, signal < 0
for (int signal = from_signal_index;
(signal >= from_signal_index && signal <= to_signal_index) || (signal == -2);
(signal == to_signal_index ? signal = -2 : ++signal))
{
if (signal >= connectionLists->count()) {
signal = to_signal_index;
continue;
}
int count = connectionLists->at(signal).count();

// 就是在这里获取信号接收的槽函数指针的。

// connectionLists里的数据,猜测是由QObject::connect()填进去的。
for (int i = 0; i < count; ++i) {
const QObjectPrivate::Connection *c = &connectionLists->at(signal)[i];
if (!c->receiver)
continue;

QObject * const receiver = c->receiver;

// determine if this connection should be sent immediately or
// put into the event queue
if ((c->connectionType == Qt::AutoConnection
&& (currentThreadData != sender->d_func()->threadData
|| receiver->d_func()->threadData != sender->d_func()->threadData))
|| (c->connectionType == Qt::QueuedConnection)) {
queued_activate(sender, signal, *c, argv);
continue;
} else if (c->connectionType == Qt::BlockingQueuedConnection) {
blocking_activate(sender, signal, *c, argv);
continue;
}

const int method = c->method;
QObjectPrivate::Sender currentSender;
currentSender.sender = sender;
currentSender.signal = signal < 0 ? from_signal_index : signal;
currentSender.ref = 1;
QObjectPrivate::Sender *previousSender = 0;
if (currentThreadData == receiver->d_func()->threadData)
previousSender = QObjectPrivate::setCurrentSender(receiver, &currentSender);
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);
}

#if defined(QT_NO_EXCEPTIONS)
receiver->qt_metacall(QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
#else
try {

// 在我们的分析中,连接的槽是QApplication::quit(),qt_metacall在哪定义的呢?
receiver->qt_metacall(QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
} catch (...) {
locker.relock();

QObjectPrivate::resetCurrentSender(receiver, &currentSender, previousSender);

--connectionLists->inUse;
Q_ASSERT(connectionLists->inUse >= 0);
if (connectionLists->orphaned && !connectionLists->inUse)
delete connectionLists;
throw;
}
#endif

locker.relock();

if (qt_signal_spy_callback_set.slot_end_callback != 0)
qt_signal_spy_callback_set.slot_end_callback(receiver, method);

QObjectPrivate::resetCurrentSender(receiver, &currentSender, previousSender);

if (connectionLists->orphaned)
break;
}

if (connectionLists->orphaned)
break;
}

--connectionLists->inUse;
Q_ASSERT(connectionLists->inUse >= 0);
if (connectionLists->orphaned) {
if (!connectionLists->inUse)
delete connectionLists;
} else {
sender->d_func()->cleanConnectionLists();
}

locker.unlock();

if (qt_signal_spy_callback_set.signal_end_callback != 0)
qt_signal_spy_callback_set.signal_end_callback(sender, from_signal_index);
}
单步跟踪,receiver->qt_metacall();实际调用的是QApplication::qt_metacall(),根据调用参数实现不同的函数调用。在moc_QApplication.cpp中定义,是由MOC工具自动产生的代码。至此,信号与槽的关联分析完毕。

明天接着分析QObject::connect()如何把相关数据填入connectionLists。自定义信号如何与windows消息关联在明天分析完毕之后再来证实。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
QT的重要的概念
Qt那点事儿(一)
【深入QT】信号槽机制浅析
用QtWebKit开发简单的浏览器
QObject ——QT所有类的基类
Porting to Qt 4 译文
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服