Sur cette page

Gestion de la mise en page

Le système de mise en page de Qt offre un moyen simple et puissant d'agencer automatiquement les widgets enfants au sein d'un widget afin de garantir qu'ils exploitent au mieux l'espace disponible.

Introduction

Qt comprend un ensemble de classes de gestion de mise en page qui servent à décrire la disposition des widgets dans l'interface utilisateur d'une application. Ces mises en page positionnent et redimensionnent automatiquement les widgets lorsque l'espace qui leur est alloué change, garantissant ainsi une disposition cohérente et le maintien de la facilité d'utilisation de l'interface utilisateur dans son ensemble.

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

  • Positionnement des widgets enfants
  • Définition de tailles par défaut raisonnables pour les fenêtres
  • Définition de 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
    • Masquage ou affichage d’un widget enfant
    • Suppression de widgets enfants

Les classes de mise en page de Qt

Les classes de mise en page de Qt ont été conçues pour le code C++ écrit à la main ; elles permettent de spécifier les dimensions en pixels pour plus de simplicité, ce qui les rend faciles à comprendre et à utiliser. Le code généré pour les formulaires créés à l’aide du Designer d’ Qt Widgets utilise également ces classes de mise en page. Le Designer d’ Qt Widgets est particulièrement 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 associé au développement d’interfaces utilisateur.

QBoxLayout

Aligne les widgets enfants horizontalement ou verticalement

QButtonGroup

Conteneur permettant d’organiser des groupes de widgets « bouton »

QFormLayout

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

QGraphicsAnchor

Représente un point d’ancrage entre deux éléments dans un QGraphicsAnchorLayout

QGraphicsAnchorLayout

Disposition permettant d'ancrer des widgets les uns aux autres dans la vue graphique

QGridLayout

Dispose les widgets dans une grille

QGroupBox

Cadre de zone de groupe avec un titre

QHBoxLayout

Aligne les widgets horizontalement

QLayout

La 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 dont un seul est visible à la fois

QStackedWidget

Pile de widgets dont un seul est visible à la fois

QVBoxLayout

Aligne les widgets verticalement

QWidgetItem

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

Dispositions horizontale, verticale, en grille et en formulaire

Le moyen le plus simple d’organiser correctement vos widgets est d’utiliser les gestionnaires de mise en page intégrés : QHBoxLayout, QVBoxLayout, QGridLayout et QFormLayout. Ces classes héritent de QLayout, qui dérive lui-même de QObject (et non de QWidget). Elles se chargent de la gestion de la géométrie d’un ensemble de widgets. Pour créer des mises en page plus complexes, vous pouvez imbriquer les gestionnaires de mise en page les uns dans les autres.

  • Un gestionnaire de disposition de type « QHBoxLayout » dispose les widgets en une ligne horizontale, de gauche à droite (ou de droite à gauche pour les langues s’écrivant de droite à gauche).

    Cinq boutons disposés horizontalement

  • Un « QVBoxLayout » dispose les widgets en une colonne verticale, de haut en bas.

    Cinq boutons disposés verticalement

  • Un « QGridLayout » dispose les widgets dans une grille bidimensionnelle. Les widgets peuvent occuper plusieurs cellules.

    Cinq boutons disposés en grille

  • Un « QFormLayout » dispose les widgets selon un style « étiquette-champ » descriptif à deux colonnes.

    Trois boutons accompagnés de champs de saisie dans une mise en page de formulaire

Disposition des widgets dans le code

Le code suivant crée un « QHBoxLayout » qui gère la géométrie de cinq « QPushButtons », comme illustré sur 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 de l'QVBoxLayout est identique, à l'exception de la ligne où la disposition est créée. Le code de l'QGridLayout est légèrement 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 QPushButton s'étend sur deux colonnes. Cela est possible en spécifiant la valeur 2 comme cinquième argument de la fonction QGridLayout::addWidget().

QFormLayout permet d’ajouter deux widgets sur une même 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 définira l’« QLineEdit » comme le « buddy » de l’« QLabel ». Le code suivant utilise l’« QFormLayout » pour placer trois « QPushButtons » et un « QLineEdit » correspondant sur une même 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 d’utilisation des mises en page

Lorsque vous utilisez une disposition, vous n’avez pas besoin de lui passer un parent lors de la création des widgets enfants. La disposition réattribuera automatiquement un parent aux widgets (à l’aide de la fonction ` QWidget::setParent()`) afin qu’ils deviennent les 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 pour parent que d'autres widgets, et non des dispositions.

Vous pouvez imbriquer des mises en page à l’aide de la fonction addLayout() sur une mise en page ; la mise en page interne devient alors un enfant de la mise en page 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 se déroule comme suit :

  1. Dans un premier temps, un espace est alloué à tous les widgets en fonction de leurs paramètres « QWidget::sizePolicy » () et « QWidget::sizeHint » ().
  2. Si l'un des widgets possède un facteur d'étirement défini, avec une valeur supérieure à zéro, alors l'espace lui est attribué proportionnellement à son facteur d'étirement (explication ci-dessous).
  3. Si certains widgets ont un facteur d’étirement défini sur zéro, ils ne recevront davantage d’espace que si aucun autre widget ne revendique cet espace. Parmi ceux-ci, l’espace est d’abord attribué aux widgets dont la politique de taille est « Expanding ».
  4. Tout widget auquel on attribue moins d’espace que sa taille minimale (ou sa taille minimale indicative si aucune taille minimale n’est spécifiée) se voit attribuer cette taille minimale dont il a besoin. (Les widgets ne sont pas tenus d’avoir une taille minimale ou une taille minimale indicative ; dans ce cas, le facteur d’étirement est le facteur déterminant.)
  5. Tout widget auquel est alloué plus d’espace que sa taille maximale se voit attribuer l’espace correspondant à cette taille maximale dont il a besoin. (Les widgets ne sont pas tenus d’avoir une taille maximale ; dans ce cas, le facteur d’étirement est le facteur déterminant.)

Facteurs d'étirement

Les widgets sont généralement créés sans facteur d’étirement défini. Lorsqu’ils sont placés dans une mise en page, les widgets se voient attribuer une part d’espace correspondant à leur taille minimale ( QWidget::sizePolicy()) ou à leur indication de taille minimale, la plus grande des deux prévalant. Les facteurs d’étirement permettent de modifier la proportion d’espace allouée aux widgets les uns par rapport aux autres.

Si nous disposons de trois widgets organisés à l'aide d'un QHBoxLayout sans aucun facteur d'étirement défini, nous obtiendrons une mise en page comme celle-ci :

Trois widgets alignés en ligne, espacés de manière régulière

Si l’on applique des facteurs d’étirement à chaque widget, ceux-ci seront disposés de manière proportionnelle (mais jamais en dessous de leur taille minimale indiquée), par exemple :

Trois widgets étirés alignés les uns à côté des autres

Widgets personnalisés dans les mises en page

Lorsque vous créez votre propre classe de widget, vous devez également définir ses propriétés de mise en page. Si le widget utilise l'une des mises en page de Qt, cela est déjà pris en charge. Si le widget ne comporte aucun widget enfant ou utilise une mise en page manuelle, vous pouvez modifier son comportement à l'aide de l'un ou de l'ensemble des mécanismes suivants :

  • Réimplémentez la méthode ` QWidget::sizeHint()` pour renvoyer la taille préférée du widget.
  • Réimplémentez la méthode ` QWidget::minimumSizeHint()` pour renvoyer la plus petite taille que le widget peut avoir.
  • Appeler la méthode ` QWidget::setSizePolicy()` pour spécifier l’espace requis par le widget.

Appelez la méthode ` QWidget::updateGeometry()` chaque fois que l'indication de taille, l'indication de taille minimale ou la politique de taille changent. Cela entraînera un recalcul de la mise en page. Plusieurs appels consécutifs à la méthode ` QWidget::updateGeometry()` n'entraîneront qu'un seul recalcul de la mise en page.

Si la hauteur souhaitée de votre widget dépend de sa largeur réelle (par exemple, une étiquette avec coupure de mot automatique), activez le drapeau « height-for-width » dans la méthode ` size policy ` du widget et réimplémentez la méthode ` QWidget::heightForWidth()`.

Même si vous implémentez ` QWidget::heightForWidth()`, il est tout de même recommandé de fournir une valeur raisonnable pour `sizeHint()`.

Pour plus d’informations sur l’implémentation de ces fonctions, consultez l’article « Trading Height for Width » publié dans Qt Quarterly.

Problèmes de mise en page

L'utilisation de texte enrichi dans un widget « label » peut poser certains problèmes de mise en page pour son widget parent. Ces problèmes sont dus à la manière dont le texte enrichi est géré par les gestionnaires de mise en page de Qt lorsque le texte du « label » est renvoyé à la ligne.

Dans certains cas, la mise en page parente est placée en mode QLayout::FreeResize, ce qui signifie qu’elle n’adaptera pas la mise en page de son contenu pour s’ajuster à des fenêtres de petite taille, voire qu’elle empêchera l’utilisateur de réduire la fenêtre au point de la rendre inutilisable. Il est possible de contourner ce problème en sous-classant les widgets problématiques et en implémentant des fonctions sizeHint() et minimumSizeHint() adaptées.

Dans certains cas, le moment où une disposition est ajoutée à un widget a son importance. Lorsque vous définissez le widget d’une fonction ` QDockWidget ` ou ` QScrollArea ` (à l’aide de ` QDockWidget::setWidget()` et ` QScrollArea::setWidget()`), la disposition doit déjà avoir été définie sur le widget. Sinon, le widget ne sera pas visible.

Mise en page manuelle

Si vous créez une mise en page spéciale et unique, vous pouvez également créer un widget personnalisé comme décrit ci-dessus. Réimplémentez la méthode ` QWidget::resizeEvent()` pour calculer la répartition des tailles requise et appelez la méthode ` setGeometry()` sur chaque enfant.

Le widget recevra un événement de type QEvent::LayoutRequest lorsque la mise en page 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 créant une sous-classe de ` 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 mises en page imbriquées) les uns au-dessus des autres, chaque élément étant décalé par la méthode ` QLayout::spacing()`.

Pour écrire votre propre classe de mise en page, 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 souhaitée de la mise en page.
  • itemAt(), comment parcourir la mise en page.
  • takeAt(), comment supprimer des éléments de la mise en page.

Dans la plupart des cas, vous devrez également implémenter la méthode ` 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"

Nous définissons tout d’abord la fonction count() afin de 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 parcourent la mise en page : 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 accessibles aux développeurs d'applications.

itemAt() renvoie l’élément situé à l’index donné. ` takeAt() ` supprime l’élément situé à l’index donné, puis le renvoie. Dans ce cas, nous utilisons l’index de la liste comme index de mise en page. Dans d’autres cas où la structure de données est plus complexe, il peut s’avérer nécessaire de consacrer davantage 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 mise en page. Cette fonction doit être implémentée. Elle est utilisée par QLayout::add(), ainsi que par le constructeur de QLayout qui prend une mise en page comme parent. Si votre mise en page dispose d’options de placement avancées nécessitant des paramètres, vous devez fournir des fonctions d’accès supplémentaires, telles que les surcharges de QGridLayout::addItem(), QGridLayout::addWidget() et QGridLayout::addLayout() permettant de s’étendre sur plusieurs lignes et colonnes.

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

La mise en page prend en charge les éléments ajoutés. Étant donné que ` QLayoutItem ` n’hérite pas de ` QObject`, nous devons supprimer les éléments manuellement. Dans le destructeur, nous retirons 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 en réalité la mise en page. Le rectangle fourni en argument n’inclut pas margin(). Si nécessaire, 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 généralement très similaires dans leur implémentation. Les tailles renvoyées par ces 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 prend pas en compte la hauteur par rapport à la largeur.
  • Nous ignorons la fonction QLayoutItem::isEmpty() ; cela signifie que la mise en page traitera les widgets masqués comme s’ils étaient visibles.
  • Pour les mises en page complexes, la vitesse peut être considérablement améliorée en mettant en cache les valeurs calculées. Dans ce cas, implémentez ` QLayoutItem::invalidate()` pour marquer les données mises en cache comme modifiées.
  • L’appel de ` QLayoutItem::sizeHint()`, etc., peut être coûteux en ressources. Vous devriez donc stocker la valeur dans une variable locale si vous en avez à nouveau besoin plus tard au sein de la même fonction.
  • Vous ne devez pas appeler ` QLayoutItem::setGeometry()` deux fois sur le même élément au sein d’une même fonction. Cet appel peut être très coûteux si l’élément comporte plusieurs widgets enfants, car le gestionnaire de mise en page doit effectuer une mise en page complète à chaque fois. Calculez plutôt la géométrie, puis définissez-la. (Ceci ne s'applique pas uniquement aux mises en page ; vous devriez procéder de la même manière si vous implémentez votre propre méthode `resizeEvent()`, par exemple.)

Exemples de mise en page

De nombreux exemples d’ Qt Widgets utilisent déjà des mises en page, mais il existe plusieurs exemples destinés à illustrer différentes mises en page.

Calculator Example

L'exemple montre comment utiliser les signaux et les slots pour implémenter les fonctionnalités d'un widget de calculatrice, et comment utiliser QGridLayout pour placer des widgets enfants dans une grille.

Calendar Widget Example

L’exemple « Widget Calendrier » illustre l’utilisation de QCalendarWidget.

Flow Layout Example

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

Image Composition Example

Il montre comment fonctionnent les modes de composition dans QPainter.

Menus Example

L'exemple « Menus » montre comment utiliser les menus dans une application à fenêtre principale.

Simple Tree Model Example

L'exemple « Modèle d'arborescence simple » montre comment utiliser un modèle hiérarchique avec les classes de vue 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.