Handling Interaction: Signals & Slots

Tip

You may find it useful to have The Qt Project documentation on Signals & Slots open in your browser while doing this exercise.

Using Built-in Signals & Slots

  1. Create a directory for this exercise and download the archive volume.zip into it. Then unpack the archive. This should create a subdirectory called volume. In a terminal window, cd into this subdirectory and spend a couple of minutes examining the files therein.

    These files simulate a volume control application. The required widget are present, but there is currently no code to handle user interaction.

  2. Use CMake to build the application, then enter ./volume to run it. You should see something like this:

    Screenshot of Qt volume control application

    Adjusting the dial will currently have no effect.

  3. Edit window.hpp and find the comment that says ‘declare makeConnections method here’. Replace the comment with the following method prototype:

    void makeConnections();

    This method is where the code to connect signals to slots will go. Note that there is no requirement to do signal-slot connection within such a method; you could do it directly within the constructor, for example. However, you may find this approach helps to make your code better structured and easier to read.

  4. Edit window.cpp. Inside the VolumeControl constructor, just after the call to arrangeWidgets(), add a call to the makeConnections() method. Then implement the method at the bottom of the file, like so:

    void VolumeControl::makeConnections()
    {
      connect(dial, SIGNAL(valueChanged(int)), number, SLOT(display(int)));
    }

    Here, connect() is a method inherited from QWidget that is used to connect signals to slots. This particular call will connect the valueChanged(int) signal generated by the QDial widget to the display(int) slot provided by the QLCDNumber widget.

  5. Rebuild the application and try running it again. If you have connected signal and slot correctly, you should find that adjusting the dial updates the number.

Custom Slots

  1. Download the archive dice.zip to the directory that you created for this exercise. In a terminal window, cd to that directory and unpack the archive with the command unzip dice.zip. This should create a subdirectory dice, containing several files.

    Spend a few minutes examining the files window.hpp, window.cpp and main.cpp. Then use CMake to build the application, in the usual way. When you run the application with ./dice, you should see the GUI shown below.

    Screenshot of Qt dice roller application

    Clicking on the ‘Roll Dice’ button will currently have no effect.

  2. Edit window.hpp. Add the text Q_OBJECT as the first item inside the DiceRoller class definition, on a line by itself, just before the public: section header. Q_OBJECT is a preprocessor macro used in Qt to indicate that a class will define a custom slot.

    Then add a prototype for a void method named makeConnections to the private section.

    Finally, add a new ‘slots’ section to the class definition, just after the private section:

    private slots:
      void rollDice();
  3. Edit window.cpp. Add the following definition of the rollDice() method to the bottom of the file:

    void DiceRoller::rollDice()
    {
      int n = random->bounded(0, 6);
      die1->setPixmap(dieFaces[n]);
    
      n = random->bounded(0, 6);
      die2->setPixmap(dieFaces[n]);
    }

    This method will alter the images displayed by label widgets die1 and die2, making its choices randomly from the images available in the array dieFaces.

    Then find the two lines near the start of the DiceRoller constructor that call setPixmap() on labels die1 and die2. Replace these two lines with a line that calls the rollDice() method.

  4. Now define the makeConnections() method to look like this:

    void DiceRoller::makeConnections()
    {
      connect(rollButton, SIGNAL(clicked()), this, SLOT(rollDice()));
    }

    The call to the connect() method looks a little different from the earlier example. Here, the recipient is specified using the this pointer, meaning ‘this DiceRoller object’. The slot is the custom rollDice() method defined in the previous step.

    Caution

    Don’t forget that you also need to add a line to the constructor that calls makeConnections(). Put this immediately after the call to arrangeWidgets().

  5. Now enter cmake --build . (or simply make) to rebuild the application. Run the application again, with ./dice. Clicking on the ‘Roll Dice’ button should now change the die face images in a random fashion.

    Note

    Behind the scenes, Qt is checking whether it needs to run the Meta-Object Compiler (moc) during the build process. This tool generates additional code for the custom slot that integrates it into Qt’s signals & slots mechanism.

    If you are using the older method for building Qt applications, based on the qmake tool, then you may find that you need to run this tool again, to generate a new makefile, after adding any new custom signals or slots to your code.

Over To You…

Experiment some more with signals and slots.

  • Try modifying the volume control application so that it does something else in response to adjustment of the dial. For example, you could change the colour of the number if it exceeds a threshold.

  • Return to the currency converter application from the previous exercise. Connect up the widgets so that the appropriate conversion calculations are performed when a value or the currency is changed.