Flow Layout Example¶
Shows how to arrange widgets for different window sizes.
Flow Layout implements a layout that handles different window sizes. The widget placement changes depending on the width of the application window.
The Flowlayout class mainly uses
QLayout
andQWidgetItem
, while the Window usesQWidget
andQLabel
.For more information, visit the Layout Management page.
Running the Example¶
To run the example from Qt Creator , open the Welcome mode and select the example from Examples . For more information, visit Building and Running an Example.
FlowLayout Class Definition¶
The
FlowLayout
class inheritsQLayout
. It is a custom layout class that arranges its child widgets horizontally and vertically.class FlowLayout : public QLayout { public: explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1); explicit FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1); ~FlowLayout(); void addItem(QLayoutItem *item) override; int horizontalSpacing() const; int verticalSpacing() const; Qt::Orientations expandingDirections() const override; bool hasHeightForWidth() const override; int heightForWidth(int) const override; int count() const override; QLayoutItem *itemAt(int index) const override; QSize minimumSize() const override; void setGeometry(const QRect &rect) override; QSize sizeHint() const override; QLayoutItem *takeAt(int index) override; private: int doLayout(const QRect &rect, bool testOnly) const; int smartSpacing(QStyle::PixelMetric pm) const; QList<QLayoutItem *> itemList; int m_hSpace; int m_vSpace; };We reimplement functions inherited from
QLayout
. These functions add items to the layout and handle their orientation and geometry.We also declare two private methods,
doLayout()
andsmartSpacing()
.doLayout()
lays out the layout items, while thesmartSpacing()
function calculates the spacing between them.
FlowLayout Class Implementation¶
We start off by looking at the constructor:
FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing) { setContentsMargins(margin, margin, margin, margin); } FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing) : m_hSpace(hSpacing), m_vSpace(vSpacing) { setContentsMargins(margin, margin, margin, margin); }In the constructor we call
setContentsMargins()
to set the left, top, right and bottom margin. By default,QLayout
uses values provided by the current style (seePixelMetric
).FlowLayout::~FlowLayout() { QLayoutItem *item; while ((item = takeAt(0))) delete item; }In this example we reimplement
addItem()
, which is a pure virtual function. When usingaddItem()
the ownership of the layout items is transferred to the layout, and it is therefore the layout’s responsibility to delete them.void FlowLayout::addItem(QLayoutItem *item) { itemList.append(item); }
addItem()
is implemented to add items to the layout.int FlowLayout::horizontalSpacing() const { if (m_hSpace >= 0) { return m_hSpace; } else { return smartSpacing(QStyle::PM_LayoutHorizontalSpacing); } } int FlowLayout::verticalSpacing() const { if (m_vSpace >= 0) { return m_vSpace; } else { return smartSpacing(QStyle::PM_LayoutVerticalSpacing); } }We implement
horizontalSpacing()
andverticalSpacing()
to get hold of the spacing between the widgets inside the layout. If the value is less than or equal to 0, this value will be used. If not,smartSpacing()
will be called to calculate the spacing.int FlowLayout::count() const { return itemList.size(); } QLayoutItem *FlowLayout::itemAt(int index) const { return itemList.value(index); } QLayoutItem *FlowLayout::takeAt(int index) { if (index >= 0 && index < itemList.size()) return itemList.takeAt(index); return nullptr; }We then implement
count()
to return the number of items in the layout. To navigate the list of items we useitemAt()
and takeAt() to remove and return items from the list. If an item is removed, the remaining items will be renumbered. All three functions are pure virtual functions fromQLayout
.Qt::Orientations FlowLayout::expandingDirections() const { return { }; }
expandingDirections()
returns theOrientation
s in which the layout can make use of more space than itssizeHint()
.bool FlowLayout::hasHeightForWidth() const { return true; } int FlowLayout::heightForWidth(int width) const { int height = doLayout(QRect(0, 0, width, 0), true); return height; }To adjust to widgets of which height is dependent on width, we implement
heightForWidth()
. The functionhasHeightForWidth()
is used to test for this dependency, andheightForWidth()
passes the width on todoLayout()
which in turn uses the width as an argument for the layout rect, i.e., the bounds in which the items are laid out. This rect does not include the layout margin().void FlowLayout::setGeometry(const QRect &rect) { QLayout::setGeometry(rect); doLayout(rect, false); } QSize FlowLayout::sizeHint() const { return minimumSize(); } QSize FlowLayout::minimumSize() const { QSize size; for (const QLayoutItem *item : qAsConst(itemList)) size = size.expandedTo(item->minimumSize()); const QMargins margins = contentsMargins(); size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom()); return size; }
setGeometry()
is normally used to do the actual layout, i.e., calculate the geometry of the layout’s items. In this example, it callsdoLayout()
and passes the layout rect.
sizeHint()
returns the preferred size of the layout andminimumSize()
returns the minimum size of the layout.int FlowLayout::doLayout(const QRect &rect, bool testOnly) const { int left, top, right, bottom; getContentsMargins(&left, &top, &right, &bottom); QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); int x = effectiveRect.x(); int y = effectiveRect.y(); int lineHeight = 0;
doLayout()
handles the layout ifhorizontalSpacing()
orverticalSpacing()
don’t return the default value. It usesgetContentsMargins()
to calculate the area available to the layout items.for (QLayoutItem *item : qAsConst(itemList)) { const QWidget *wid = item->widget(); int spaceX = horizontalSpacing(); if (spaceX == -1) spaceX = wid->style()->layoutSpacing( QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); int spaceY = verticalSpacing(); if (spaceY == -1) spaceY = wid->style()->layoutSpacing( QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);It then sets the proper amount of spacing for each widget in the layout, based on the current style.
int nextX = x + item->sizeHint().width() + spaceX; if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) { x = effectiveRect.x(); y = y + lineHeight + spaceY; nextX = x + item->sizeHint().width() + spaceX; lineHeight = 0; } if (!testOnly) item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); x = nextX; lineHeight = qMax(lineHeight, item->sizeHint().height()); } return y + lineHeight - rect.y() + bottom; }The position of each item in the layout is then calculated by adding the items width and the line height to the initial x and y coordinates. This in turn lets us find out whether the next item will fit on the current line or if it must be moved down to the next. We also find the height of the current line based on the widgets height.
int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const { QObject *parent = this->parent(); if (!parent) { return -1; } else if (parent->isWidgetType()) { QWidget *pw = static_cast<QWidget *>(parent); return pw->style()->pixelMetric(pm, nullptr, pw); } else { return static_cast<QLayout *>(parent)->spacing(); } }
smartSpacing()
is designed to get the default spacing for either the top-level layouts or the sublayouts. The default spacing for top-level layouts, when the parent is aQWidget
, will be determined by querying the style. The default spacing for sublayouts, when the parent is aQLayout
, will be determined by querying the spacing of the parent layout.
© 2022 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.