signal and slot as the core mechanism of QT is widely used in QT programming, this paper introduces some basic concepts of signal and slot, meta-object tools and some problems that should be paid attention to in the process of actual use.
QT is a cross-platform C + + GUI application architecture, which provides a rich set of widgets, object-oriented, easy to extend, real component programming and so on, even more noticeable is that the most popular KDE desktop environment on Linux is built on the basis of the QT library. QT supports the following platforms: Ms/windows-95, 98, NT and 2000;unix/x11-linux, Sun Solaris, HP-UX, Digital UNIX, IBM AIX, SGI IRIX Embedded-supports the Framebuffer Linux platform. With the rapid development and popularization of KDE, QT is likely to become the preferred GUI for software development on Linux Windows platforms.
I. Overview
Signal and slot mechanism is the core mechanism of QT, to be proficient in QT programming must know the signal and the slot. Signals and slots are an advanced interface that is applied to communication between objects, which is the core feature of Qt and is also an important part of QT's differentiation from other toolkits. Signals and slots are a communication mechanism defined by QT itself, independent of the standard C/s + + language, so proper processing of signals and slots requires the use of a QT tool called the MOC (Meta Object Compiler), a C + + preprocessor, It automatically generates the additional code needed for high-level event handling.
In many of the GUI toolkits we know, widgets have a callback function that responds to each action they can trigger, which is usually a pointer to a function. However, the signals and slots in QT replace these messy function pointers, making it more concise and straightforward to write these communication programs. Signals and slots can carry any number and any type of parameter, they are completely safe in type and do not produce a core dumps like a callback function.
All classes derived from Qobject or its subclasses (for example, qwidget) can contain signals and slots. When an object changes its state, the signal is emitted by the object (emit), which is all that the object has to do, and it does not know who is receiving the signal at the other end. This is the real message encapsulation, which ensures that the object is used as a real software component. Slots are used to receive signals, but they are normal object member functions. A slot does not know if there is any signal connected to itself. Furthermore, the object is not aware of specific communication mechanisms.
You can connect a lot of signals to a single slot, or you can connect a single signal to a lot of slots, or even connect one signal to another, and it's possible to send a second message regardless of when the first signal is fired. In short, signals and slots construct a powerful component programming mechanism.
Second, the signal
When a signal changes the internal state of its customer or owner, the signal is emitted by an object. Only classes that have defined this signal and their derived classes can emit this signal. When a signal is emitted, the slot associated with it will be executed immediately, just like a normal function call. The signal-slot mechanism is completely independent of any GUI event loop. Only if all the slots return after the transmit function (emit) is returned. If there are multiple slots associated with a signal, then when the signal is emitted, the slots will be executed one after the other, but the order in which they are executed will be random and indeterminate, and we cannot artificially specify which first to execute and which to execute.
The signal is declared in the header file, and QT's signals keyword indicates that it has entered the signal declaration area and can then declare its own signal. For example, three signals are defined below:
signals: void mySignal();void mySignal(int x);void mySignalParam(int x,int y);
|
In the definition above, signals is the key word for QT, not C + +. The next line, void Mysignal (), defines the signal mysignal, which does not carry parameters, and the next line, void mysignal (int x), defines the name of the signal mysignal, but it carries an shaping parameter, which is somewhat similar to C + + The virtual function in the. The formal signal declaration is the same as the normal C + + function, but the signal does not have a function body definition, in addition, the return type of the signal is void, do not expect to be able to return any useful information from the signal.
The signals are generated automatically by the MOC and they should not be implemented in the. cpp file.
Three, Groove
Slots are normal C + + member functions that can be called normally, and their only peculiarity is that many signals can be associated with them. This slot is called when the signal associated with it is emitted. Slots can have parameters, but slot parameters cannot have default values.
Since slots are normal member functions, they have access as well as other functions. Access to slots determines who can be associated with them. Like normal C + + member functions, slot functions are divided into three types, public slots, private slots, and protected slots.
- Public slots: A slot declared in this area means that any object can connect the signal to it. This is useful for component programming, where you can create objects that are not known to each other and connect their signals to slots so that the information can be passed correctly.
- Protected slots: A slot declared in this area means that the current class and its subclasses can connect the signal to it. This applies to those slots, which are part of the class implementation, but whose interface interfaces are externally oriented.
- Private slots: A slot declared in this area means that only the class itself can connect the signal to it. This applies to very tightly connected classes.
Slots can also be declared as virtual functions, which is also very useful.
The declaration of the slot is also made in the header file. For example, three slots are declared below:
public slots:void mySlot();void mySlot(int x);void mySignalParam(int x,int y);
|
Iv. correlation of signals to slots
The signal of an object is associated with the slot function of another object by calling the Connect function of the Qobject object so that the receiver's slot function is called when the emitter emits the signal. The function is defined as follows:
bool QObject::connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * member ) [static]
|
The purpose of this function is to associate the signal signal in the emitter sender object with the member slot function in receiver receiver. When specifying the signal signal, you must use the QT macro signal (), which must be used when specifying the slot function (). If the emitter and receiver belong to the same object, then the receiver parameter can be omitted from the connect call.
For example, the following defines two objects: the Label object label and the ScrollBar object scroll, and the valuechanged () signal is associated with the Setnum () of the Label object, and the signal also carries an shaping parameter so that the label always displays the value of the position where the scroll bar is located.
QLabel *label = new QLabel; QScrollBar *scroll = new QScrollBar; QObject::connect( scroll, SIGNAL(valueChanged(int)), label, SLOT(setNum(int)) );
|
One signal can even be associated with another signal, see the following example:
class MyWidget : public QWidget { public: MyWidget(); ... signals: void aSignal(); ... private: ... QPushButton *aButton; }; MyWidget::MyWidget() { aButton = new QPushButton( this ); connect( aButton, SIGNAL(clicked()), SIGNAL(aSignal()) ); }
|
In the above constructor, Mywidget creates a private button Abutton, and the button's Click event generates a signal clicked () associated with another signal asignal (). Thus, when the signal clicked () is emitted, the signal asignal () is then emitted. Of course, you can also directly associate a click event with a private slot function and then emit a asignal () signal in the slot, which seems a bit superfluous.
We can use the Disconnect function to disconnect the signal when it is not necessary to continue to associate with the slot. It is defined as follows:
bool QObject::disconnect ( const QObject * sender, const char * signal, const Object * receiver, const char * member ) [static]
|
This function disconnects the signal in the emitter from the slot function in the receiver.
There are three situations in which you must use the Disconnect () function:
-
Disconnects any objects associated with an object. This seems a little incomprehensible, in fact, when we define one or more signals in an object that are associated with slots in several other objects, and if we want to sever these associations, we can use this method very concisely.
Disconnect (myObject, 0, 0, 0) or myobject->disconnect () |
-
Disconnects any association with a particular signal.
Disconnect (MyObject, SIGNAL (Mysignal ()), 0, 0) or Myobject->disconnect (SIGNAL (Mysignal ())) |
-
Breaks an association between two objects.
Disconnect (myObject, 0, myreceiver, 0) or Myobject->disconnect (myreceiver) |
In the Disconnect function 0 can be used as a wildcard character, representing any signal, any receiving object, or any slot function in the receiving object. But emitter sender cannot be 0, and the value of the other three parameters can be equal to 0.
Five, meta-object tools
The metafile compiler moc (Meta object compiler) parses a class declaration from a C + + file and produces C + + code for initializing a meta object that contains the names of all the signals and slots and pointers to those functions.
The MOC reads the C + + source file, and if it finds a class with a Q_object macro declaration, it generates another C + + source file that contains the meta-object code for that class. For example, suppose we have a header file mysignal.h that contains a signal or slot declaration, and the MOC tool automatically generates a C + + source file named Mysignal.moc.h and submits it to the compiler before compiling, as a result of that file. Similarly, The MOC tool corresponding to the Mysignal.cpp file will automatically generate a file named Mysignal.moc.cpp that is submitted to the compiler.
Meta-object code is required by the signal/slot mechanism. C + + source files generated with MOC must be compiled and concatenated with the class implementation, or included in the source file of the class with the # include statement. The MOC does not extend the # include or # define macro definitions, it simply skips any pre-processing instructions encountered.
Vi. Examples of procedures
Here is a simple sample program, the program defines three signals, three slot functions, and then the signal is associated with the slot, each slot function simply pop up a dialog window. Readers can use KDevelop to generate a simple QT application, and then add the following code to the appropriate program.
The declaration of the signal and slot functions is generally in the header file, and the Q_object statement must be added at the beginning of the class declaration, which is indispensable, and it tells the compiler that the MOC tool must be used to extend before compiling. The keyword signals points to the subsequent start of the signal Declaration, where signals is used in the plural form rather than the singular, siganls no public, private, protected and other attributes, this is different from slots. In addition, the signals and slots keywords are defined by QT itself, not keywords in C + +.
The declaration of a signal is similar to a declaration of a function rather than a declaration of a variable, a type on the left, a parenthesis on the right, or a type in parentheses if you want to pass a parameter to a slot, and of course, there can be more than one form parameter.
The keyword slots points to the declaration of the subsequent beginning of the slot, where slots is used in the plural form.
The declaration of a slot, like the declaration of a normal function, can carry 0 or more formal parameters. Since the declaration of a signal is similar to the declaration of a normal C + + function, the signal can also be declared in the form of a virtual function in C + +, with the same name but with different parameters. For example, the first defined void mysignal () has no parameters, and the second is defined with parameters, from which we can see that the signal mechanism of QT is very flexible.
The connection between the signal and the slot must be specified in advance with the Connect function. If you want to break the relationship between the two, you can use the function disconnect.
Tsignal.h...class tsignalapp:public qmainwindow{q_object...//Signal Declaration Area signals://Declaration signal mysignal () void mysignal ();// DECLARE signal mysignal (int) void mysignal (int x),//Declaration signal Mysignalparam (int,int) void Mysignalparam (int x,int y);//Groove Declaration Area public slots ://Declaration slot function Myslot () void Myslot ();//Declaration slot function Myslot (int) void myslot (int x);//Declaration slot function Mysignalparam (int,int) void Mysignalparam (int x,int y);} .....//tsignal.cpp ... Tsignalapp::tsignalapp () {...//the Signal mysignal () is associated with the slot Myslot () Connect (this,signal (mysignal ()), slot (Myslot ())); Associate the Signal mysignal (int) with the slot myslot (int) with connect (this,signal (mysignal (int)), slot (Myslot (int))); Connect the signal Mysignalparam (Int,int) to the slot Myslotparam (Int,int) (This,signal (Mysignalparam)), slot ( Myslotparam (Int,int)); }//defines the slot function myslot () void Tsignalapp::myslot () {qmessagebox::about (this, "tsignal", "The is a Signal/slot sample without parameter. "); Define the Slot function myslot (int) void tsignalapp::myslot (int x) {qmessagebox::about (this, "tsignal", "This is a Signal/slot sample with One parameter. ");} Define slot function Myslotparam (int,int) void TsiGnalapp::myslotparam (int x,int y) {char s[256];sprintf (S, "x:%d y:%d", x, y); Qmessagebox::about (This, "tsignal", s);} void Tsignalapp::slotfilenew () {//Transmit signal mysignal () emit mysignal ();//Transmit signal mysignal (int) emit mysignal (5);// Transmit signal Mysignalparam (5,100) emit Mysignalparam (5,100);}
|
Vii. issues to be noted
Signal and groove mechanism is relatively flexible, but some limitations we must understand, so in the actual use of the process to achieve a targeted, to avoid some errors. Here's a look at the situation.
1. The signal and groove efficiency is very high, but compared with the real callback function, because of increased flexibility, so there is a loss of speed, of course, this loss is relatively small, by testing on a i586-133 machine is 10 microseconds (running Linux), It can be seen that the simplicity and flexibility provided by this mechanism are worthwhile. But if we are to pursue efficiency, for example in real-time systems, we need to use this mechanism as sparingly as possible.
2. The signal and slot mechanism is the same as the call of the normal function, and if used improperly, it is possible to generate a dead loop when the program executes. Therefore, it is important to avoid indirectly forming an infinite loop when defining the slot function, that is, the same signal received again in the slot. For example, in the example given earlier, if you add a statement emit mysignal () in the Myslot () slot function, you can form a dead loop.
3. If a signal is associated with multiple slots, then when the signal is emitted, the associated slots will be activated in a random order.
4. Macro definitions cannot be used in signal and slot parameters.
Since the MOC tool does not extend the # define, macros that carry parameters in signals and slots do not work correctly, if not with parameters. For example, the following example takes a parameter macro signedness (a) as a signal parameter that is not syntactically:
#ifdef ultrix #define SIGNEDNESS(a) unsigned a #else #define SIGNEDNESS(a) a #endif class Whatever : public QObject { [...] signals: void someSignal( SIGNEDNESS(a) ); [...] };
|
5. Constructors cannot be used within the signals or slots declaration area.
Indeed, placing a constructor in the signals or slots area is somewhat incomprehensible, however, it cannot be placed in the private slots, protected slots, or public slots area. The following usage is not syntactically required:
class SomeClass : public QObject { Q_OBJECT public slots: SomeClass( QObject *parent, const char *name ) : QObject( parent, name ) {} // 在槽声明区内声明构造函数不合语法 [...] };
|
6. The function pointer cannot be used as a signal or slot parameter.
For example, the following example will void (*applyfunction) (qlist*, void*) as a parameter is not syntactically:
class someClass : public QObject { Q_OBJECT [...] public slots: void apply(void (*applyFunction)(QList*, void*), char*); // 不合语法 };
|
You can bypass this limitation by using the following method:
typedef void (*ApplyFunctionType)(QList*, void*); class someClass : public QObject { Q_OBJECT [...] public slots: void apply( ApplyFunctionType, char *); };
|
7. The signal and slot cannot have default parameters.
Since Signal->slot bindings occur at run time, it is conceptually difficult to use default parameters. The following usage is unreasonable:
class SomeClass : public QObject { Q_OBJECT public slots: void someSlot(int x=100); // 将x的缺省值定义成100,在槽函数声明中使用是错误的 };
|
8. The signal and the slot also cannot carry the template class parameter.
If the signal and slot are declared as template class parameters, it is not possible to obtain the expected results even if the MOC tool does not report an error. For example, in the following example, when a signal is emitted, the slot function is not called correctly:
[...] public slots: void MyWidget::setLocation (pair<int,int> location); [...] public signals: void MyObject::moved (pair<int,int> location);
|
However, you can use TypeDef statements to circumvent this limitation. As shown below:
typedef pair<int,int> IntPair; [...] public slots: void MyWidget::setLocation (IntPair location); [...] public signals: void MyObject::moved (IntPair location);
|
If you use this, you can get the right results.
9. Nested classes cannot be in the signal or slot area, nor can they have signals or slots.
For example, in the following example, declaring slot B () in class B is not syntactically appropriate, and declaring slot B () in the signal area is also non-grammatical.
class A { Q_OBJECT public: class B { public slots: // 在嵌套类中声明槽不合语法 void b(); [....] }; signals: class B { // 在信号区内声明嵌套类不合语法 void b(); [....] }: };
|
10. The friend declaration cannot be located in the signal or slot declaration area. Instead, they should be declared in the private, protected, or public areas of ordinary C + +. The following examples are syntactically irregular:
class someClass : public QObject { Q_OBJECT [...] signals: //信号定义区 friend class ClassTemplate; // 此处定义不合语法 };
|
Introduction to the signal and groove mechanism of QT