版权声明
请尊重原创作品。转载请保持文章完整性,并以超链接形式注明原始作者“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 为了解决 C++ 静态类型的限制而设计的一种机制,它允许在运行时动态地添加和修改对象的属性,以及实现对象之间的通信。 1. **元对象系统**: - Qt 的元对象系统基于 C++ 编译器的元数据(metadata...
本文将对Qt中的信号与槽机制进行深入探讨,包括其基本概念、工作原理以及应用场景等。 #### 二、信号与槽的基本概念 在图形界面编程中,常常需要解决一个问题:如何在不同的对象之间传递信息,以便这些对象能够...
QT的元对象系统(Meta Object System)是实现信号与槽机制的关键。元对象编译器(moc)是一个预处理程序,它为QT类生成额外的代码,使信号和槽的连接成为可能。moc处理QT类的头文件,生成必要的C++代码,使得对象...
**QT实现信号与槽机制详解** ...这就是Qt信号与槽机制的基本用法,它极大地简化了对象间的交互和事件处理。在实际项目中,开发者可以利用这一机制构建复杂的交互逻辑,提高代码的可读性和可维护性。
Qt信号与槽机制是 Qt 编程的核心机制,用于对象之间的通信。信号和槽是一种高级接口,应用于对象之间的通信。信号机制不同于传统的回调函数机制,它提供了一种高效、灵活的方式来实现对象之间的交互。 信号与槽...
在QT中,信号和槽机制是其核心特性之一,它使得对象间的通信变得简单而直观。本示例将探讨如何在QT中通过信号槽传递自定义结构体,以此实现更复杂的对象间数据交换。 标题"QT信号槽传递自定义结构体示例"中提到的...
你可以将很多信号与单个的槽进行连接,也可以将单个的信号与很多的槽进行连接,甚至于将一个信号与另外一个信号相连接也是可能的,这时无论第一个信号什么时候发射系统都将立刻发射第二个信号。 信号的声明是在...
Qt 信号和槽机制 Qt 信号和槽机制是 Qt 的核心机制,要精通 Qt 编程就必须对信号和槽有所...信号和槽机制是 Qt 的核心机制,它提供了一种高级的通信机制,实现了对象之间的松散耦合,提高了软件的可维护性和可扩展性。
3. **信号与槽机制**:这是QT的一大特色,它允许对象间进行通信而无需显式调用,提高了代码的可读性和可维护性。 4. **模型-视图-控制器(MVC)架构**:QT的GUI设计遵循MVC模式,分离了数据、显示和控制逻辑,便于...
这里我们将深入探讨如何在Qt中实现静态单例以及如何利用它来管理信号和槽。 首先,理解单例模式的概念至关重要。单例模式是一种创建型设计模式,它保证一个类只有一个实例,并提供一个全局访问点。在Qt中,我们通常...
为了实现一个完整的DXF解析器,你需要对DXF文件格式有深入理解,同时熟悉Qt的图形系统和C++编程。这个源码项目提供了一个起点,你可以在此基础上扩展功能,如支持更复杂的图元,优化性能,或者增加对3D元素的支持。 ...
QT框架中的信号槽机制是其核心特性之一,用于实现对象间的通信。在QT中,信号和槽通过一种安全、类型安全的方式连接,使得当信号触发时,对应的槽函数会被自动调用。在深入理解QT原理时,特别是信号槽的连接存储结构...
在深入探讨Qt的对象模型与信号槽的概念之前,我们首先需要了解Qt库的基础。Qt是一个跨平台的应用程序开发框架,主要用于C++编程,它提供了一整套工具、库和API,用于创建用户界面和其他软件组件。诺基亚是Qt的原始...
除此之外,Qt5还支持信号和槽机制,这是一种事件驱动的编程模式,用于对象间的通信。在汽车管理系统中,例如,当用户点击“保存”按钮时,槽函数会被触发,执行相应的数据保存操作。同时,Qt的多线程支持(如QThread...
信号和槽机制是Qt的核心机制之一,要掌握Qt编程就需要对信号和槽有所了解。信号和槽是一种高级接口,它们被应用于对象之间的通信,它们是Qt的核心特性,也是Qt不同于其它同类工具包的重要地方之一。在我们所了解的...
本文将深入探讨在Qt信号槽中不同参数传递方式——值传递和引用传递的区别,以及如何在实际应用中选择合适的传递方式。 首先,我们要理解信号和槽的基本概念。信号是对象在特定事件发生时发出的通知,而槽则是响应...