Understanding the QWidget layout flow
Nov 21, 2013 · 3-minute read
When layouts in a UI are not behaving as expected or performance is poor, it can be helpful to have a mental model of the layout process in order to know where to start debugging. For web browsers there are some good resources which provide a description of the process at different levels. The layout documentation for Qt describes the various layout facilities that are available but I haven’t found a detailed description of the flow, so this is my attempt to explain what happens when a layout is triggered that ultimately ends up with the widgets being resized and repositioned appropriately.
-
A widget’s contents are modified in some way that require a layout update. Such changes can include:
- Changes to the content of the widget (eg. the text in a label, content margins being altered)
- Changes to the
sizePolicy()
of the widget - Changes to the layout() of the widget, such as new child widgets being added or removed
-
The widget calls
QWidget::updateGeometry()
which then performs several steps to trigger a layout:- It invalidates any cached size information for the QWidgetItem associated with the widget in the parent layout.
- It recursively climbs up the widget tree (first to the parent widget, then the grandparent and so on), invalidating that widget’s layout. The process stops when we reach a widget that is a top level window or doesn’t have its own layout - we’ll call this widget the top-level widget, though it might not actually be a window.
-
If the top-level widget is not yet visible, then the process stops and layout is deferred until the widget is due to be shown.
-
If the top-level widget is shown, a LayoutRequest event is posted asynchronously to the top-level widget, so a layout will be performed on the next pass through the event loop.
-
If multiple layout requests are posted to the same top-level widget during a pass through the event loop, they will get compressed into a single layout request. This is similar to the way that multiple
QWidget::update()
requests are compressed into a single paint event. -
The top-level widget receives the LayoutRequest event on the next pass through the event loop. This can then be handled in one of two ways:
- If the widget has a layout, the layout will intercept the LayoutRequest event using an event filter and handle it by calling QLayout::activate()
- If the widget does not have a layout, it may handle the LayoutRequest event itself and manually set the geometry of its children.
-
When the layout is activated, it first sets the fixed, minimum and/or maximum size constraints of the widget depending on QLayout::sizeConstraint(), using the values calculated by QLayout::minimumSize(), maximumSize() and sizeHint(). These functions will recursively proceed down the layout tree to determine the constraints for each item and produce a final size constraint for the whole layout. This may or may not alter the current size of the widget.
-
The layout is then asked to resize its contents to fit the current size of the widget using QLayout::setGeometry(widget->size()). The specific implementation of the layout - whether it is a box layout, grid layout or something else then lays out its child items to fit this new size.
-
For each item in the layout, the QLayoutItem::setGeometry() implementation will typically ask the item for various size parameters (minimum size, maximum size, size hint, height for width) and then decide upon a final size and position for the item. It will then invoke QLayoutItem::setGeometry() to update the position and size of the widget.
-
If the layout item is itself a layout or a widget, steps 5-6 proceed recursively down the tree, updating all of the items whose constraints have been modified.