In the previous article, I translated an article on the signal and slot mechanism to explain the QT signal and slot working mechanism-translation. In this article, I will analyze the mechanism from QT source according to my own understanding. It is suggested to look at the previous translation before reading this article or to see how Qt signals and slots Work directly from the original text. This article only analyzed the approximate idea, many details did not delve further, if has the mistake, asks everybody to correct me.
The test for all of the following code is based on the QT5.4.0,MOC version 67. Test Code
Again, we use the official routines to explain.
Counter.h
#ifndef counter_h
#define COUNTER_H
#include <QObject>
#include <QDebug>
class COUNTER: Public QObject
{
q_object
int m_value;
Public:
int value () const {return m_value;}
Public slots:
void setValue (int value);
Signals:
void valuechanged (int newvalue);
Public:
Counter ();
~counter ();
};
#endif//Counter_h
Counter.c
#include "Counter.h"
counter::counter ()
{
m_value = 0;
}
void Counter::setvalue (int value)
{
if (value!= m_value)
{
m_value = value;
Emit valuechanged (value);
}
}
Counter::~counter ()
{
}
Main.c
#include "counter.h"
int main ()
{
counter A, B;
Qobject::connect (&a, SIGNAL (valuechanged (int)), &b, SLOT (int));
Qdebug () << "First:b.value () is" << b.value ();
A.setvalue (a);
Qdebug () << "Second:b.value () is" << b.value ();
return 0;
}
In the debug directory, we found the C + + files produced by MOC according to Counter.h: Moc_counter.cpp
/**************************************************************************** * * Meta object code from reading C + + File ' counter.h ' * * * * * Created by:the qt Meta Object Compiler version (QT 5.4.0) * * * warning!
All changes made in this file would be lost! /#include ". /.. /test/counter.h "#include <QtCore/qbytearray.h> #include <QtCore/qmetatype.h> #if!defined (q_moc_output_ REVISION) #error "The header file ' counter.h ' doesn ' t include <qobject>." #elif q_moc_output_revision!= #error " This file is generated using the MOC from 5.4.0. It "#error" cannot is used with the include the "#error" (the MOC has changed too) "#endi
F qt_begin_moc_namespace struct qt_meta_stringdata_counter_t {qbytearraydata data[6];
Char stringdata[46];
}; #define QT_MOC_LITERAL (idx, OFS, len) \ Q_static_byte_array_data_header_initializer_with_offset (len, QPTRDIFF (Offsetof (qt_meta_stringdata_counter_t, stringdata) + ofs \ idx * sizeof (qbytearraydata)) \) Static Cons T qt_meta_stringdata_counter_t qt_meta_stringdata_counter = {{qt_moc_literal (0, 0, 7),//"Counter" qt_moc_literal (1 , 8,,//"ValueChanged" qt_moc_literal (2, 0),//"" Qt_moc_literal (3, 8),//"NewValue" qt_moc_literal (4, 31, 8),//"SetValue" Qt_moc_literal (5, 5)//"Value"}, "Counter\0valuechanged\0\0newvalue\0setvalue\0" "Val
UE "}; #undef qt_moc_literal static const UINT Qt_meta_data_counter[] = {//Content:7,//revision 0, ClassName 0, 0,//ClassInfo 2,///methods 0, 0,//Properties 0, 0,// Enums/sets 0, 0,//constructors 0,//flags 1,//Signalcount//Signals:name, ARG
c, parameters, tag, flags 1, 1, 2, 0x06/* Public * *,//Slots:name, argc, parameters, tag, flags 4, 1, 27, 2, 0x0a/* public */,//Signals:parameters qmetatype::void, Qmetatype::int, 3,//Slots:parameters
Qmetatype::void, Qmetatype::int, 5, 0//EOD}; void Counter::qt_static_metacall (QObject *_o, qmetaobject::call _c, int _id, void **_a) {if (_c = = Qmetaobject::invok
Emetamethod) {Counter *_t = Static_cast<counter *> (_o);
Switch (_id) {case 0: _t->valuechanged (*reinterpret_cast< int (*) > (_a[1))); Case 1: _t->setvalue ((*reinterpret_cast< int (*) > (_a[1)));
Break
Default:;
} else if (_c = = qmetaobject::indexofmethod) {int *result = Reinterpret_cast<int *> (_a[0));
void **func = Reinterpret_cast<void **> (_a[1));
{typedef void (Counter::* _t) (int); if (*reinterpret_cast<_t *> (func) = = static_cast<_t> (&counter::valuechanged)) {*result =
0;
}
}}} const Qmetaobject Counter::staticmetaobject = {{&qobject::staticmetaobject, qt_meta_stringdata_counter.data
, Qt_meta_data_counter, Qt_static_metacall, Q_nullptr, q_nullptr}; Const Qmetaobject *counter::metaobject () const {return QObject::d _ptr->metaobject?
QObject::d _ptr->dynamicmetaobject (): &staticMetaObject;
} void *counter::qt_metacast (const char *_clname) {if (!_clname) return q_nullptr; if (!strcmp (_clname, Qt_meta_stringdata_counter.stringdata)) return static_cast<void*> (const_cast< Counte
R*> (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;
else if (_c = = Qmetaobject::registermethodargumentmetatype) { if (_id < 2) *reinterpret_cast<int*> (_a[0]) =-1;
_id-= 2;
return _id; }//SIGNAL 0 void counter::valuechanged (int _t1) {void *_a[] = {q_nullptr, const_cast<void*> (Reinterpret_cas
T<const void*> (&_T1))};
Qmetaobject::activate (this, &staticmetaobject, 0, _a); } qt_end_moc_namespace
The results of the above operation are as follows:
Main () function
In main (), we first create two objects of Counter type A and b:counter a, b;.
Then we bind the signal valuechanged (int) in object A to the slot function SetValue (int) in object B: Qobject::connect (&a, SIGNAL (valuechanged (int)), & B, SLOT (setValue (int)));
Finally, the slot function of the A object is called: A.setvalue (12); After the function is called, the value of M_value in object B becomes 12 from the original 0. Obviously, our signal is connected to the slot successfully. SetValue () function
void Counter::setvalue (int value)
{
if (value!= m_value)
{
m_value = value;
Emit valuechanged (value);
}
}
In this function, we first judge whether the incoming parameter is unequal to the property variable m_value of the Counter class, because if the two equals or the emit signal, the connection between the signal and the slot will go into the dead loop and never end. finally emit a signal valuechanged (value). The emit macro was defined in Qobject.h:
Qobjectdefs.h
# define emit/Nothing
that is, the valuechanged () function is simply called in the SetValue () function. signal function: valuechanged ()
We have only declared a signal function in Counter.h:
Signals:
void valuechanged (int newvalue);
But we are not to achieve it, then how it is achieved. This time it will reflect the strength of the MOC. As we mentioned before:MoC is saved in moc_counter.cpp file by Counter.h function generation code . We found the implementation code of the signal function valuechanged () in the C + + file:
SIGNAL 0
void counter::valuechanged (int _t1)
{
void *_a[] = {q_nullptr, const_cast<void*> ( Reinterpret_cast<const void*> (&_T1))};
Qmetaobject::activate (this, &staticmetaobject, 0, _a);
The reinterpret_cast operator is used to handle conversions between unrelated types, and it produces a new value that has exactly the same bit size as the original parameter. reinterpret_cast<const void*> (&_t1) means to convert a pointer to an integral type _t1 to a const void*.
Const_cast is a const or volatile qualifier used to overflow a variable. const_cast<void*> (Reinterpret_cast<const void*> (&_T1)) converts a pointer to an integral _t1 to void* as I understand it. We just keep in mind that here the value of _a[1] is a pointer to the parameter of the signal function.
The Qmetaobject::activate () is invoked and the parameters are passed in. activate () parameter: Staticmetaobject
Or in the Moc_counter.cpp file, we found the initialization data for the Staticmetaobject object, which is the Qmetaobject type. Staticmetaobject contains some very important information in the counter class and needs to be paid more attention to.
Const Qmetaobject Counter::staticmetaobject = {
{&qobject::staticmetaobject, Qt_meta_stringdata_ Counter.data,
Qt_meta_data_counter, qt_static_metacall, Q_nullptr, q_nullptr}
;
Const Qmetaobject *counter::metaobject () const
{return
QObject::d _ptr->metaobject? QObject::d _ptr->dynamicmetaobject (): &staticMetaObject;
}
We found the Qmetaobject constructor in the qobjectdefs.h.
struct {//private data
const qmetaobject *superdata;
Const Qbytearraydata *stringdata;
const UINT *data;
typedef void (*staticmetacallfunction) (QObject *, qmetaobject::call, int, void * *);
Staticmetacallfunction Static_metacall;
Const QMETAOBJECT * Const *relatedmetaobjects;
void *extradata; Reserved for future use
} D;
So we can get the following equation relationship:
1, SuperData = &QObject::staticMetaObject;
2, stringdata = Qt_meta_stringdata_counter.data;
3, data = Qt_meta_data_counter;
4, Static_metacall = Qt_static_metacall;
5, relatedmetaobjects = q_nullptr;
6, extradata = q_nullptr;
2, 3, 4 points of information we will use later. qmetaobject::activate () function
Next we look at the qmetaobject::activate (this, &staticmetaobject, 0, _a) of the last call in the Signal function valuechanged ().
Qmetaobject::activate () is defined in the Qobject.cpp file.
/*!
\internal
*
/void Qmetaobject::activate (QObject *sender, const qmetaobject *m, int local_signal_index,
void **argv)
{
Activate (sender, Qmetaobjectprivate::signaloffset (m), Local_signal_index, argv);
/*!
\internal
*
/void Qmetaobject::activate (QObject *sender, int signaloffset, int local_signal_index, void * * * argv)
{
//detailed explanation see the article mentioned at the beginning of the article
}
The Activate () function receives 4 parameters:
The 1th parameter represents the object that sends the signal, which of course refers to object A;
The 2nd argument is a pointer to the Qmetaobject type, where we pass in the &staticmetaobject, which contains a lot of important information that we've already talked about earlier.
The 3rd parameter is the index value of the signal in this class. Because we define only one signal valuechanged () in the Counter class, the index value of the signal is 0. So if we define multiple signals, the index value of the signal is one by one corresponding to the order in which the signal definition appears.
The 4th parameter is to receive a pointer to the pointer, which passes a pointer to the array _a, we also said earlierthe value of _a[1] refers to the parameter newvalue passed to the signal function valuechanged (), and it is obvious that the value passed to NewValue is 12. 。
Overloaded functions for Qmetaobject::activate (): void Qmetaobject::activate (QObject *sender, int signaloffset, int local_signal_index, void **ARGV) differs from the previous version only in the 2nd argument, where the 2nd parameter passes the signal's offset qmetaobjectprivate::signaloffset (m) instead of the pointer m to the Qmetaobject object. I have not found the relevant definition of qmetaobjectprivate::signaloffset (), where the function is understood by its function name:gets the offset of the signal from the class. That is, the offset of the signal is found in the Qt_meta_data_counter array. Regardless of which version of the Activate () function is invoked, you should tell it (pass parameters to it) the following information:
1, send the object sender;
2, the signal in all signals of the offset signaloffset;
each of the signals in each object maintains a two-way list of connected Connectionlist (mentioned later), which contains information about the connection to the signal, such as receiving objects, receiving slot functions, parameters, and so on. If you find the offset of the signal, you can find a two-way list of its maintenance, and you can find the slot function.
3, the index value of the signal in the class is local_signal_index;
4, the signal carries the parameter _a[1].
Here we talk about the process of emit a signal, but only a little bit of the signal and slot mechanism is mentioned. Why is there a slot function response when emit a signal? How the signal is bound to the slot. What information is bound to it.Qobject::connect ()
In the main () function, we call Qobject::connect (&a, SIGNAL (valuechanged (int)), &b, SLOT (int)), and bind the signal to the slot. Before we analyze the Connect function, let's take a look at what the macro signal () and slot () are all ghosts. macros signal and slot
We found the definitions of the two macros in the qobjectdefs.h:
#ifndef qt_no_meta_macros
#ifndef qt_no_debug
# define Qlocation "__file__": Qt_stringify (__line__)
# ifndef qt_no_keywords
# define Method (a) qflaglocation ("0" #a qlocation)
# endif
# define SLOT (a) Qflaglocation ("1" #a qlocation)
# define SIGNAL (a) qflaglocation ("2" #a qlocation)
#else
# ifndef Qt_no_keywords
# define Method (a) "0" #a
# endif
# define SLOT (a) "1" #a
# define SIGNAL (a) "2" #a
#endif
The two macros are nothing more than converting the names of the signals and slots functions (function names and arguments) to strings. In front of the method name give an ID 0, in front of the slot function to an ID 1, in front of the signal function to identify 2, which is the 3 identification of the following parsing parameter is the type of the role of a great effect. are we here to speculate that this is one of the reasons why the function names of many programming languages cannot begin with numbers? Also, we notice that the macro here converts the name of the signal and slot to a string, where the name includes the parameter. So we can conclude that the parameters of the signal and slot cannot include the macro. If you include macros, you have to wait for the macro to expand and then slot and signal to expand or first slot and signal unfold. None of this is defined. Qt4.8 version of the previous connect ()
Good excited, Big Boss appeared, Seconds of it ...
We are here to use the old Connect () syntax before the Qt4.8 version, and if you want to understand the explanation of the syntax above Qt5.0 version, you can read this article: Signals and slots in QT5.
Let's take a look at this function introduction (in the Qobject.cpp file):
Threadsafe creates a connection of the given \a type from the \a signal in the \a the object to the sender method in th
e \a Receiver object.
Returns a handle to the connection that can is used to disconnect it later. You are must use the \c SIGNAL () and \c SLOT () macros when specifying the \a SIGNAL and the \a method, for example: \snippet C Ode/src_corelib_kernel_qobject.cpp This example ensures the label always displays the current scroll bar value . Note this signal and slots parameters must not contain any variable names, only the type. e.g. the following would not work and return false: \snippet Code/src_corelib_kernel_qobject.cpp-A signal can also be C Onnected to another signal: \snippet code/src_corelib_kernel_qobject.cpp On This example, the \c mywidget
or relays a signal from private member variable, and makes it available under a name of relates to \c. A signal can be connected to many slots and signals. Many signals can be Connected to one slot. If a signal is connected to several slots, the slots are the activated in the same order in which the connections were the made, W
Hen the signal is emitted. The function returns a qmetaobject::connection that represents a handle to a Connection if it successfully connects the SI Gnal to the slot. The connection handle would be invalid if it cannot create the connection, for example, if QObject are unable to verify the
Existence of either \a signal or \a method, or if their signatures. Aren ' t compatible.
You can check if the handle are valid by casting it to a bool.
By default, a signal are emitted for every connection your make; Two signals are emitted for duplicate connections.
You can break all of this connections with a single disconnect () call. If you pass the Qt::uniqueconnection \a type, the connection would only be made if it was not a duplicate. If there is already a duplicate (exact same signal to the exact same on the slot same), the objects Would fail and connect would return to an invalid qmetaobject::connection. The optional \a type parameter describes the type of connection to establish. In particular, it determines whether a particular signal are delivered to a slot immediately or queued for delivery at a la ter time. If the signal is queued, the parameters must are of types that are known to QT's meta-object system, because Qt needs to co PY the arguments to store them in a event behind the scenes. If you try to use a queued connection and get the error message \snippet Code/src_corelib_kernel_qobject.cpp call Qregi
Stermetatype () To register the data type before you establish the connection.
\sa disconnect (), sender (), Qregistermetatype (), Q_declare_metatype ()
The above introduction sums up the following points:
1. The return value is a handle to the connection that deletes the connection and indicates whether the connection was successful or not;
2, this version of Connect () must use signal () and SLOT () macros to specify signals and slots;
3, a signal can be connected with other signals;
4, a signal can be connected with many slot functions or signals, multiple signals can be connected with a slot function;
5. When a signal is connected with a plurality of slot functions, the response of the slot function is in the order of Connect;
6. Optional parameters are used to specify that the slot function is an immediate response signal or a later response.
Next we want to see the true face of Connect (), we call the main.c inside
Qobject::connect (&a, SIGNAL (valuechanged (int)), &b, SLOT (int));
We expand the macros signal () and slot () and actually pass the parameters as follows:
Qobject::connect (&a, "2valueChanged (int)", &b, "1setValue (int)");
Finally, let's look at the function implementation of Connect ():
Qmetaobject::connection qobject::connect (const QObject *sender, const char *signal, C
Onst QObject *receiver, const char *method, Qt::connectiontype type) {//(1) 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 qmetaobject::connection (0);
} Qbytearray Tmp_signal_name;
(2) if (!check_signal_macro (sender, Signal, "Connect", "bind")) return qmetaobject::connection (0); Const Qmetaobject *smeta = Sender->metaobject ();
(3) const char *SIGNAL_ARG = signal; ++signal; SKip Code Qargumenttypearray signaltypes;
Q_assert (Qmetaobjectprivate::get (smeta)->revision >= 7); Qbytearray signalname = qmetaobjectprivate::d ecodemethodsignature (signal, signaltypes); (4)//(5) int signal_index = Qmetaobjectprivate::indexofsignalrelative (&smeta, Signalname, si
Gnaltypes.size (), Signaltypes.constdata ()); if (Signal_index < 0) {//Check for normalized signatures tmp_signal_name = Qmetaobject::normalizedsig
Nature (SIGNAL-1);
signal = Tmp_signal_name.constdata () + 1;
Signaltypes.clear ();
Signalname = qmetaobjectprivate::d ecodemethodsignature (signal, signaltypes);
Smeta = Sender->metaobject (); Signal_index =