Beispiel für Flow-Layout

Zeigt, wie man Widgets für verschiedene Fenstergrößen anordnet.

Flow Layout implementiert ein Layout, das verschiedene Fenstergrößen handhabt. Die Platzierung der Widgets ändert sich in Abhängigkeit von der Breite des Anwendungsfensters.

Screenshot of the Flow Layout example

Die Flowlayout-Klasse verwendet hauptsächlich QLayout und QWidgetItem, während das Window QWidget und QLabel verwendet.

Weitere Informationen finden Sie auf der Seite Layoutverwaltung.

Ausführen des Beispiels

Zum Ausführen des Beispiels von Qt Creatorzu starten, öffnen Sie den Modus Welcome und wählen Sie das Beispiel unter Examples aus. Weitere Informationen finden Sie unter Erstellen und Ausführen eines Beispiels.

FlowLayout-Klassendefinition

Die Klasse FlowLayout erbt von QLayout. Sie ist eine benutzerdefinierte Layoutklasse, die ihre untergeordneten Widgets horizontal und vertikal anordnet.

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;
};

Wir reimplementieren Funktionen, die wir von QLayout geerbt haben. Diese Funktionen fügen dem Layout Elemente hinzu und behandeln ihre Ausrichtung und Geometrie.

Wir deklarieren auch zwei private Methoden, doLayout() und smartSpacing(). doLayout() ordnet die Layoutelemente an, während die Funktion smartSpacing() den Abstand zwischen ihnen berechnet.

Implementierung der FlowLayout-Klasse

Wir beginnen mit einem Blick auf den Konstruktor:

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);
}

Im Konstruktor rufen wir setContentsMargins() auf, um den linken, oberen, rechten und unteren Rand festzulegen. Standardmäßig verwendet QLayout die vom aktuellen Stil bereitgestellten Werte (siehe QStyle::PixelMetric).

FlowLayout::~FlowLayout()
{
    QLayoutItem *item;
    while ((item = takeAt(0)))
        delete item;
}

In diesem Beispiel reimplementieren wir addItem(), das eine rein virtuelle Funktion ist. Bei der Verwendung von addItem() wird das Eigentum an den Layoutelementen an das Layout übertragen, und es liegt daher in der Verantwortung des Layouts, diese zu löschen.

void FlowLayout::addItem(QLayoutItem *item)
{
    itemList.append(item);
}

addItem() ist implementiert, um dem Layout Elemente hinzuzufügen.

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);
    }
}

Wir implementieren horizontalSpacing() und verticalSpacing(), um die Abstände zwischen den Widgets innerhalb des Layouts zu ermitteln. Wenn der Wert kleiner oder gleich 0 ist, wird dieser Wert verwendet. Wenn nicht, wird smartSpacing() aufgerufen, um den Abstand zu berechnen.

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;
}

Anschließend implementieren wir count(), um die Anzahl der Elemente im Layout zu ermitteln. Um in der Liste der Elemente zu navigieren, verwenden wir itemAt() und takeAt(), um Elemente aus der Liste zu entfernen und zurückzugeben. Wenn ein Element entfernt wird, werden die verbleibenden Elemente neu nummeriert. Alle drei Funktionen sind rein virtuelle Funktionen von QLayout.

Qt::Orientations FlowLayout::expandingDirections() const
{
    return { };
}

expandingDirections() gibt die Qt::Orientations zurück, in denen das Layout mehr Platz als seine sizeHint() nutzen kann.

bool FlowLayout::hasHeightForWidth() const
{
    return true;
}

int FlowLayout::heightForWidth(int width) const
{
    int height = doLayout(QRect(0, 0, width, 0), true);
    return height;
}

Zur Anpassung an Widgets, deren Höhe von der Breite abhängt, implementieren wir heightForWidth(). Die Funktion hasHeightForWidth() wird verwendet, um auf diese Abhängigkeit zu testen, und heightForWidth() gibt die Breite an doLayout() weiter, das wiederum die Breite als Argument für das Layout-Rect verwendet, d. h. die Grenzen, in denen die Elemente angeordnet werden. Dieses Rect enthält nicht den 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 : std::as_const(itemList))
        size = size.expandedTo(item->minimumSize());

    const QMargins margins = contentsMargins();
    size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom());
    return size;
}

setGeometry() wird normalerweise für das eigentliche Layout verwendet, d. h. für die Berechnung der Geometrie der Elemente des Layouts. In diesem Beispiel wird doLayout() aufgerufen und das Layout-Rect übergeben.

sizeHint() gibt die bevorzugte Größe des Layouts zurück und minimumSize() gibt die Mindestgröße des Layouts zurück.

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() behandelt das Layout, wenn horizontalSpacing() oder verticalSpacing() nicht den Standardwert zurückgeben. Es verwendet getContentsMargins(), um den für die Layoutelemente verfügbaren Bereich zu berechnen.

    for (QLayoutItem *item : std::as_const(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);

Anschließend wird der richtige Abstand für jedes Widget im Layout festgelegt, basierend auf dem aktuellen Stil.

        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;
}

Die Position der einzelnen Elemente im Layout wird dann berechnet, indem die Elementbreite und die Zeilenhöhe zu den anfänglichen x- und y-Koordinaten addiert werden. Auf diese Weise lässt sich herausfinden, ob das nächste Element in die aktuelle Zeile passt oder ob es in die nächste Zeile verschoben werden muss. Außerdem wird die Höhe der aktuellen Zeile anhand der Höhe des Widgets ermittelt.

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() dient dazu, die Standardabstände für die Layouts der obersten Ebene oder die Sublayouts zu ermitteln. Der Standardabstand für Layouts der obersten Ebene, wenn das übergeordnete Layout ein QWidget ist, wird durch Abfrage des Stils ermittelt. Der Standardabstand für Sublayouts, wenn das übergeordnete Layout ein QLayout ist, wird durch die Abfrage des Abstandes des übergeordneten Layouts bestimmt.

Beispielprojekt @ code.qt.io

© 2025 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.