In the previous chapter, we learned how to animate our application by using signals and slots to trigger and respond to actions that occur within our application. So far, we have been concentrating on examples that are contained in only one file and do not expressly describe a full working application. To do so, we will need to change the style in which our applications are written, and also adopt a number of new conventions.
In this chapter, we shall work with Windows in Qt, so that by the end of the chapter, you should be able to do the following:
To create a window(ed) application, we usually call the show() method on an instance of QWidget and that makes that widget, to be contained in a window of its own, along with its child widgets displayed in it.
A recap of such a simple application is as follows:
#include <QApplication>
#include <QMainWindow>
#include <QLabel>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMainWindow mainWindow;
mainWindow.show();
return a.exec();
}
mainWindow here is an instance of QMainWindow, which is derived from QWidget. As such, by calling the show() method, a window will appear. If you were to replace QMainWindow with QLabel, this will still work.
But this style of writing applications is not the best. Instead, from this point onward, we shall define our own custom widget, in which we shall define child widgets and make connections between signals and sockets.
Now, let's rewrite the preceding application by sub-classing QMainWindow. We have chosen to subclass QMainWindow because we need to illustrate the menu and toolbars.
We start off by creating a new folder and defining a header file. The name of our header file here is mainwindow.h, but feel free to name it how you want and remember to add the .h suffix. This file should basically contain the following:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QLabel>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow();
};
#endif
We include the Qt classes QMainWindow, and QLabel in our header file. Then, we subclass QMainWindow and call it MainWindow. The constructor of this new class is declared with the following:
public:
MainWindow();
The entire class definition is wrapped within an #ifndef ... #endif directive, which tells the preprocessor to ignore its content if it is accidentally included multiple times in a file.
It is possible to use the non-standard, but widely used, preprocessor directive, #pragma once.
Take notice of the Q_OBJECT macro. This is what makes the signals and slots mechanism possible. Remember that the C++ language does not know about the keywords used to set up signals and slots. By including this macro, it becomes part of the C++ syntax.
What we have defined so far is just the header file. The body of the main program has to live in some other .cpp file. For easy identification, we call it mainwindow.cpp. Create this file within the same folder and add the following lines of code:
#include "mainwindow.h"
MainWindow::MainWindow()
{
setWindowTitle("Main Window");
resize(400, 700);
QLabel *mainLabel = new QLabel("Main Widget");
setCentralWidget(mainLabel);
mainLabel->setAlignment(Qt::AlignCenter);
}
We include the header file that we defined earlier with the first line of code. The default constructor of our sub-classed widget, MainWindow, is defined.
Notice how we call the method that sets the title of the window. setWindowTitle() is invoked and can be accessed from within the constructor since it is an inherited method from QWindow. There is no need to use the this keyword. The size of the window is specified by calling the resize() method and passing two integer values to be used as the dimensions of the window.
An instance of a QLabel is created, mainLabel. The text within the label is aligned to the center by calling mainLabel->setAlignment(Qt::AlignCenter).
A call to setCentralWidget() is important as it situates any class that inherits from QWidget to occupy the interior of the window. Here, mainLabel is being passed to setCentralWidget, and that will make it the only widget to be displayed within the window.
Consider the structure of QMainWindow in the following diagram:
At the very top of every window is the Menu Bar. Elements such as the file, edit, and help menus go there. Below that, are the Toolbars. Contained within the Toolbars are the Dock Widgets, which are collapsible panels. Now, the main controls within the window must be put in the Central Widget location. Since a UI is made up of several widgets, it will be good to compose a widget that will contain child widgets. This parent widget is what you will stick into the Central Widget area. To do this, we call setCentralWidget() and pass in the parent widget. At the bottom of the window, is the Status Bar.
To run the application, we need to create an instance of our custom window class. Create a file called main.cpp within the same folder where the header and .cpp files are located. Add the following lines of code to main.cpp:
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow mainwindow;
mainwindow.show();
return app.exec();
}
We include the header file mainwindow.h, which contains the declaration of our custom class, MainWindow. Without this, the compiler wouldn't know where to find the definition of the MainWindow class.
An instance of MainWindow is created and the show() method is called on it. We still have to call the show() method on mainwindow. MainWindow, which is a subclass of QMainWindow, and behaves just like any widget out there. Furthermore, as we already know, to cause a widget to appear, you have to call the show() method on it.
To run the program, move into the folder via the command line and issue the following commands:
% qmake -project
Add QT += widgets to the .pro file that is generated. Now continue with the next set of commands:
% qmake
% make
Examine the .pro file for a second. At the very bottom of the file, we have the following lines:
HEADERS += mainwindow.h
SOURCES += main.cpp mainwindow.cpp
The headers are automatically collected and added to HEADERS. Similarly, the .cpp files are collected and added to SOURCES. Always remember to check this file when there are compilation errors to ensure that all required files have been added.
To run the program, issue the following command:
% ./classSimpleWindow
For those who work on th...