Chapter 5. Where Do Controls Begin?

Who can say where one thing ends and another begins?
— Sun Tzu

Almost every application has a graphical user interface (GUI, Graphical User Interface). Widgets are the fundamental building blocks used to construct one. A widget is not simply a region displayed on screen — it is a component capable of performing a variety of actions, such as responding to incoming signals and events or sending signals to other widgets. Qt provides a complete set of widgets, from menu buttons to dialog boxes, covering everything needed to build professional applications. If the built-in widgets are not sufficient, you can create your own by subclassing existing widget classes.

The widget class hierarchy is shown in Fig. 5.1. Part II of this book covers most of these classes. Classes not described in this part can be found in other chapters: QMenu (see Chapter 31), QGLWidget (see Chapter 23), QMainWindow (see Chapter 34), QGraphicsView (see Chapter 21).

The QWidget Class

The QWidget class is the foundation of all widget classes. Its interface contains a large number of methods, properties, and definitions required by every widget — for example, for resizing, repositioning, event handling, and more. As shown in Fig. 5.1, QWidget itself inherits from QObject, which means it can participate in the signal/slot mechanism and the object hierarchy system. As a result, widgets can have child widgets that are rendered inside their parent. This is significant because any widget can act as a container for other widgets — Qt makes no distinction between controls and containers. Widgets inside containers can themselves act as containers for yet more widgets, and so on indefinitely. For example, a dialog box that contains OK and Cancel buttons is itself a container. This design is also convenient because if a parent widget becomes disabled or hidden, all child widgets automatically inherit that state.

Qt6 widget class hierarchy diagram showing QWidget subclasses including QDialog, QFrame, QAbstractSpinBox, and QAbstractScrollArea
Fig. 5.1. Widget class hierarchy
Important!

Qt distinguishes between two types of hierarchy: the class hierarchy (inheritance) and the object hierarchy (parent-child relationships between objects). When we say that “widgets can have children,” we are referring to the object hierarchy, not class inheritance. For example, a button (QPushButton) is a child of a window not in the sense of class inheritance (it inherits from QAbstractButton), but in the sense of the object structure — it is placed inside the window and owned by it.

Widgets with no parent are called top-level widgets and have their own window. Any widget can be a top-level widget. The position of child widgets within a parent widget can be changed manually using the setGeometry() method, or automatically using layout manager classes (see Chapter 6). To display a widget on screen, call show(); to hide it, call hide().

Always call show() after creating a top-level widget

Keep in mind that after creating a top-level widget, you must call show() to make it visible. Otherwise, both the window itself and all child widgets will remain invisible.

The QWidget class and most of its subclasses have a constructor with two parameters:

QWidget(QWidget* pwgtParent = nullptr, Qt::WindowFlags f = Qt::WindowFlags())

As the signature shows, it is not required to pass arguments to the constructor since both parameters have default values. This means that if the constructor is called without arguments, the created widget becomes a top-level widget. The second parameter, Qt::WindowFlags, is used to set window properties and allows you to control the appearance and display behavior of a window (for example, keeping a window on top of other windows). To change the appearance of a window, pass modifier values combined with the window type (Fig. 5.2) using the bitwise OR operator | as the second constructor parameter. The same result can be achieved by calling setWindowFlags(). For example:

wgt.setWindowFlags(Qt::Window | Qt::WindowTitleHint |
                   Qt::WindowStaysOnTopHint);

Fig. 5.2 shows some examples of what these values produce.

Qt6 top-level widget window styles set with WindowFlags
Fig. 5.2. Appearance of top-level widget windows

 

Keep a window always in the foreground!

The Qt::WindowStaysOnTopHint value does not change the visual appearance of a window; it only hints to the window manager that the window should remain in the foreground and should not be obscured by other windows.

The setWindowTitle() slot sets the text displayed in the window title bar. This is meaningful only for top-level widgets. For example:

wgt.setWindowTitle("My Window");

The setEnabled() slot sets a widget to either the enabled or disabled state. Passing true enables the widget; passing false disables it. To check the current state of a widget, call isEnabled().

When creating custom widget classes, it is important that the widget is capable of handling events (see Chapter 14). For example, to handle mouse events you must override at least one of the following methods: mousePressEvent(), mouseMoveEvent(), mouseReleaseEvent(), or mouseDoubleClickEvent().

Widget Size and Coordinates

A widget occupies a rectangular region (Fig. 5.3). There are several methods available for querying a widget’s position and dimensions. The methods size(), height(), and width() return the widget’s dimensions. While height() and width() return integer values for the height and width respectively, size() returns an object of type QSize (see Chapter 17) that stores both the width and height of the widget.

The methods x(), y(), and pos() are used to determine the widget’s coordinates. The first two return integer coordinate values along the X and Y axes, while pos() returns an object of type QPoint (see Chapter 17) that stores both coordinates.

The geometry() method returns an object of type QRect (see Chapter 17) that describes the position and dimensions of the widget.

A widget’s position can be changed using move(), and its size can be changed using resize(). For example:

pwgt->move(5, 5);
pwgt->resize(260, 330);
Diagram of a Qt widget's position and size within its parent
Fig. 5.3. A widget within a screen region (or within a parent widget)

To change both position and size simultaneously, call setGeometry(). The first parameter specifies the X coordinate of the widget’s top-left corner, the second specifies the Y coordinate, the third sets the width, and the fourth sets the height. For example, the following call is equivalent to the two calls to move() and resize() shown above:

pwgt->setGeometry(5, 5, 260, 330);

The Backing Store Mechanism

The Backing Store technique involves keeping raster images for all widgets in a window stored in memory at all times. This allows the required portion of the stored region to be drawn very quickly, without triggering a paint event (PaintEvent) from the system — regardless of how complex the widget’s rendering logic is.

A paint event for a widget is triggered only when truly necessary — for example, when the background changes. This technique can significantly improve rendering performance.

Setting a Widget Background

A widget can be given a background, which may be either a solid color or a raster image. To fill with a solid color or a raster image, you first need to create a palette object (see Chapter 13) and then apply it to the widget by calling setPalette().

Widgets have an important property called autoFillBackground, which is false by default. As a result, child widgets do not have their backgrounds painted and are therefore invisible. Setting this property to true instructs the widget to fill its background automatically, making it visible. For example:

wgt.setAutoFillBackground(true);

Listing 5.1 creates a top-level widget wgt, which is passed as a parent to two other widgets (pointers pwgt1 and pwgt2).

Listing 5.1. Creating a top-level widget (file main.cpp)

#include <QApplication>
#include <QWidget>
#include <QPixmap>

int main(int argc, char** argv)
{
    QApplication app(argc, argv);
    QWidget      wgt;

    auto* pwgt1 = new QWidget(&wgt);
    QPalette pal1;
    pal1.setColor(pwgt1->backgroundRole(), Qt::blue);
    pwgt1->setPalette(pal1);
    pwgt1->resize(128, 128);
    pwgt1->move(40, 40);
    pwgt1->setAutoFillBackground(true);

    auto* pwgt2 = new QWidget(&wgt);
    QPalette pal2;
    pal2.setBrush(pwgt2->backgroundRole(), QBrush(QPixmap(":/crocodile.jpg")));
    pwgt2->setPalette(pal2);
    pwgt2->resize(128, 128);
    pwgt2->move(90, 90);
    pwgt2->setAutoFillBackground(true);

    wgt.resize(250, 250);
    wgt.show();

    return app.exec();
}

The first child widget is given a palette object via setPalette() that sets a solid background color (blue). After resizing it with resize() and repositioning it within the parent widget using move(), the setAutoFillBackground() method sets the autoFillBackground property to true so the widget becomes visible.

Two QWidget children, one with a solid blue background and one with an image background
Fig. 5.4. Widgets with backgrounds

The same operations are performed on the second child widget (pointer pwgt2). The difference is that the second widget uses a raster image loaded from stone.jpg as its background, applied via the palette object pal2.

As a result, one widget is filled with a solid color and the other with a raster image (Fig. 5.4).

Changing the Mouse Cursor

The mouse cursor class QCursor is defined in the QCursor header file. The cursor is a small raster image that indicates the current mouse position on screen. Its appearance may change depending on its location. In most cases it is an arrow, but when hovering over a window border it may change to a double-headed arrow, indicating that the window can be resized.

You can set the cursor image by calling setCursor() and passing one of the CursorShape values from the Qt namespace, as listed in Table 5.1.

Value Description Appearance
ArrowCursor Standard arrow cursor. Appears over most widgets. Used for pointing, selecting, or moving objects. ArrowCursor
UpArrowCursor Upward-pointing arrow. Application depends on context. UpArrowCursor
CrossCursor Crosshair cursor. Used to select rectangular regions. May appear over any widget that supports this operation. CrossCursor
WaitCursor Wait cursor. Appears over any widget or position while a background operation is in progress. WaitCursor
IbeamCursor I-beam text cursor represented as a vertical line. Appears over text to indicate editing, selection, or insertion. IbeamCursor
PointingHandCursor Hand cursor. Appears over hyperlinks. PointingHandCursor
ForbiddenCursor “No entry” cursor. Appears over a drop target during drag-and-drop operations to indicate that the target cannot accept the dragged item. ForbiddenCursor
WhatsThisCursor Question-mark cursor. Appears over most widgets to trigger context-sensitive help. WhatsThisCursor
SizeVerCursor Vertical resize cursor. Appears over a vertically resizable window border. SizeVerCursor
SizeHorCursor Horizontal resize cursor. Appears over a horizontally resizable window border. SizeHorCursor
SizeBDiagCursor Diagonal resize cursor (one direction). Appears over a diagonally resizable window border. SizeBDiagCursor
SizeFDiagCursor Diagonal resize cursor (other direction). Appears over a diagonally resizable window border. SizeFDiagCursor
SizeAllCursor Move cursor. Indicates that the window is ready to be moved. SizeAllCursor
SplitVCursor Vertical split cursor. Appears over the border between two vertically split widgets. See Chapter 6 for details on widget splitters. SplitVCursor
SplitHCursor Horizontal split cursor. Appears over the border between two horizontally split widgets. See Chapter 6 for details on widget splitters. SplitHCursor
OpenHandCursor Open hand cursor. Indicates that content in the viewport can be panned. OpenHandCursor
ClosedHandCursor Closed hand cursor. Indicates that content in the viewport is being panned. ClosedHandCursor
BlankCursor Blank cursor. Indicates that mouse input is not available.

Table 5.1. Qt namespace CursorShape values

The QCursor class provides a pos() method that returns the current cursor position relative to the top-left corner of the screen. The cursor can be moved programmatically using setPos().

Setting the cursor for the entire application

Calling the static method QGuiApplication::setOverrideCursor() sets the cursor image for the entire application. This can be useful, for example, to inform the user that the application is performing an intensive, long-running operation and cannot respond to input. During this time, all widgets should display a wait cursor, which is accomplished by calling QGuiApplication::setOverrideCursor(Qt::WaitCursor). Once the application is ready to accept user input again, calling the static method QGuiApplication::restoreOverrideCursor() restores the cursor to its previous appearance.

To create a custom cursor image, you need two QBitmap raster images of the same size, one of which serves as a bitmask. Where the mask has the color color1, the cursor image is drawn; where the mask has the color color0, the image is transparent.

A simpler approach is to use a QPixmap object. The output of the program in Listing 5.2 (shown in Fig. 5.5) demonstrates this capability.

Qt widget displaying a custom mouse cursor loaded from a QPixmap
Fig. 5.5. Using a custom image for the mouse cursor

Listing 5.2. Changing the mouse cursor

#include <QApplication>
#include <QWidget>

int main(int argc, char** argv)
{
    QApplication app(argc, argv);
    QWidget      wgt;
    QPixmap      pix(":/cursor.png");
    QCursor      cur(pix);

    wgt.setCursor(cur);
    wgt.resize(180, 100);
    wgt.show();

    return app.exec();
}

In Listing 5.2, a widget wgt is created first, then a QPixmap object pix whose constructor receives the name of a PNG file containing both the raster image and its bitmask (a detailed description of this format and the capabilities of the QPixmap class can be found in Chapter 19). To create the cursor, the pixmap object is passed to the QCursor constructor, and the resulting cursor is applied to the widget via setCursor().

Widget Stack

The QStackedWidget class inherits from QFrame and represents a widget that shows only one of its children at a time.

Widgets are added to the stack using addWidget(), which accepts a pointer to a widget and returns an integer identifier assigned to that widget. Widgets are removed from the stack by calling removeWidget() with a pointer to the widget.

To make a specific widget visible, pass a pointer to it to the setCurrentWidget() slot, or pass its identifier to the setCurrentIndex() slot. A widget’s identifier can be retrieved by calling indexOf() with a pointer to the widget.

Frames

The QFrame class inherits from QWidget and extends it with the ability to render a frame border. It serves as the base class for a large number of widget classes (see Fig. 5.1). The frame style can be customized using setFrameStyle(), which accepts a combination of shape flags and shadow flags joined with the bitwise OR operator |.

There are three shadow flags (Table 5.2): QFrame::Raised, QFrame::Plain, and QFrame::Sunken. These produce a beveled or flat visual effect on the frame.

Flags Appearance
Box | Plain Box | Plain
Box | Raised Box | Raised
Box | Sunken Box | Sunken
Panel | Plain Panel | Plain
Panel | Raised Panel | Raised
Panel | Sunken Panel | Sunken
WinPanel | Plain WinPanel | Plain
WinPanel | Raised WinPanel | Raised
WinPanel | Sunken WinPanel | Sunken
HLine | Plain HLine | Plain
HLine | Raised HLine | Raised
HLine | Sunken HLine | Sunken
VLine | Plain VLine | Plain
VLine | Raised VLine | Raised
VLine | Sunken VLine | Sunken
StyledPanel | Plain StyledPanel | Plain
StyledPanel | Raised StyledPanel | Raised
StyledPanel | Sunken StyledPanel | Sunken

Table 5.2. Frame examples

Five primary shape flags are available for defining the frame appearance (see Table 5.2): QFrame::Box, QFrame::Panel, QFrame::WinPanel, QFrame::HLine, and QFrame::VLine. If no frame should be displayed at all, pass QFrame::NoFrame to setFrameStyle().

The setContentsMargin() method of the QWidget class sets the spacing between the frame and the widget content. The setLineWidth() and setMidLineWidth() methods of QFrame control the thickness of the frame itself:

auto* pfrm = new QFrame;
pfrm->setFrameStyle(QFrame::Box | QFrame::Sunken);
pfrm->setLineWidth(3);

In this example, a frame widget is created, the desired frame style is set using setFrameStyle(), and the frame thickness is set using setLineWidth().

Scroll Area Widget

The base class for scroll areas, QAbstractScrollArea, inherits from QFrame and provides a viewport for viewing only a portion of its content. The scroll area widget itself is implemented by the QScrollArea class.

This widget can contain child widgets, and if any of them extends beyond the boundaries of the viewport, horizontal and/or vertical scrollbars appear automatically. These scrollbars allow the user to scroll different parts of the widget into the visible area.

If you want the scrollbars to always be visible, pass Qt::ScrollBarAlwaysOn to the scrollbar policy methods. For example:

QScrollArea sa;
sa.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
sa.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);

As shown in Fig. 5.6, the scroll area widget is a composite of several widgets working together. Pointers to these sub-widgets can be obtained via the methods labeled in the figure. The viewport widget is accessible via QAbstractScrollArea::viewport(). The methods verticalScrollBar() and horizontalScrollBar() return pointers to the vertical and horizontal QScrollBar widgets respectively. The cornerWidget() method returns a pointer to the widget occupying the bottom-right corner.

Diagram of QScrollArea internal structure: viewport, scrollbars, corner widget
Fig. 5.6. Structure of the scroll area widget

A widget can be placed inside the scroll area by calling setWidget() with a pointer to it. This operation automatically makes the passed widget a child of the viewport widget. The pointer to the currently set widget can always be retrieved using widget(). A widget is removed from a QScrollArea by calling removeChild().

Qt application window scrolling a large image inside a QScrollArea
Fig. 5.7. Scroll area widget example

Fig. 5.7 shows the output of the scroll area example program (Listing 5.3). The program allows the user to scroll different parts of an image into the visible window area.

Listing 5.3. Using a scroll area (file main.cpp)

#include <QApplication>
#include <QScrollArea>
#include <QWidget>

int main(int argc, char** argv)
{
    QApplication app(argc, argv);
    QScrollArea  sa;

    auto*   pwgt = new QWidget;
    QPixmap pix(":/img.jpg");

    QPalette pal;
    pal.setBrush(pwgt->backgroundRole(), QBrush(pix));
    pwgt->setPalette(pal);
    pwgt->setAutoFillBackground(true);
    pwgt->setFixedSize(pix.width(), pix.height());

    sa.setWidget(pwgt);
    sa.resize(350, 150);
    sa.show();

    return app.exec();
}

In Listing 5.3, the scroll area widget sa, a plain widget (pointer pwgt), and a pixmap object pix are created first. The pixmap is loaded from img.jpg and set as the widget’s background via setPalette(). The setAutoFillBackground() call enables automatic background painting to make the widget visible. The widget’s size is matched to the pixmap’s dimensions using setFixedSize(). Finally, the scroll area sa places the created widget inside its viewport by calling setWidget().

Summary

The central concept in building a user interface is the widget (control element). The QWidget class is the base for all controls. Virtually everything that makes up the user interface in Qt applications consists of objects of class QWidget and its subclasses.

There is no distinction between container widgets and regular widgets — any widget can serve as a container for other widgets. The primary operations on widgets, beyond show and hide, are the methods for resizing and repositioning them.

The position and size of widgets within a parent widget can be set automatically when layout manager classes are used.

Top-level widgets have their own window, which can be decorated in various ways — for example, by changing the window frame style.

Mouse cursors can be set per widget. Qt provides a set of predefined cursor images that can be used directly, and it is also possible to create custom cursors from raster images.

The QFrame class inherits from QWidget and represents a rectangle with a configurable border style.

The QStackedWidget class shows only one of its children at any given time. This is useful when multiple widgets exist but only one should be visible at a time.

The scroll area widget provides a viewport for displaying only part of its content. It is used to display content whose dimensions exceed the available viewport area.


Unlock the full potential of this chapter! Access the supplementary materials prepared for it, ask a question, share your experience, or join the discussion at: https://qt-book.com/05-69-en/

Leave a Reply

Your email address will not be published. Required fields are marked *