Qt Applications as Classes

This exercise introduces a cleaner and more scalable way of structuring your Qt applications, as classes rather than procedural programs.

We focus here on creating subclasses of QWidget, which is a suitable choice for simple UIs that don’t need menus, toolbars, etc. A better approach for more complex UIs is to subclass QMainWindow—which is covered in a later exercise.

A Template For Subclassing QWidget

Try structuring your UI using three files: window.hpp, which defines the class; window.cpp, which implements the methods of the class; and main.cpp, which provides the main program.

window.hpp

This can have the following format:

#pragma once

#include <QWidget>

// Put forward references to widget classes here

class MyWindow: public QWidget
{
  public:
    MyWindow();

  private:
    void createWidgets();
    void arrangeWidgets();

    // Specify widgets here, using pointers
};

You should obviously change MyWindow to a name that is more appropriate for the application that you are creating.

The private methods specified here should be called from within the constructor. This isn’t a requirement; it is done purely to make the constructor smaller and improve the structure and readability of the code by keeping widget configuration and layout code cleanly separated.

The comments in the template above will need to be replaced with some code. For example, if your UI needs a text field for entry of someone’s name, you would specify a forward reference for the QLineEdit class like this:

class QLineEdit;

You would then specify the widget instance variable in the private section of the class definition like so:

QLineEdit* nameField;

window.cpp

The implementation file for the class defined in window.hpp can have this format:

#include <QtWidgets>
#include "window.hpp"

MyWindow::MyWindow()
{
  createWidgets();
  arrangeWidgets();
  setWindowTitle("My Window");
  setMinimumSize(320, 200);
}

void MyWindow::createWidgets()
{
  // Create widgets on the heap using new
}

void MyWindow::arrangeWidgets()
{
  // Create layout for widgets here
  // Remember to call setLayout!
}

Notice how the constructor calls the methods to create widgets and layout. This is also a logical place to set the title for the window and impose any constraints on window size. If you wanted to prevent resizing, for example, you could call setFixedSize() here, instead of setMinimumSize().

main.cpp

The main program can take this form:

#include <QApplication>
#include "window.hpp"

int main(int argc, char* argv[])
{
  QApplication app(argc, argv);

  MyWindow window;
  window.show();

  return app.exec();
}

In this case, the top-level UI object is created and manipulated on the stack (lines 8 & 9), but you could create it on the heap instead if you wanted to:

QWidget* window = new MyWindow();
window->show();

Putting It Into Practice

  1. Create a directory named currency, to hold the application. Copy the CMakeLists.txt file from the ‘Getting Started’ exercise into the currency directory, then edit it, replacing all occurrences of hello with currency.

    You will also need to alter the part of the build file that calls the add_qt_executable function, changing it to look like this:

    add_qt_executable(currency main.cpp window.cpp)
  2. Inside the currency directory, use the template described above to create a simple currency converter application, in three files called window.hpp, window.cpp and main.cpp.

    The application should look something like this:

    Screenshot of currency converter UI

    Use QDoubleSpinBox widgets for the amounts and QComboBox widgets for the corresponding currencies. Use a QHBoxLayout to pair up each spin box with the corresponding combo box. Use a QVBoxLayout for the overall layout of the window.

  3. Use CMake to build the application, in the manner discussed in previous exercises. Run the application with ./currency.

  4. If you want more practice, try using the template to create the UI for one of the layout examples in the previous worksheet.