3.3. Signals and Slots

The most important features of Qt are signals and slots.

Signals tell you that something has just happened. Signals are emitted (sent) when the user works with the computer. For example, when the user clicks the mouse or presses keys on a keyboard a signal is emitted. Signals can also be emitted when something happens inside the computer—when the clock ticks, for example.

Slots are the functions that respond to certain signals. It is important that your program responds to signals. Otherwise, it might look as if your program hangs. KDE programs don't—or shouldn't—hang!

Signals and slots are very object independent. Slots that handle a signal can be put in any object in your program. The object that sends the signal doesn't have to know anything about the slot or the object where the slot can be found. For example, you may have one window that contains a button and one window that contains a text box. You can let the text box respond to button clicks.

Signals and slots are primarily used for events handling, but you can use it for easy communication between objects too. When two windows need to communicate with each other, you can use signals and slots. Communication this way is much easier than doing it with pointers.

Note

Event handling is solved by callbacks in many other toolkits. A callback is a pointer to a function. The widgets contain callbacks, pointers to functions, for each event. When an event occurs, the appropriate function is called. It is simple in theory, but it is hard in practice. The callbacks are not type safe, which means that it is easy to make mistakes. Callbacks also can't take any number of parameters of any type like signals and slots do.

3.3.1. Creating a Slot

Creating a slot is easy. Any class that inherits from QObject can have slots.

First you must enable signals and slots. In the class definition, add the word Q_OBJECT. This is a keyword, which the moc understands.

The slot is just a member function in your class, but you must declare it in a slots section. Slots can be public, private, or protected.

The following example shows a class with a slot:


   1 
   2 class MyWindow : public QWidget
   3 {
   4   Q_OBJECT  // Enable signals and slots
   5 public:
   6   MyWindow();
   7 public slots:  // This slots section is public
   8   void mySlot();  // A public slot
   9 };
  10 

The slot in the preceding class definition is called mySlot. The keyword before slots defines the access mode. The slot mySlot above is public.

You write the implementation for the slot as if it was a common member function. The following example shows you what a slot implementation may look like:


   1 
   2 void MyWindow::mySlot()
   3 {
   4   cout << "slotPublic" << endl;
   5 }
   6 

3.3.2. Emitting a Signal

When you want to tell Qt that an event has occurred, you emit a signal. When that happens, Qt executes all slots that are connected to the signal.

Before a signal can be emitted, it must be defined. The class that emits a signal must contain the signal definition. Signals are defined in a signals section in your class. The following class definition defines a signal:


   1 
   2 class MyWindow : public QWidget
   3 {
   4   Q_OBJECT  // Enable signals and slots
   5 public:
   6   MyWindow();
   7 signals:
   8   void created();
   9 };
  10 

Signals are emitted with the command emit. The signal may be emitted like so:


   1 
   2 // Constructor for MyWindow
   3 MyWindow::MyWindow() : QWidget()
   4 {
   5   // Emit the signal created()
   6   emit created();
   7 }
   8 

The example above is only a simple demonstration that shows you how it works.

3.3.3. Connecting a Slot to a Signal

To make a slot respond to a certain signal, you must connect them to each other. You can connect several slots to one signal.

It is very simple to connect a slot to a signal. The command connect does this. The syntax is simple:


   1 
   2 connect(startobject, SIGNAL(signal()), targetobject, SLOT(slot()))
   3 

The parameter startobject contains a pointer to the object that the signal comes from.

The parameter signal specifies what signal to handle. The signal must be emitted by the startobject.

The object which responds to a signal is specified in the parameter targetobject.

The slot which responds to the signal is specified in the parameter slot. The slot must be in the object specified by targetobject.

The following class demonstrates that several slots can be connected to the same signal, and one slot can be connected to several signals:


Example 3.4. buttons.h: Class Definition for the Class MyWindow

   1 
   2  1:class MyWindow : public QWidget
   3  2:{
   4  3:  Q_OBJECT  // Enable slots and signals
   5  4:public:
   6  5: MyWindow();
   7  6:private slots:
   8  7:  void slotButton1();
   9  8:  void slotButton2();
  10  9:  void slotButtons();
  11 10:private:
  12 11:  QPushButton *button1;
  13 12:  QPushButton *button2;
  14 13: };
  15 

The listing below contains the class implementation:


Example 3.5. buttons.cc: Class Implementation for the Class MyWindow Declared in Listing 3.4

   1 
   2  1: MyWindow::MyWindow() : QWidget()
   3  2: {
   4  3:   // Create button1 and connect button1->clicked() to this->slotButton1()
   5  4:   button1 = new QPushButton("Button1", this);
   6  5:   button1->setGeometry(10,10,100,40);
   7  6:   button1->show();
   8  7:   connect(button1, SIGNAL(clicked()), this, SLOT(slotButton1()));
   9  8: 
  10  9:   // Create button2 and connect button2->clicked() to this->slotButton2()
  11 10:   button2 = new QPushButton("Button2", this);
  12 11:   button2->setGeometry(110,10,100,40);
  13 12:   button2->show();
  14 13:   connect(button2, SIGNAL(clicked()), this, SLOT(slotButton2()));
  15 14: 
  16 15:   // When any button is clicked, call this->slotButtons()
  17 16:   connect(button1, SIGNAL(clicked()), this, SLOT(slotButtons()));
  18 17:   connect(button2, SIGNAL(clicked()), this, SLOT(slotButtons()));
  19 18: }
  20 19: 
  21 20: 
  22 21: // This slot is called when button1 is clicked.
  23 22: void MyWindow::slotButton1()
  24 23: {
  25 24:   cout << "Button1 was clicked" << endl;
  26 25: }
  27 26: 
  28 27: 
  29 28: // This slot is called when button2 is clicked
  30 29: void MyWindow::slotButton2()
  31 30: {
  32 31:   cout << "Button2 was clicked" << endl;
  33 32: }
  34 33: 
  35 34: 
  36 35: // This slot is called when any of the buttons were clicked
  37 36: void MyWindow::slotButtons()
  38 37: {
  39 38:   cout << "A button was clicked" << endl;
  40 39: }
  41 

3.3.4. Signals and Slots with Parameters

During communication, it is sometimes useful to say more than "Hey!" That is all that the preceding signals say.

If you need to say more, the simplest way is to use parameters in your signals and slots.

For example, you may have two windows both containing a button and a text box. When the user types in text and clicks the button in one window, the caption for the other window will change to whatever was typed in.

The solution is to use slots and signals with parameters. Give both the signal and slot a parameter that contains the new window caption. When you emit the signal you set this parameter.

The following example code shows how parameters work. The signal and slot are both in the same class, but of course that is not necessary:


   1 
   2 class MyWindow : public QWidget
   3 {
   4   Q_OBJECT  // Enable signals and slots
   5 public:
   6   MyWindow();
   7 private slots:
   8   void slotChanged(int i);
   9 signals:
  10   void changed(int i);
  11 };
  12 

The class constructor may connect the slot to the signal, like this:


   1 
   2 MyWindow::MyWindow() : QWidget()
   3 {
   4   connect(this, SIGNAL(changed(int)), this, SLOT(slotChanged(int)));
   5 }
   6 

The slot and the signal must have compatible parameters. In the preceding example, they each have one integer as a parameter.

It is easy to emit a signal with a parameter. The following function emits the signal changed(int i):


   1 
   2 void MyWindow::emitter(int i)
   3 {
   4   emit changed(i);
   5 }
   6 

3.3.5. Slots in Temporary Classes

When a signal is emitted, the slots connected to it are activated.

Take a look at the following class constructor:


   1 
   2 MyWindow::MyWindow() : QWidget()
   3 {
   4   MyClass *temp = new MyClass();
   5 
   6   button = new QPushButton(this, "Button");
   7   button->setGeometry(0,0,100,30);
   8   button->show();
   9   connect(button, SIGNAL(clicked()), temp, SLOT(slotTemp()));
  10 
  11   delete temp;
  12 }
  13 

A button is created. The clicked() signal is connected to temp->slotTemp(). When you delete temp, the slot slotTemp() is also deleted. If the user clicks the button, an error will occur. Always consider this when you delete Qt objects.