我们使用QT的UI控件时,常用到触发控件的操作。
比如点击一个按钮,就进行一个什么操作。ui文件可以右击控件–转到槽来生成一个槽函数,很方便。
选择槽函数后,头文件里会多一个槽函数。
这是QT的 QObject函数的信号与槽功能,使得控件的点击为一个信号,点击后触发槽函数进行操作。
我们自己也可以在QObject类里写上Q_OBJECT,signals和slots来实现信号槽。
它实现的是
观察者模式:通知依赖关系,一个对象目标的状态发生改变,所有依赖对象( 观察者对象)都将得到通知。
观察者模式通过面向对象技术,弱化关系,形成稳定依赖,实现软件体系结构的松耦合。
信号槽需要使用QT的MOC实现,需要在类里写Q_OBJECT宏才能实现。
moc工具会解析具有Q_OBJECT宏的类,生成对应的moc_xx.cpp文件,该文件会随着项目一起编译。
Qt程序在交由标准编译器(例如MSVC)编译之前,先使用moc(MOC, 元对象编译器 the Meta Object Compiler)分析cpp头文件;如果它发现在一个头文件中包含了Q_OBJECT宏,则会生成另外一个cpp源文件(moc_文件名.cpp),该cpp源文件中包含了Q_OBJECT宏的实现、运行时信息(反射)等。因此Qt程序的完整编译过程为moc->预处理->编译->链接
QObject和QMetaObject:
顾名思义,QMetaObject包含了QObject的所谓的元数据,也就是QObject信息的一些描述信息:除了类型信息外,还包含QT中特 有的signal&slot信息。
QObject::metaObject ()方法返回一个QObject对象对应的metaobject对象,注意这个方法是virtual方法。如上文所说,如果一个类的声明中包含了 Q_OBJECT宏,编译器会生成代码来实现这个类对应的QMetaObject类,并重载QObject::metaObject()方法来返回这个 QMetaObject类的实例引用。这样当通过QObject类型的引用调用metaObejct方法时,返回的是这个引用的所指的真实对象的 metaobject。
如果一个类从QObject派生,确没有声明Q_OBJECT宏,那么这个类的metaobject对象不会被生成,这样这个类所声明的 signal slot都不能使用,而这个类实例调用metaObject()返回的就是其父类的metaobject对象,这样导致的后果就是你从这个类实例获得的元 数据其实都是父类的数据,这显然给你的代码埋下隐患。因此如果一个类从QOBject派生,它都应该声明Q_OBJECT宏,不管这个类有没有定义 signal&slot和Property。
这样每个QObject类都有一个对应的QMetaObject类,形成一个平行的类型层次。
观察者模式定义:
定义对象间的一种一对多变化的依赖关系,以便当一个对象(subject)的状态发送变化时,所有依赖于它的对象都得到通知并自动更新。
动机:
在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系” ----一个对象(目标对象)的状态发生改变,所以的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。
使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。
目的都是去耦合。信号槽是将这种模式封装好了,方便我们调用。实现了 对接口编程而不是对实现编程.从抽象类QObject类继承的类都共享其接口(signals slots).
我们定义一个发送信号的sender发送者
发送信号operate(),
和一个接收槽函数的接收者receiver
信号 :当按钮(对象)改变状态时,信号就emit,对象只负责发送,不负责接收方的处理。
槽: 接收到信号,不关心信号。
信号与槽如何连接:
原来QT是通过QObject::connect静态函数建立连接;其中sender与receiver是指向对象的指针,分别代表了被观察者和 观察者。
signal与method分别通过SIGNAL()与SLOT()宏来进行转换 (类型是const char*)
他们通过 connect(this, &myclass::operate, worker, &Worker::doWork);绑定信号和槽函数。
QObject类型有 signals slots Q_OBJECT emit SIGNAL SLOT 等信号槽相关的宏定义。
CTRL键+鼠标点击这些宏,进入qobjecdefs.h文件,这里定义了这些宏。
首先 emit 信号 发送了什么?(注:emit是个空的宏)
moc_xxx.cpp文件中看到
发送者的信号函数里有 QMetaObject::activate
// SIGNAL 0
void myclass::operate(const QString & _t1)
{
void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
QMetaObject::activate(this, &staticMetaObject, 0, _a);
}
其定义在
qobjectdefs.h文件里
// internal index-based signal activation
static void activate(QObject *sender, int signal_index, void **argv);
static void activate(QObject *sender, const QMetaObject *, int local_signal_index, void **argv);
static void activate(QObject *sender, int signal_offset, int local_signal_index, void **argv);
**槽函数呢 **在moc_xxx文件里 找到槽函数相关的函数 后面会讲如何和信号绑定 的如何触发 的。
void Worker::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
auto *_t = static_cast<Worker *>(_o);
Q_UNUSED(_t)
switch (_id) {
case 0: _t->resultReady((*reinterpret_cast< const QString(*)>(_a[1]))); break;
case 1: _t->doWork((*reinterpret_cast< const QString(*)>(_a[1]))); break;
default: ;
}
} else if (_c == QMetaObject::IndexOfMethod) {
int *result = reinterpret_cast<int *>(_a[0]);
{
using _t = void (Worker::*)(const QString & );
if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&Worker::resultReady)) {
*result = 0;
return;
}
}
}
}
发送者和接收者都继承QObject类,
重写了QObject::qt_metacall 调用了qt_static_metacall()
qt_static_metacall会将槽函数调用
int Worker::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;
} else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
if (_id < 2)
*reinterpret_cast<int*>(_a[0]) = -1;
_id -= 2;
}
return _id;
}
QObject里有 静态成员变量
static const QMetaObject staticQtMetaObject;
moc文件里看出来预处理对应的worker 的QMetaObject 的成员变量是
QT_INIT_METAOBJECT const QMetaObject Worker::**staticMetaObject** = { {
&QObject::staticMetaObject,
qt_meta_stringdata_Worker.data,
qt_meta_data_Worker,
qt_static_metacall,
nullptr,
nullptr
} };
其中成员 qt_meta_stringdata_Worker.data,为
static const qt_meta_stringdata_Worker_t qt_meta_stringdata_Worker = {
{
QT_MOC_LITERAL(0, 0, 6), // "Worker"
QT_MOC_LITERAL(1, 7, 11), // "resultReady"
QT_MOC_LITERAL(2, 19, 0), // ""
QT_MOC_LITERAL(3, 20, 7), // "result1"
QT_MOC_LITERAL(4, 28, 6), // "doWork"
QT_MOC_LITERAL(5, 35, 9) // "parameter"
},
"Worker\0resultReady\0\0result1\0doWork\0"
"parameter"
};
可以看出是Worker类的名称、信号名称、槽函数名称及参数等字符串。
connect有什么呢?
点击进去
connect函数是QObject类的成员函数 返回值是QMetaObject::Connection
static QMetaObject::Connection connect(const QObject *sender, const char *signal,
const QObject *receiver, const char *member, Qt::ConnectionType = Qt::AutoConnection);
static QMetaObject::Connection connect(const QObject *sender, const QMetaMethod &signal,
const QObject *receiver, const QMetaMethod &method,
Qt::ConnectionType type = Qt::AutoConnection);
inline QMetaObject::Connection connect(const QObject *sender, const char *signal,
const char *member, Qt::ConnectionType type = Qt::AutoConnection) const;
所以QObject 里实现connect函数
QMetaObject 实现Connection 类是connect的返回类型
class Q_CORE_EXPORT QMetaObject::Connection {
void *d_ptr; //QObjectPrivate::Connection*
explicit Connection(void *data) : d_ptr(data) { }
friend class QObject;
friend class QObjectPrivate;
friend struct QMetaObject;
bool isConnected_helper() const;
public:
~Connection();
Connection();
Connection(const Connection &other);
Connection &operator=(const Connection &other);
#ifdef Q_QDOC
operator bool() const;
#else
typedef void *Connection::*RestrictedBool;
operator RestrictedBool() const { return d_ptr && isConnected_helper() ? &Connection::d_ptr : nullptr; }
#endif
Connection(Connection &&o) Q_DECL_NOTHROW : d_ptr(o.d_ptr) { o.d_ptr = nullptr; }
Connection &operator=(Connection &&other) Q_DECL_NOTHROW
{ qSwap(d_ptr, other.d_ptr); return *this; }
};
实现及架构–看这里----->