Sur cette page

Exemple de mise en page fluide

Montre comment organiser les widgets en fonction des différentes tailles de fenêtre.

La mise en page « Flow Layout » implémente une mise en page qui s'adapte aux différentes tailles de fenêtre. Le placement des widgets varie en fonction de la largeur de la fenêtre de l'application.

Capture d'écran de l'exemple de mise en page « Flow Layout »

La classe FlowLayout utilise principalement QLayout et QWidgetItem, tandis que la classe Window utilise QWidget et QLabel.

Pour plus d’informations, consultez la page Gestion des mises en page.

Exécution de l’exemple

Vous pouvez exécuter l’exemple à partir de :

Définition de la classe FlowLayout

La classe FlowLayout hérite de QLayout. Il s'agit d'une classe de mise en page personnalisée qui organise ses widgets enfants horizontalement et verticalement.

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

Nous réimplémentons les fonctions héritées de ` QLayout`. Ces fonctions ajoutent des éléments à la mise en page et gèrent leur orientation et leur géométrie.

Nous déclarons également deux méthodes privées : ` doLayout() ` et ` smartSpacing()`. La méthode ` doLayout() ` organise les éléments de la mise en page, tandis que la fonction ` smartSpacing() ` calcule l'espacement entre eux.

Implémentation de la classe FlowLayout

Commençons par examiner le constructeur :

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

Dans le constructeur, nous appelons ` setContentsMargins() ` pour définir les marges gauche, supérieure, droite et inférieure. Par défaut, ` QLayout ` utilise les valeurs fournies par le style actuel (voir ` QStyle::PixelMetric`).

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

Dans cet exemple, nous réimplémentons ` addItem()`, qui est une fonction virtuelle pure. Lorsque l’on utilise ` addItem() `, la propriété des éléments de mise en page est transférée à la mise en page ; c’est donc à cette dernière qu’il incombe de les supprimer.

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

addItem() est implémentée pour ajouter des éléments à la mise en page.

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

Nous implémentons ` horizontalSpacing() ` et ` verticalSpacing() ` pour déterminer l’espacement entre les widgets à l’intérieur de la mise en page. Si la valeur est inférieure ou égale à 0, cette valeur sera utilisée. Dans le cas contraire, ` smartSpacing() ` sera appelée pour calculer l’espacement.

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

Nous implémentons ensuite ` count() ` pour renvoyer le nombre d’éléments dans la disposition. Pour parcourir la liste des éléments, nous utilisons ` itemAt() ` et `takeAt()` pour supprimer et renvoyer des éléments de la liste. Si un élément est supprimé, les éléments restants seront renumérotés. Ces trois fonctions sont des fonctions virtuelles pures issues de ` QLayout`.

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

expandingDirections() renvoie les valeurs d’ Qt::Orientations dans lesquelles la disposition peut utiliser plus d’espace que sa largeur ( sizeHint()).

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

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

Pour s’adapter aux widgets dont la hauteur dépend de la largeur, nous implémentons ` heightForWidth()`. La fonction ` hasHeightForWidth() ` est utilisée pour vérifier cette dépendance, et ` heightForWidth() ` transmet la largeur à ` doLayout() `, qui utilise à son tour cette largeur comme argument pour le rectangle de mise en page (layout rect), c’est-à-dire les limites dans lesquelles les éléments sont disposés. Ce rectangle n’inclut pas la marge de mise en page (`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() est normalement utilisée pour effectuer la mise en page proprement dite, c’est-à-dire pour calculer la géométrie des éléments de la mise en page. Dans cet exemple, elle appelle doLayout() et lui transmet le rectangle de mise en page.

sizeHint() renvoie la taille préférée de la mise en page et ` minimumSize() ` renvoie la taille minimale de la mise en page.

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() gère la mise en page si ` horizontalSpacing() ` ou ` verticalSpacing() ` ne renvoient pas la valeur par défaut. Elle utilise ` getContentsMargins() ` pour calculer la surface disponible pour les éléments de la mise en page.

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

Elle définit ensuite l’espacement approprié pour chaque widget de la mise en page, en fonction du style actuel.

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

La position de chaque élément dans la mise en page est ensuite calculée en ajoutant la largeur de l’élément et la hauteur de ligne aux coordonnées x et y initiales. Cela nous permet alors de déterminer si l’élément suivant tiendra sur la ligne actuelle ou s’il doit être déplacé vers la ligne suivante. Nous déterminons également la hauteur de la ligne actuelle en fonction de la hauteur des widgets.

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() est conçu pour obtenir l’espacement par défaut soit pour les mises en page de niveau supérieur, soit pour les sous-mises en page. L’espacement par défaut des mises en page de niveau supérieur, lorsque le parent est un ` QWidget`, sera déterminé en interrogeant le style. L’espacement par défaut des sous-mises en page, lorsque le parent est un ` QLayout`, sera déterminé en interrogeant l’espacement de la mise en page parente.

Exemple de projet @ code.qt.io

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