版权声明
请尊重原创作品。转载请保持文章完整性,并以超链接形式注明原始作者“tingsking18”和主站点地址,方便其他朋友提问和指正。
QT源码解析(一) QT创建窗口程序、消息循环和WinMain函数
QT源码解析(二)深入剖析QT元对象系统和信号槽机制
QT源码解析(三)深入剖析QT元对象系统和信号槽机制(续)
QT源码解析(四)剖析Qt的事件机制原理
QT源码解析(五)QLibrary跨平台调用动态库的实现
QT源码解析(六)Qt信号槽机制与事件机制的联系
QT源码解析(七)Qt创建窗体的过程
QT源码解析(八)Qt是如何处理windows消息的
QT源码解析(九)解析QDateTime
QT的信号和槽机制是用来在对象间通讯的方法,当一个特定事件发生的时候,signal会被 emit 出来,slot 调用是用来响应相应的 signal 的。简单点说就是如何在一个类的一个函数中触发另一个类的另一个函数调用,而且还要把相关的参数传递过去.好像这和回调函数也有点关系,但是消息机制可比回调函数有用多了,也复杂多了。
下面的代码是我写的一个继承QLabel的类,是QLabel可以响应鼠标单击的消息。
这段代码很简单,讲述了QT的singal和slot的使用。下面我们就深入QT的源码内部,来看一看QT是如何实现singal和slots的。
#include “main.moc” 的意思就是使编译器找到moc对Q_OBJECT处理后的标准C++文件。编译的时候我们需要首先在该目录中使用 qmake -project 生成一个 .pro 文件,该文件含有工程细节,然后使用 qmake 产生 Makefile,最后 nmake 就可以产生可执行文件了。我们看看在nmake之后除了生成目标代码和可执行文件之外,还有一个main.moc文件,这个文件是moc产生的一个中间文件。
现在我们要看一下Q_OBJECT宏到底是什么?他与main.moc有什么关联呢?相信我介绍完了Q_OBJECT宏之后,再看main.moc就能明白其所有函数的含义了。我们先到objectdefs.h 文件中看一下Q_OBJECT宏的定义:
#define Q_OBJECT \
public: \
Q_OBJECT_CHECK \
static const QMetaObject staticMetaObject; \
virtual const QMetaObject *metaObject() const; \
virtual void *qt_metacast(const char *); \
QT_TR_FUNCTIONS \
virtual int qt_metacall(QMetaObject::Call, int, void **); \
private:
1首先调用了 Q_OBJECT_CHECK (插入了一个 qt_check_for_QOBJECT_macro 的 template function)
2 然后是全局常量 QMetaObject 对象,因此可以用 QClassname::staticMetaObject 直接访问,另外提供了两个接口函数 metaObject() 用于不同的 class 返回自己的 staticMetaObject、qt_metacast() 用于转换,我们在 moc 产生的文件里面可以找到这两个接口的实现:
const QMetaObject *ClickedLabel::metaObject() const
{
return &staticMetaObject;
}
void *ClickedLabel::qt_metacast(const char *_clname)
{
if (!_clname) return 0;
if (!strcmp(_clname, qt_meta_stringdata_ClickedLabel))
return static_cast<void*>(const_cast< ClickedLabel*>(this));
return QLabel::qt_metacast(_clname);
}
3 宏QT_TR_FUNCTIONS是和i18n相关的,我们暂时不用去管它。
# define QT_TR_FUNCTIONS \
static inline QString tr(const char *s, const char *c = 0) \
{ return staticMetaObject.tr(s, c); }
4 最后是接口函数qt_metacall,他的作用是查表,调用函数
int ClickedLabel::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
_id = QLabel::qt_metacall(_c, _id, _a);
if (_id < 0)
return _id;
if (_c == QMetaObject::InvokeMetaMethod) {
switch (_id) {
case 0: Clicked((*reinterpret_cast< ClickedLabel*(*)>(_a[1]))); break;
case 1: OnCLicked((*reinterpret_cast< ClickedLabel*(*)>(_a[1]))); break;
}
_id -= 2;
}
return _id;
}
我们来仔细看看 QMetaObject,这就是meta-object的数据结构定义
struct Q_CORE_EXPORT QMetaObject
{
const char *className() const;
const QMetaObject *superClass() const;
QObject *cast(QObject *obj) const;
// ...
struct { // private data
const QMetaObject *superdata;
const char *stringdata;
const uint *data;
const void *extradata;
} d;
} ;
下面看看我们生成的具体的代码:
static const uint qt_meta_data_ClickedLabel[] = {
// content:
1, // revision
0, // classname
0, 0, // classinfo
2, 10, // methods
0, 0, // properties
0, 0, // enums/sets
// signals: signature, parameters, type, tag, flags
22, 14, 13, 13, 0x05,
// slots: signature, parameters, type, tag, flags
45, 13, 13, 13, 0x0a,
0 // eod
};
static const char qt_meta_stringdata_ClickedLabel[] = {
"ClickedLabel\0\0clicked\0Clicked(ClickedLabel*)\0"
"OnCLicked(ClickedLabel*)\0"
};
const QMetaObject ClickedLabel::staticMetaObject = {
{ &QLabel::staticMetaObject, qt_meta_stringdata_ClickedLabel,
qt_meta_data_ClickedLabel, 0 }
};
这就是meta-object的初始化代码,meta-object包含所有继承QObject类的元对象信息。包括class name, superclass name, properties, signals and slots等等。
ClickedLabel的staticMetaObject初始化用到了QLabel::staticMetaObject,
qt_meta_stringdata_ClickedLabel是元数据的签名
qt_meta_data_ClickedLabel,是元数据的索引数组指针。
qt_meta_data_ClickedLabel中这些莫名其妙的数字是如何变成QMetaObject的呢?
在qmetaobject.cpp中我们找到了QMetaObjectPrivate的定义:
struct QMetaObjectPrivate
{
int revision;
int className;
int classInfoCount, classInfoData;
int methodCount, methodData;
int propertyCount, propertyData;
int enumeratorCount, enumeratorData;
};
很明显,利用qt_meta_data_ClickedLabel中存储的索引和qt_meta_stringdata_ClickedLabel中存储的值,我们很容易将QMetaObject构建起来。这中间的转换是通过
static inline const QMetaObjectPrivate *priv(const uint* data)
{ return reinterpret_cast<const QMetaObjectPrivate*>(data); }
这个函数来完成的。
下面我们着重看看几个与 signal/slot 相关的代码
qobject.cpp 文件中关于 QObject::connect() 函数的代码,
bool QObject::connect(const QObject *sender, const char *signal,
const QObject *receiver, const char *method,
Qt::ConnectionType type)
{
{
const void *cbdata[] = { sender, signal, receiver, method, &type };
if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata))
return true;
}
#ifndef QT_NO_DEBUG
bool warnCompat = true;
#endif
if (type == Qt::AutoCompatConnection) {
type = Qt::AutoConnection;
#ifndef QT_NO_DEBUG
warnCompat = false;
#endif
}
//判断是否是NULL
if (sender == 0 || receiver == 0 || signal == 0 || method == 0) {
qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",
sender ? sender->metaObject()->className() : "(null)",
(signal && *signal) ? signal+1 : "(null)",
receiver ? receiver->metaObject()->className() : "(null)",
(method && *method) ? method+1 : "(null)");
return false;
}
QByteArray tmp_signal_name;
if (!check_signal_macro(sender, signal, "connect", "bind"))
return false;
const QMetaObject *smeta = sender->metaObject();
++signal; //skip code
int signal_index = smeta->indexOfSignal(signal);
if (signal_index < 0) {
// check for normalized signatures
tmp_signal_name = QMetaObject::normalizedSignature(signal).prepend(*(signal - 1));
signal = tmp_signal_name.constData() + 1;
signal_index = smeta->indexOfSignal(signal);
if (signal_index < 0) {
err_method_notfound(QSIGNAL_CODE, sender, signal, "connect");
err_info_about_objects("connect", sender, receiver);
return false;
}
}
QByteArray tmp_method_name;
int membcode = method[0] - '0';
if (!check_method_code(membcode, receiver, method, "connect"))
return false;
++method; // skip code
const QMetaObject *rmeta = receiver->metaObject();
int method_index = -1;
switch (membcode) {
case QSLOT_CODE:
method_index = rmeta->indexOfSlot(method);
break;
case QSIGNAL_CODE:
method_index = rmeta->indexOfSignal(method);
break;
}
if (method_index < 0) {
// check for normalized methods
tmp_method_name = QMetaObject::normalizedSignature(method);
method = tmp_method_name.constData();
switch (membcode) {
case QSLOT_CODE:
method_index = rmeta->indexOfSlot(method);
break;
case QSIGNAL_CODE:
method_index = rmeta->indexOfSignal(method);
break;
}
}
if (method_index < 0) {
err_method_notfound(membcode, receiver, method, "connect");
err_info_about_objects("connect", sender, receiver);
return false;
}
if (!QMetaObject::checkConnectArgs(signal, method)) {
qWarning("QObject::connect: Incompatible sender/receiver arguments"
"\n\t%s::%s --> %s::%s",
sender->metaObject()->className(), signal,
receiver->metaObject()->className(), method);
return false;
}
int *types = 0;
if ((type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
&& !(types = queuedConnectionTypes(smeta->method(signal_index).parameterTypes())))
return false;
#ifndef QT_NO_DEBUG
{
QMetaMethod smethod = smeta->method(signal_index);
QMetaMethod rmethod = rmeta->method(method_index);
if (warnCompat) {
if(smethod.attributes() & QMetaMethod::Compatibility) {
if (!(rmethod.attributes() & QMetaMethod::Compatibility))
qWarning("QObject::connect: Connecting from COMPAT signal (%s::%s)", smeta->className(), signal);
} else if(rmethod.attributes() & QMetaMethod::Compatibility && membcode != QSIGNAL_CODE) {
qWarning("QObject::connect: Connecting from %s::%s to COMPAT slot (%s::%s)",
smeta->className(), signal, rmeta->className(), method);
}
}
}
#endif
QMetaObject::connect(sender, signal_index, receiver, method_index, type, types);
const_cast<QObject*>(sender)->connectNotify(signal - 1);
return true;
}
上面这段代码首先调用了 QInternal 这个 namespace 里面 activateCallbacks 这个函数,然后根据 QMetaObject 信息检查了 sender、receiver 以及对应 signal/slots 的匹配性,得到元数据类。把 signal/slot 字符串转换成为了对应的 index,然后检查信号和槽的参
分享到:
相关推荐
源码中大量使用了Qt的信号和槽机制,以及Windows API,如`SHObjIDL`库来实现与Windows系统功能的交互。 开发者在使用Qt WinExtras时,需要包含对应的头文件并链接相应的库。例如,如果要在项目中使用...
1. 信号与槽机制:Qt5的核心特性之一,用于对象间的通信。当一个对象发出信号时,其他对象可以通过槽函数进行响应。 2. 对象树:Qt中的对象可以形成一棵树状结构,父对象可以管理其子对象的生命周期。 3. 数据模型...
1. Qt4的事件驱动模型和信号槽机制。 2. 多线程编程在并发处理中的应用。 3. RTP/RTSP/SIP协议的工作流程和实现细节。 4. 实时音视频编解码原理及其实现库的使用。 5. 网络编程,特别是TCP/IP和UDP协议的运用。 6. ...
本学习文集聚焦于嵌入式技术中的核心部分——Linux内核与Qt框架,通过深入剖析Linux内核以及利用Qt进行图形用户界面设计,帮助读者构建坚实的嵌入式开发基础。 《Linux内核分析及编程(倪继利).pdf》这本书可能涵盖...
QT的信号与槽机制使得事件处理更加灵活,增强了程序的可维护性和扩展性。 "CodeCounter"作为一个完整的工程,包含了编译和运行所需的所有文件。用户可以直接下载并编译源码,体验其功能。编译过程一般包括配置QT...
本文将深入剖析一个名为“ruborz-VideoPlayer-master.zip”的开源Qt播放器项目,帮助开发者了解如何利用Qt库创建一个功能完备的视频播放器。 Qt是一个跨平台的C++图形用户界面应用程序开发框架,广泛应用于桌面、...
- **核心库功能**:解析Qt核心库的功能,包括信号槽机制、事件处理、布局管理等。 - **QtopiaCore环境**:探讨QtopiaCore这一基于Qt的嵌入式GUI框架,以及其在嵌入式Linux系统中的应用。 #### 2. QtopiaCore开发...
在learnOSG项目中,这部分内容可能包含如何在QT界面上嵌入OSG视窗,以及如何利用QT的信号与槽机制实现3D视图与用户交互。这将帮助开发者理解如何在实际项目中整合2D UI与3D视图,提高用户体验。 在学习过程中,理解...
Qt库包含丰富的控件、信号和槽机制、模型视图架构等,使得开发者可以轻松地构建出直观、用户友好的界面,用户可以通过简单的操作进行磁盘分析。 在DiskAnalyzer中,C++和Qt的结合使用使得程序能高效地遍历文件系统...