Sur cette page

Gestion de la mise en page

Le système de disposition de Qt Widgets fournit un moyen simple et puissant d'arranger automatiquement les widgets enfants à l'intérieur d'un widget pour s'assurer qu'ils font bon usage de l'espace disponible.

Introduction

Qt Widgets comprend un ensemble de classes de gestion de la disposition qui sont utilisées pour décrire la façon dont les widgets sont disposés dans l'interface utilisateur d'une application. Ces dispositions positionnent et redimensionnent automatiquement les widgets lorsque la quantité d'espace disponible pour eux change, garantissant ainsi une disposition cohérente et une utilisation optimale de l'interface utilisateur dans son ensemble.

Toutes les sous-classes de QWidget peuvent utiliser des dispositions pour gérer leurs enfants. La fonction QWidget::setLayout() applique une disposition à un widget. Lorsqu'une disposition est définie sur un widget de cette manière, elle prend en charge les tâches suivantes :

  • Positionnement des widgets enfants
  • Tailles par défaut raisonnables pour les fenêtres
  • Tailles minimales raisonnables pour les fenêtres
  • Gestion du redimensionnement
  • Mises à jour automatiques en cas de modification du contenu :
    • Taille de la police, texte ou autre contenu des widgets enfants
    • Masquer ou afficher un widget enfant
    • Suppression des widgets enfants

Classes de présentation de Qt

Les classes d'agencement de Qt Designer ont été conçues pour un code C++ écrit à la main, permettant de spécifier les mesures en pixels pour plus de simplicité, de sorte qu'elles sont faciles à comprendre et à utiliser. Le code généré pour les formulaires créés à l'aide de Qt Widgets Designer utilise également les classes de présentation. Qt Widgets Designer est utile pour expérimenter la conception d'un formulaire, car il évite le cycle de compilation, de liaison et d'exécution généralement impliqué dans le développement d'une interface utilisateur.

QBoxLayout

Aligne les widgets enfants horizontalement ou verticalement.

QButtonGroup

Conteneur permettant d'organiser des groupes de boutons.

QFormLayout

Gère les formulaires de widgets de saisie et leurs étiquettes associées

QGraphicsAnchor

Représente une ancre entre deux éléments dans un QGraphicsAnchorLayout

QGraphicsAnchorLayout

Disposition permettant d'ancrer des widgets ensemble dans une vue graphique

QGridLayout

Disposition des widgets dans une grille

QGroupBox

Cadre de groupe avec un titre

QHBoxLayout

Aligne les widgets horizontalement

QLayout

Classe de base des gestionnaires de géométrie

QLayoutItem

Élément abstrait manipulé par un QLayout

QSizePolicy

Attribut de mise en page décrivant la politique de redimensionnement horizontal et vertical

QSpacerItem

Espace vide dans une mise en page

QStackedLayout

Pile de widgets où un seul widget est visible à la fois

QStackedWidget

Pile de widgets où un seul widget est visible à la fois

QVBoxLayout

Aligne les widgets verticalement

QWidgetItem

Élément de mise en page représentant un widget

Dispositions horizontales, verticales, en grille et en formulaire

Le moyen le plus simple de donner une bonne présentation à vos widgets est d'utiliser les gestionnaires de présentation intégrés : QHBoxLayout, QVBoxLayout, QGridLayout, et QFormLayout. Ces classes héritent de QLayout, qui dérive à son tour de QObject (et non de QWidget). Elles s'occupent de la gestion de la géométrie pour un ensemble de widgets. Pour créer des présentations plus complexes, vous pouvez imbriquer les gestionnaires de présentation les uns dans les autres.

  • Un QHBoxLayout présente les widgets dans une rangée horizontale, de gauche à droite (ou de droite à gauche pour les langues parlées de droite à gauche).

    Cinq boutons en disposition horizontale

  • Un QVBoxLayout présente les widgets dans une colonne verticale, de haut en bas.

    Cinq boutons en disposition verticale

  • Une page QGridLayout présente les widgets sous la forme d'une grille bidimensionnelle. Les widgets peuvent occuper plusieurs cellules.

    Cinq boutons dans une grille

  • Un site QFormLayout présente les widgets dans un style de champ d'étiquettes descriptives à deux colonnes.

    Trois boutons avec des champs de texte dans la mise en page du formulaire

Disposition des widgets dans le code

Le code suivant crée un site QHBoxLayout qui gère la géométrie de cinq sites QPushButtons, comme le montre la première capture d'écran ci-dessus :

QWidget *window = new QWidget;
QPushButton *button1 = new QPushButton("One");
QPushButton *button2 = new QPushButton("Two");
QPushButton *button3 = new QPushButton("Three");
QPushButton *button4 = new QPushButton("Four");
QPushButton *button5 = new QPushButton("Five");

QHBoxLayout *layout = new QHBoxLayout(window);
layout->addWidget(button1);
layout->addWidget(button2);
layout->addWidget(button3);
layout->addWidget(button4);
layout->addWidget(button5);

window->show();

Le code pour QVBoxLayout est identique, à l'exception de la ligne où la disposition est créée. Le code pour QGridLayout est un peu différent, car nous devons spécifier la position en ligne et en colonne du widget enfant :

QWidget *window = new QWidget;
QPushButton *button1 = new QPushButton("One");
QPushButton *button2 = new QPushButton("Two");
QPushButton *button3 = new QPushButton("Three");
QPushButton *button4 = new QPushButton("Four");
QPushButton *button5 = new QPushButton("Five");

QGridLayout *layout = new QGridLayout(window);
layout->addWidget(button1, 0, 0);
layout->addWidget(button2, 0, 1);
layout->addWidget(button3, 1, 0, 1, 2);
layout->addWidget(button4, 2, 0);
layout->addWidget(button5, 2, 1);

window->show();

Le troisième site QPushButton s'étend sur deux colonnes. Cela est possible en spécifiant 2 comme cinquième argument de QGridLayout::addWidget().

QFormLayout ajoutera deux widgets sur une ligne, généralement un QLabel et un QLineEdit pour créer des formulaires. L'ajout d'un QLabel et d'un QLineEdit sur la même ligne fera de QLineEdit le copain de QLabel. Le code suivant utilise QFormLayout pour placer trois QPushButtons et un QLineEdit correspondant sur une ligne.

QWidget *window = new QWidget;
QPushButton *button1 = new QPushButton("One");
QLineEdit *lineEdit1 = new QLineEdit();
QPushButton *button2 = new QPushButton("Two");
QLineEdit *lineEdit2 = new QLineEdit();
QPushButton *button3 = new QPushButton("Three");
QLineEdit *lineEdit3 = new QLineEdit();

QFormLayout *layout = new QFormLayout(window);
layout->addRow(button1, lineEdit1);
layout->addRow(button2, lineEdit2);
layout->addRow(button3, lineEdit3);

window->show();

Conseils pour l'utilisation des modèles

Lorsque vous utilisez une disposition, vous n'avez pas besoin de transmettre un parent lors de la construction des widgets enfants. La disposition répartira automatiquement les widgets (à l'aide de QWidget::setParent()) de manière à ce qu'ils soient des enfants du widget sur lequel la disposition est installée.

Remarque : les widgets d'une disposition sont des enfants du widget sur lequel la disposition est installée, et non de la disposition elle-même. Les widgets ne peuvent avoir que d'autres widgets comme parents, pas les dispositions.

Vous pouvez imbriquer des dispositions en utilisant addLayout() sur une disposition ; la disposition intérieure devient alors un enfant de la disposition dans laquelle elle est insérée.

Ajout de widgets à une mise en page

Lorsque vous ajoutez des widgets à une mise en page, le processus de mise en page fonctionne comme suit :

  1. Tous les widgets se voient initialement attribuer une quantité d'espace correspondant à leurs QWidget::sizePolicy() et QWidget::sizeHint().
  2. Si l'un des widgets a des facteurs d'étirement définis, avec une valeur supérieure à zéro, l'espace qui lui est alloué est proportionnel à son facteur d'étirement (expliqué ci-dessous).
  3. Si l'un des widgets a des facteurs d'étirement définis à zéro, il n'obtiendra plus d'espace que si aucun autre widget n'en veut. Parmi ceux-ci, l'espace est alloué en premier lieu aux widgets dont la politique de taille est Expanding.
  4. Tous les widgets qui se voient attribuer moins d'espace que leur taille minimale (ou l'indice de taille minimale si aucune taille minimale n'est spécifiée) se voient attribuer la taille minimale dont ils ont besoin. (Les widgets n'ont pas besoin d'avoir une taille minimale ou un indice de taille minimale, auquel cas le facteur d'étirement est leur facteur déterminant).
  5. Tous les widgets qui se voient attribuer plus d'espace que leur taille maximale se voient attribuer l'espace de taille maximale dont ils ont besoin. (Les widgets ne doivent pas avoir de taille maximale, auquel cas le facteur d'étirement est leur facteur déterminant).

Facteurs d'étirement

Les widgets sont normalement créés sans facteur d'étirement. Lorsqu'ils sont disposés dans une mise en page, les widgets se voient attribuer une part d'espace en fonction de leur QWidget::sizePolicy() ou de leur taille minimale, la plus grande étant retenue. Les facteurs d'étirement sont utilisés pour modifier l'espace accordé aux widgets les uns par rapport aux autres.

Si nous avons trois widgets disposés à l'aide d'une page QHBoxLayout sans facteurs d'étirement, nous obtiendrons une disposition comme celle-ci :

Trois widgets régulièrement espacés dans une rangée

Si nous appliquons des facteurs d'étirement à chaque widget, ils seront disposés proportionnellement (mais jamais en deçà de leur taille minimale), par ex.

Trois widgets étirés à la suite

Widgets personnalisés dans les mises en page

Lorsque vous créez votre propre classe de widget, vous devez également communiquer ses propriétés de disposition. Si le widget utilise l'une des dispositions de Qt Widgets, cela est déjà pris en compte. Si le widget n'a pas de widget enfant, ou utilise une disposition manuelle, vous pouvez modifier le comportement du widget en utilisant l'un ou l'autre ou l'ensemble des mécanismes suivants :

Appelez QWidget::updateGeometry() chaque fois que l'indice de taille, l'indice de taille minimale ou la politique de taille change. Cela entraînera un nouveau calcul de la mise en page. Plusieurs appels consécutifs à QWidget::updateGeometry() n'entraîneront qu'un seul recalcul de la disposition.

Si la hauteur préférée de votre widget dépend de sa largeur réelle (par exemple, une étiquette avec césure automatique des mots), activez le drapeau height-for-width dans le size policy du widget et réimplémentez QWidget::heightForWidth().

Même si vous implémentez QWidget::heightForWidth(), c'est toujours une bonne idée de fournir un sizeHint() raisonnable.

Pour plus d'informations sur l'implémentation de ces fonctions, voir l'article de Qt Quarterly Trading Height for Width.

Problèmes de mise en page

L'utilisation de texte enrichi dans un widget d'étiquette peut entraîner des problèmes de mise en page pour le widget parent. Ces problèmes sont dus à la manière dont le texte riche est géré par les gestionnaires de mise en page de Qt lorsque l'étiquette est enveloppée de mots.

Dans certains cas, la disposition parentale est mise en mode QLayout::FreeResize, ce qui signifie qu'elle n'adaptera pas la disposition de son contenu pour tenir dans des fenêtres de petite taille, ou même empêchera l'utilisateur de rendre la fenêtre trop petite pour être utilisable. Ce problème peut être résolu en sous-classant les widgets problématiques et en implémentant les fonctions sizeHint() et minimumSizeHint() appropriées.

Dans certains cas, il est important d'ajouter une disposition à un widget. Lorsque vous définissez le widget d'un QDockWidget ou d'un QScrollArea (avec QDockWidget::setWidget() et QScrollArea::setWidget()), la disposition doit déjà avoir été définie sur le widget. Si ce n'est pas le cas, le widget ne sera pas visible.

Mise en page manuelle

Si vous créez une présentation spéciale unique, vous pouvez également créer un widget personnalisé comme décrit ci-dessus. Réimplémentez QWidget::resizeEvent() pour calculer la distribution requise des tailles et appelez setGeometry() sur chaque enfant.

Le widget recevra un événement de type QEvent::LayoutRequest lorsque la disposition devra être recalculée. Réimplémentez QWidget::event() pour gérer les événements QEvent::LayoutRequest.

Comment écrire un gestionnaire de mise en page personnalisé

Une alternative à la mise en page manuelle consiste à écrire votre propre gestionnaire de mise en page en sous-classant QLayout. L'exemple Flow Layout montre comment procéder.

Nous présentons ici un exemple en détail. La classe CardLayout s'inspire du gestionnaire de mise en page Java du même nom. Elle dispose les éléments (widgets ou dispositions imbriquées) les uns au-dessus des autres, chaque élément étant décalé par QLayout::spacing().

Pour écrire votre propre classe de présentation, vous devez définir les éléments suivants :

  • Une structure de données pour stocker les éléments gérés par la mise en page. Chaque élément est un QLayoutItem. Nous utiliserons un QList dans cet exemple.
  • addItem(), comment ajouter des éléments à la mise en page.
  • setGeometry(), comment effectuer la mise en page.
  • sizeHint(), la taille préférée de la mise en page.
  • itemAt(), comment itérer sur la disposition.
  • takeAt(), comment supprimer des éléments de la disposition.

Dans la plupart des cas, vous implémenterez également minimumSize().

Le fichier d'en-tête (card.h)

#ifndef CARD_H
#define CARD_H

#include <QtWidgets>
#include <QList>

class CardLayout : public QLayout
{
public:
    CardLayout(int spacing): QLayout()
    { setSpacing(spacing); }
    CardLayout(int spacing, QWidget *parent): QLayout(parent)
    { setSpacing(spacing); }
    ~CardLayout();

    void addItem(QLayoutItem *item) override;
    QSize sizeHint() const override;
    QSize minimumSize() const override;
    int count() const override;
    QLayoutItem *itemAt(int) const override;
    QLayoutItem *takeAt(int) override;
    void setGeometry(const QRect &rect) override;

private:
    QList<QLayoutItem *> m_items;
};
#endif

Le fichier d'implémentation (card.cpp)

//#include "card.h"

Tout d'abord, nous définissons count() pour récupérer le nombre d'éléments de la liste.

int CardLayout::count() const
{
    // QList::size() returns the number of QLayoutItems in m_items
    return m_items.size();
}

Ensuite, nous définissons deux fonctions qui itèrent sur la présentation : itemAt() et takeAt(). Ces fonctions sont utilisées en interne par le système de mise en page pour gérer la suppression des widgets. Elles sont également disponibles pour les programmeurs d'applications.

itemAt() renvoie l'élément à l'index donné. takeAt() supprime l'élément à l'index donné et le renvoie. Dans ce cas, nous utilisons l'index de la liste comme index de présentation. Dans d'autres cas, lorsque la structure des données est plus complexe, il se peut que nous devions consacrer plus d'efforts à la définition d'un ordre linéaire pour les éléments.

QLayoutItem *CardLayout::itemAt(int idx) const
{
    // QList::value() performs index checking, and returns nullptr if we are
    // outside the valid range
    return m_items.value(idx);
}

QLayoutItem *CardLayout::takeAt(int idx)
{
    // QList::take does not do index checking
    return idx >= 0 && idx < m_items.size() ? m_items.takeAt(idx) : 0;
}

addItem() met en œuvre la stratégie de placement par défaut pour les éléments de la mise en page. Cette fonction doit être implémentée. Elle est utilisée par QLayout::add(), par le constructeur QLayout qui prend une disposition comme parent. Si votre disposition possède des options de placement avancées qui nécessitent des paramètres, vous devez fournir des fonctions d'accès supplémentaires telles que les surcharges de portée de ligne et de colonne de QGridLayout::addItem(), QGridLayout::addWidget() et QGridLayout::addLayout().

void CardLayout::addItem(QLayoutItem *item)
{
    m_items.append(item);
}

La mise en page assume la responsabilité des éléments ajoutés. Comme QLayoutItem n'hérite pas de QObject, nous devons supprimer les éléments manuellement. Dans le destructeur, nous supprimons chaque élément de la liste à l'aide de takeAt(), puis nous le supprimons.

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

La fonction setGeometry() effectue la mise en page. Le rectangle fourni en argument n'inclut pas margin(). Le cas échéant, utilisez spacing() comme distance entre les éléments.

void CardLayout::setGeometry(const QRect &r)
{
    QLayout::setGeometry(r);

    if (m_items.size() == 0)
        return;

    int w = r.width() - (m_items.count() - 1) * spacing();
    int h = r.height() - (m_items.count() - 1) * spacing();
    int i = 0;
    while (i < m_items.size()) {
        QLayoutItem *o = m_items.at(i);
        QRect geom(r.x() + i * spacing(), r.y() + i * spacing(), w, h);
        o->setGeometry(geom);
        ++i;
    }
}

sizeHint() et minimumSize() sont normalement très similaires dans leur mise en œuvre. Les tailles renvoyées par les deux fonctions doivent inclure spacing(), mais pas margin().

QSize CardLayout::sizeHint() const
{
    QSize s(0, 0);
    int n = m_items.count();
    if (n > 0)
        s = QSize(100, 70); //start with a nice default size
    int i = 0;
    while (i < n) {
        QLayoutItem *o = m_items.at(i);
        s = s.expandedTo(o->sizeHint());
        ++i;
    }
    return s + n * QSize(spacing(), spacing());
}

QSize CardLayout::minimumSize() const
{
    QSize s(0, 0);
    int n = m_items.count();
    int i = 0;
    while (i < n) {
        QLayoutItem *o = m_items.at(i);
        s = s.expandedTo(o->minimumSize());
        ++i;
    }
    return s + n * QSize(spacing(), spacing());
}

Remarques complémentaires

  • Cette mise en page personnalisée ne gère pas la hauteur pour la largeur.
  • Nous ignorons QLayoutItem::isEmpty(), ce qui signifie que la mise en page traitera les widgets cachés comme des widgets visibles.
  • Pour les mises en page complexes, la vitesse peut être grandement améliorée en mettant en cache les valeurs calculées. Dans ce cas, implémentez QLayoutItem::invalidate() pour indiquer que les données mises en cache sont sales.
  • L'appel à QLayoutItem::sizeHint(), etc. peut être coûteux. Vous devez donc stocker la valeur dans une variable locale si vous en avez besoin ultérieurement dans la même fonction.
  • Vous ne devez pas appeler QLayoutItem::setGeometry() deux fois sur le même élément dans la même fonction. Cet appel peut s'avérer très coûteux si l'élément a plusieurs widgets enfants, car le gestionnaire de mise en page doit effectuer une mise en page complète à chaque fois. Au lieu de cela, calculez la géométrie et définissez-la ensuite. (Cela ne s'applique pas seulement aux dispositions, vous devriez faire de même si vous implémentez votre propre resizeEvent(), par exemple).

Exemples de mise en page

De nombreux exemples du site Qt Widgets utilisent déjà des layouts, mais il existe plusieurs exemples qui illustrent les différents layouts.

Calculator Example

L'exemple montre comment utiliser les signaux et les emplacements pour mettre en œuvre la fonctionnalité d'un widget de calculatrice, et comment utiliser QGridLayout pour placer des widgets enfants dans une grille.

Calendar Widget Example

L'exemple du widget calendrier montre l'utilisation de QCalendarWidget.

Flow Layout Example

Montre comment disposer les widgets en fonction de la taille de la fenêtre.

Image Composition Example

Montre comment les modes de composition fonctionnent dans QPainter.

Menus Example

L'exemple Menus montre comment les menus peuvent être utilisés dans une application à fenêtre principale.

Simple Tree Model Example

L'exemple Simple Tree Model montre comment utiliser un modèle hiérarchique avec les classes de vues standard de Qt.

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