- Game Programming Using Qt Beginner's Guide
- Witold Wysota Lorenz Haas
- 1758字
- 2025-04-04 20:19:16
Time for action – creating a Qt Desktop project
When you first start Qt Creator, you will see a welcome screen. From the File menu, choose New File or Project. There are a number of project types to choose from. follow the given steps for creating a Qt Desktop project:
- For a widget-based application, choose the Applications group and the Qt Gui Application template:
- The next step is to choose a name and location for your new project:
- We are going to create a simple tic-tac-toe game, so we will name our project
tictactoe
and provide a nice location for it.Tip
If you have a common directory where you put all your projects, you can tick the Use as default project location checkbox for Creator to remember the location and suggest it the next time when you start a new project.
- When you click on Next, you will be presented with a window that lets you choose one or more of the defined compilation kits for the project. Proceed to the next step without changing anything. You will be presented with the option of creating the first widget for your project. Fill in the data as shown in the following screenshot:
- Then, click on Next and Finish.
What just happened?
Creator created a new subdirectory in the directory that you previously chose for the location of the project and where you put a number of files. Two of these files (tictactoewidget.h
and tictactoewidget.cpp
) implement the TicTacToeWidget
class as the subclass of QWidget
. The third file called main.cpp
contains code for the entry point of the application:
#include "tictactoewidget.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); TicTacToeWidget w; w.show(); return a.exec(); }
This file creates an instance of the QApplication
class and feeds it with standard arguments to the main()
function. Then, it instantiates our TicTacToeWidget
class, calls its show
method, and finally returns a value returned by the exec
method of the application object.
QApplication
is a singleton class that manages the whole application. In particular, it is responsible for processing events that come from within the application or from external sources. For events to be processed, an event loop needs to be running. The loop waits for incoming events and dispatches them to proper routines. Most things in Qt are done through events—input handling, redrawing, receiving data over the network, triggering timers, and so on. This is the reason we say that Qt is an event-oriented framework. Without an active event loop, nothing would function properly. The exec
call in QApplication
(or to be more specific, in its base class—QCoreApplication
) is responsible for entering the main event loop of the application. The function does not return until your application requests the event loop to be terminated. When this eventually happens, the main
function returns and your application ends.
The final file that was generated is called tictactoe.pro
and is the project configuration file. It contains all the information that is required to build your project using the tools Qt provides. Let's analyze this file:
QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = tictactoe TEMPLATE = app SOURCES += main.cpp tictactoewidget.cpp HEADERS += tictactoewidget.h
The first two lines enable Qt's core
, gui
, and widgets
modules. The next two lines specify that your project file describes an application (as opposed to, for example, a library) and declares the name of the target that is executable to be tictactoe
. The last two lines add files that Creator generated for us to build the process.
What we have now is a complete minimal Qt GUI project. To build and run it, simply choose the Run entry from the Build drop-down menu or click on the green triangle icon on the left-hand side of the Qt Creator window. After a while, you should see a window pop up. Since we didn't add anything to the window, it is blank.
Adding child widgets to a window
After we managed to get a blank window on screen, the next step is to add some content to it. To do this, you need to create widgets and tell Qt to position them in the window. The basic way to do this is to provide a parent to a widget.
In Qt, we group objects (such as widgets) into parent-child relationships. This scheme is defined in the superclass of QWidget
—QObject
, which is the most important class in Qt, and we will cover it in more detail later in this chapter. What is important now is that each object can have a parent object and an arbitrary number of children. In the case of widgets, there is a rule that a child occupies a subarea of its parent. If it doesn't have a parent, then it becomes a top-level window that can usually be dragged around, resized, and closed. We can set a parent for an object in two ways. One way is to call the setParent
method defined in QObject
that accepts a QObject
pointer. Because of the rule mentioned earlier, QWidget
wants to have other widgets as parents, so the method is overloaded in QWidget
to accept a QWidget
pointer. The other way is to pass a pointer to the parent object to the QWidget
constructor of the child object. If you look at the code of the widget that was generated by Creator, you will notice that the constructor also accepts a pointer to a widget as its last (optional) argument:
TicTacToeWidget::TicTacToeWidget(QWidget *parent) : QWidget(parent) { }
It then passes that pointer to the constructor of its base class. Therefore, it is important that you always remember to create a constructor for your widgets that accepts a pointer to a QWidget
instance and passes it up the inheritance tree. All standard Qt widgets also behave this way.
Managing widget content
Making a widget display as part of its parent is not enough to make a good user interface. You also need to set its position and size and react to the changes that happen to its content and to the content of its parent widget. In Qt, we do this using a mechanism called layouts.
Layouts allow us to arrange the content of a widget, making sure that its space is used efficiently. When we set a layout on a widget, we can start adding widgets and even other layouts, and the mechanism will resize and reposition them according to the rules that we specify. When something happens in the user interface that influences how widgets should be displayed (for example, the button text is replaced with longer text, which makes the button require more space to show its content; if not, one of the widgets gets hidden), the layout is triggered again, which recalculates all positions and sizes and updates widgets as necessary.
Qt comes with a predefined set of layouts that are derived from the QLayout
class, but you can also create your own. Those that we already have at our disposal are QHBoxLayout
and QVBoxLayout
, which position items horizontally and vertically; QGridLayout
, which arranges items in a grid so that an item can span across columns or rows; and QFormLayout
, which creates two columns of items with item descriptions in one column and item content in the other. There is also QStackedLayout
, which is rarely used directly and which makes one of the items assigned to it possess all the available space. You can see the most common layouts in action in the following figure:

To use a layout, we need to create an instance of it and pass a pointer to a widget that we want it to manage. Then, we can start adding widgets to the layout:
QHBoxLayout *layout = new QHBoxLayout(parentWidget); QPushButton *button1 = new QPushButton; QPushButton *button2 = new QPushButton; layout->addWidget(button1); layout->addWidget(button2);
We can even move widgets further from each other by setting spacing on the layout and setting custom margins on the layout:
layout->setSpacing(10); layout->setMargins(10, 5, 10, 5); // left, top, right, bottom
After building and running this code, you see two buttons that are evenly distributed in their parent space. Note that, even though we didn't explicitly pass the parent widget pointer, adding a widget to a layout makes it reparent the newly added widget to the widget that the layout manages. Resizing the parent horizontally would also cause buttons to resize again, covering all the space available. However, if you resize parentWidget
vertically, buttons will change their position but not their height.
This is because each widget has an attribute called a size policy, which decides how a widget is to be resized by a layout. You can set separate size policies for horizontal and vertical directions. A button has a vertical size policy of Fixed
, which means that the height of the widget will not change from the default height regardless of how much space there is available. The following are the available size policies:
Ignore
: In this, the default size of the widget is ignored and the widget can freely grow and shrinkFixed
: In this, the default size is the only allowed size of the widgetPreferred
: In this, the default size is the desired size, but both smaller and bigger sizes are acceptableMinimum
: In this, the default size is the smallest acceptable size for the widget, but the widget can be made larger without hurting its functionalityMaximum
: In this, the default size is the largest size of the widget and the widget can be shrunk (even to nothing) without hurting its functionalityExpanding
: In this, the default size is the desired size; a smaller size (even zero) is acceptable but the widget is able to increase its usefulness when more and more space is assigned to itMinimumExpanding
: This is a combination ofMinimum
andExpanding
—the widget is greedy in terms of space and it cannot be made smaller than its default size
How do we determine the default size? The answer is by the size returned by the sizeHint
virtual method. For layouts, the size is calculated based on the sizes and size policies of their child widgets and nested layouts. For basic widgets, the value returned by sizeHint
depends on the content of the widget. In the case of a button, if it holds a line of text and an icon, sizeHint
will return the size that is required to fully encompass the text, icon, some space between them, the button frame, and the padding between the frame and content itself.