Utilisation d’un fichier d’interface utilisateur de Qt Widgets Designer dans votre application C++
Qt Widgets Les fichiers d'interface utilisateur du Designer représentent l'arborescence des widgets du formulaire au format XML. Les formulaires peuvent être traités :
- Au moment de la compilation, ce qui signifie que les formulaires sont convertis en code C++ pouvant être compilé.
- Au moment de l'exécution, ce qui signifie que les formulaires sont traités par la classe ` QUiLoader ` qui construit dynamiquement l'arborescence des widgets tout en analysant le fichier XML.
Traitement des formulaires au moment de la compilation
Vous créez des composants d’interface utilisateur à l’aide d’ Qt Widgets Designer et utilisez les outils de construction intégrés de Qt, qmake et uic, pour générer leur code lors de la construction de l’application. Le code généré contient l’objet d’interface utilisateur du formulaire. Il s’agit d’une structure C++ qui contient :
- Des pointeurs vers les widgets, les mises en page, les éléments de mise en page, les groupes de boutons et les actions du formulaire.
- Une fonction membre appelée
setupUi()permettant de construire l’arborescence des widgets sur le widget parent. - Une fonction membre appelée `
retranslateUi()` qui gère la traduction des propriétés de chaîne de caractères du formulaire. Pour plus d’informations, consultez la section Réagir aux changements de langue.
Le code généré peut être intégré à votre application et utilisé directement à partir de celle-ci. Vous pouvez également l'utiliser pour étendre des sous-classes de widgets standard.
Un formulaire traité à la compilation peut être utilisé dans votre application selon l’une des approches suivantes :
- L'approche directe: vous créez un widget qui servira d'espace réservé pour le composant, puis vous configurez l'interface utilisateur à l'intérieur de celui-ci.
- L’approche par héritage simple: vous créez une sous-classe de la classe de base du formulaire (par exemple, `QWidget ` ou ` QDialog`), et vous y incluez une instance privée de l’objet d’interface utilisateur du formulaire.
- L’approche par héritage multiple: vous créez une sous-classe à la fois de la classe de base du formulaire et de l’objet d’interface utilisateur du formulaire. Cela permet d’utiliser directement les widgets définis dans le formulaire depuis le périmètre de la sous-classe.
À titre d’illustration, nous créons une application simple de formulaire « Calculatrice ». Elle s’inspire de l’exemple original de formulaire « Calculatrice ».
L'application se compose d'un fichier source, main.cpp, et d'un fichier d'interface utilisateur.
Le fichier « calculatorform.ui », conçu à l'aide d'Qt Widgets Designer, est présenté ci-dessous :

Lors de l'utilisation de CMake pour compiler l'exécutable, un fichier CMakeLists.txt est nécessaire :
cmake_minimum_required(VERSION 3.16)
project(calculatorform LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
qt_add_executable(calculatorform
calculatorform.ui main.cpp)
set_target_properties(calculatorform PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(calculatorform PUBLIC
Qt::Core
Qt::Gui
Qt::Widgets
)Le formulaire figure parmi les fichiers source C++ dans qt_add_executable(). L’option « CMAKE_AUTOUIC » indique à CMake d’exécuter l’outil uic afin de créer un fichier ui_calculatorform.h pouvant être utilisé par les fichiers source.
Lorsque vous utilisez qmake pour compiler l’exécutable, un fichier .pro est requis :
TEMPLATE = app
FORMS = calculatorform.ui
SOURCES = main.cppLa particularité de ce fichier réside dans la déclaration FORMS qui indique à qmake quels fichiers traiter avec uic. Dans ce cas, le fichier calculatorform.ui est utilisé pour créer un fichier ui_calculatorform.h pouvant être utilisé par n’importe quel fichier répertorié dans la déclaration SOURCES.
Remarque : vous pouvez utiliser Qt Creator pour créer le projet « Calculator Form ». Cela génère automatiquement le fichier main.cpp, l’interface utilisateur et un fichier de projet pour l’outil de compilation de votre choix, que vous pouvez modifier.
L'approche directe
Pour utiliser l’approche directe, nous incluons le fichier ui_calculatorform.h directement dans main.cpp:
#include "ui_calculatorform.h"La fonction ` main ` crée le widget de calculatrice en construisant un ` QWidget ` standard que nous utilisons pour héberger l'interface utilisateur décrite par le fichier ` calculatorform.ui `.
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget widget;
Ui::CalculatorForm ui;
ui.setupUi(&widget);
widget.show();
return app.exec();
}Dans ce cas, ` Ui::CalculatorForm ` est un objet de description d'interface issu du fichier ` ui_calculatorform.h ` qui configure tous les widgets de la boîte de dialogue ainsi que les connexions entre ses signaux et ses slots.
L’approche directe offre un moyen rapide et simple d’utiliser des composants simples et autonomes dans vos applications. Cependant, les composants créés avec Qt Widgets Designer nécessitent souvent une intégration étroite avec le reste du code de l’application. Par exemple, le code ` CalculatorForm ` fourni ci-dessus se compilera et s’exécutera, mais les objets ` QSpinBox ` n’interagiront pas avec ` QLabel `, car nous avons besoin d’un slot personnalisé pour effectuer l’opération d’ajout et afficher le résultat dans ` QLabel`. Pour y parvenir, nous devons utiliser l’approche de l’héritage simple.
L’approche de l’héritage simple
Pour utiliser l’approche de l’héritage simple, nous créons une sous-classe d’un widget Qt standard et y incluons une instance privée de l’objet d’interface utilisateur du formulaire. Cela peut se présenter sous la forme suivante :
- Une variable membre
- Une variable membre de type pointeur
Utilisation d’une variable membre
Dans cette approche, nous créons une sous-classe d’un widget Qt et configurons l’interface utilisateur depuis le constructeur. Les composants utilisés de cette manière exposent les widgets et les dispositions utilisés dans le formulaire à la sous-classe du widget Qt, et fournissent un système standard permettant d’établir des connexions de signaux et de slots entre l’interface utilisateur et d’autres objets de votre application. La structure générée ` Ui::CalculatorForm ` est un membre de la classe.
Cette approche est utilisée dans l'exemple « Calculator Form ».
Pour nous assurer de pouvoir utiliser l’interface utilisateur, nous devons inclure le fichier d’en-tête généré par uic avant de faire référence à Ui::CalculatorForm:
#include "ui_calculatorform.h"Le fichier de projet doit être mis à jour pour inclure calculatorform.h. Pour CMake:
qt_add_executable(calculatorform
calculatorform.cpp calculatorform.h calculatorform.ui
main.cpp
)Dans certains cas particuliers, comme dans l’exemple ci-dessous où la directive d’inclusion utilise un chemin relatif, la fonction qt_add_ui() peut être utilisée pour générer le fichier ui_calculatorform.h au lieu de s’appuyer sur AUTOUIC.
Quand privilégier qt_add_ui plutôt qu’AUTOUIC
#include "src/files/ui_calculatorform.h"qt_add_ui(calculatorform SOURCES calculatorform.ui
INCLUDE_PREFIX src/files)Pour qmake:
HEADERS = calculatorform.hLa sous-classe est définie de la manière suivante :
class CalculatorForm : public QWidget
{
Q_OBJECT
public:
explicit CalculatorForm(QWidget *parent = nullptr);
private slots:
void updateResult();
private:
Ui::CalculatorForm ui;
};La caractéristique importante de cette classe est l'objet privé ` ui `, qui fournit le code permettant de configurer et de gérer l'interface utilisateur.
Le constructeur de la sous-classe crée et configure tous les widgets et la mise en page de la boîte de dialogue simplement en appelant la fonction ` setupUi() ` de l'objet ` ui `. Une fois cette opération effectuée, il est possible de modifier l'interface utilisateur selon les besoins.
CalculatorForm::CalculatorForm(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
connect(ui.inputSpinBox1, &QSpinBox::valueChanged, this, &CalculatorForm::updateResult);
connect(ui.inputSpinBox2, &QSpinBox::valueChanged, this, &CalculatorForm::updateResult);
}On peut connecter les signaux et les slots des widgets de l’interface utilisateur de la manière habituelle, en ajoutant le préfixe on_<nom de l’objet>. Pour plus d’informations, voir widgets-and-dialogs-with-auto-connect.
Les avantages de cette approche résident dans son utilisation simple de l’héritage pour fournir une interface basée sur ` QWidget`, ainsi que dans l’encapsulation des variables du widget d’interface utilisateur au sein du membre de données ` ui `. Nous pouvons utiliser cette méthode pour définir plusieurs interfaces utilisateur au sein d’un même widget, chacune étant contenue dans son propre espace de noms, puis les superposer (ou les composer). Cette approche peut notamment servir à créer des onglets individuels à partir de formulaires existants.
Utilisation d’une variable membre de type pointeur
Il est également possible de faire de la structure ` Ui::CalculatorForm ` un membre pointeur de la classe. L'en-tête se présente alors comme suit :
namespace Ui {
class CalculatorForm;
}
class CalculatorForm : public QWidget
...
virtual ~CalculatorForm();
...
private:
Ui::CalculatorForm *ui;
...Le fichier source correspondant se présente comme suit :
#include "ui_calculatorform.h"
CalculatorForm::CalculatorForm(QWidget *parent) :
QWidget(parent), ui(new Ui::CalculatorForm)
{
ui->setupUi(this);
}
CalculatorForm::~CalculatorForm()
{
delete ui;
}L'avantage de cette approche est que l'objet d'interface utilisateur peut être déclaré à l'avance, ce qui signifie que nous n'avons pas besoin d'inclure le fichier d'ui_calculatorform.h s généré dans l'en-tête. Le formulaire peut alors être modifié sans avoir à recompiler les fichiers source dépendants. Ceci est particulièrement important si la classe est soumise à des restrictions de compatibilité binaire.
Nous recommandons généralement cette approche pour les bibliothèques et les applications de grande envergure. Pour plus d'informations, consultez la section Création de bibliothèques partagées.
L'approche par héritage multiple
Les formulaires créés avec le Concepteur d'Qt Widgets s peuvent être sous-classés avec une classe standard basée sur QWidget. Cette approche rend tous les composants d'interface utilisateur définis dans le formulaire directement accessibles dans le champ d'application de la sous-classe, et permet d'établir des connexions signal-slot de la manière habituelle à l'aide de la fonction connect().
Il faut inclure le fichier d’en-tête généré par uic à partir du fichier calculatorform.ui, comme suit :
#include "ui_calculatorform.h"La classe est définie de manière similaire à celle utilisée dans l’approche à héritage simple, sauf que cette fois-ci, nous héritons à la fois de ` QWidget ` et de ` Ui::CalculatorForm`, comme suit :
class CalculatorForm : public QWidget, private Ui::CalculatorForm
{
Q_OBJECT
public:
explicit CalculatorForm(QWidget *parent = nullptr);
private slots:
void on_inputSpinBox1_valueChanged(int value);
void on_inputSpinBox2_valueChanged(int value);
};Nous héritons de ` Ui::CalculatorForm ` en privé afin de garantir que les objets d’interface utilisateur soient privés dans notre sous-classe. Nous pouvons également en hériter à l’aide des mots-clés ` public ` ou ` protected `, de la même manière que nous aurions pu définir ` ui ` comme public ou protégé dans le cas précédent.
Le constructeur de la sous-classe effectue bon nombre des tâches déjà réalisées par le constructeur utilisé dans l’exemple d’héritage simple:
Dans ce cas, les widgets utilisés dans l’interface utilisateur sont accessibles de la même manière qu’un widget créé manuellement dans le code. Nous n’avons plus besoin du préfixe ui pour y accéder.
Réagir aux changements de langage
Qt informe les applications d’un changement de langue de l’interface utilisateur en envoyant un événement de type ` QEvent::LanguageChange`. Pour appeler la fonction membre ` retranslateUi() ` de l’objet d’interface utilisateur, nous réimplémentons ` QWidget::changeEvent() ` dans la classe `form`, comme suit :
void CalculatorForm::changeEvent(QEvent *e)
{
QWidget::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
}
}Traitement des formulaires à l'exécution
Il est également possible de traiter les formulaires à l'exécution, ce qui permet de générer dynamiquement des interfaces utilisateur. Pour ce faire, on utilise le module ` QtUiTools `, qui fournit la classe ` QUiLoader ` pour gérer les formulaires créés avec le ` Qt Widgets Designer`.
L'approche UiTools
Un fichier de ressources contenant un fichier d’interface utilisateur est nécessaire pour traiter les formulaires lors de l’exécution. De plus, l’application doit être configurée pour utiliser le module ` QtUiTools `. Pour ce faire, il suffit d’inclure les déclarations suivantes dans un fichier de projet ` CMake `, en s’assurant que l’application est correctement compilée et liée.
find_package(Qt6 REQUIRED COMPONENTS Core Gui UiTools Widgets)
target_link_libraries(textfinder PUBLIC
Qt::Core
Qt::Gui
Qt::UiTools
Qt::Widgets
)Pour qmake:
QT += uitoolsLa classe QUiLoader fournit un objet chargeur de formulaire permettant de construire l’interface utilisateur. Cette interface utilisateur peut être récupérée à partir de n’importe quel objet QIODevice, par exemple un objet QFile, afin d’obtenir un formulaire stocké dans le fichier de ressources d’un projet. La fonction QUiLoader::load() construit le widget du formulaire à l’aide de la description de l’interface utilisateur contenue dans le fichier.
Les classes du module QtUiTools peuvent être incluses à l’aide de la directive suivante :
#include <QtUiTools>La fonction ` QUiLoader::load()` est appelée comme indiqué dans ce code tiré de l’exemple Text Finder:
static QWidget*loadUiFile(QWidget*parent)
{
QFile file(u":/forms/textfinder.ui"_s);
if (!file.open(QIODevice::ReadOnly))
qFatal("Cannot open resource file");
retour QUiLoader().load(&file, parent);
}Dans une classe qui utilise QtUiTools pour construire son interface utilisateur lors de l'exécution, nous pouvons localiser des objets dans le formulaire à l'aide de QObject::findChild(). Par exemple, dans le code suivant, nous localisons certains composants en fonction de leurs noms d'objet et de leurs types de widget :
ui_findButton = findChild<QPushButton*>("findButton");
ui_textEdit = findChild<QTextEdit*>("textEdit");
ui_lineEdit = findChild<QLineEdit*>("lineEdit");Le traitement des formulaires à l'exécution offre au développeur la liberté de modifier l'interface utilisateur d'un programme, simplement en modifiant le fichier UI. Cela s'avère utile lors de la personnalisation de programmes pour répondre aux différents besoins des utilisateurs, tels que des icônes extra-larges ou une palette de couleurs différente pour faciliter l'accessibilité.
Connexions automatiques
Les connexions entre signaux et slots définies pour les formulaires de compilation ou d’exécution peuvent être configurées manuellement ou automatiquement, grâce à la capacité d’ QMetaObject à établir des connexions entre les signaux et les slots correctement nommés.
En général, dans un QDialog, si l'on souhaite traiter les informations saisies par l'utilisateur avant de les accepter, il faut connecter le signal clicked() du bouton OK à un slot personnalisé de notre boîte de dialogue. Nous allons d'abord présenter un exemple de boîte de dialogue dans laquelle le slot est connecté manuellement, puis le comparer à une boîte de dialogue utilisant la connexion automatique.
Une boîte de dialogue sans connexion automatique
Nous définissons la boîte de dialogue de la même manière que précédemment, mais en incluant désormais un slot en plus du constructeur :
class ImageDialog : public QDialog, private Ui::ImageDialog
{
Q_OBJECT
public:
explicit ImageDialog(QWidget *parent = nullptr);
private slots:
void checkValues();
};Le slot « checkValues() » servira à valider les valeurs fournies par l'utilisateur.
Dans le constructeur de la boîte de dialogue, nous configurons les widgets comme précédemment, puis nous connectons le signal clicked() du bouton « Annuler » au slot reject() de la boîte de dialogue. Nous désactivons également la propriété « autoDefault » des deux boutons afin de nous assurer que la boîte de dialogue n’interfère pas avec la manière dont la zone de saisie gère les événements liés à la touche Entrée :
ImageDialog::ImageDialog(QWidget *parent)
: QDialog(parent)
{
setupUi(this);
okButton->setAutoDefault(false);
cancelButton->setAutoDefault(false);
...
connect(okButton, &QAbstractButton::clicked, this, &ImageDialog::checkValues);
}Nous connectons le signal ` clicked()` du bouton « OK » au slot `checkValues()` de la boîte de dialogue, que nous implémentons comme suit :
void ImageDialog::checkValues()
{
if (nameLineEdit->text().isEmpty()) {
QMessageBox::information(this, tr("No Image Name"),
tr("Please supply a name for the image."), QMessageBox::Cancel);
} else {
accept();
}
}Ce slot personnalisé effectue le minimum nécessaire pour s'assurer que les données saisies par l'utilisateur sont valides : il n'accepte la saisie que si un nom a été attribué à l'image.
Widgets et boîtes de dialogue avec connexion automatique
Bien qu’il soit facile d’implémenter un slot personnalisé dans la boîte de dialogue et de le connecter dans le constructeur, nous pourrions plutôt utiliser les fonctionnalités de connexion automatique d’ QMetaObject pour relier le signal clicked() du bouton OK à un slot de notre sous-classe. uic génère automatiquement le code nécessaire dans la fonction ` setupUi() ` de la boîte de dialogue ; il suffit donc de déclarer et d’implémenter un slot dont le nom respecte une convention standard :
void on_<object name>_<signal name>(<signal parameters>);Remarque : lorsque l’on renomme des widgets dans le formulaire, les noms des slots doivent être adaptés en conséquence, ce qui peut poser un problème de maintenance. C’est pourquoi nous déconseillons d’utiliser cette méthode dans le nouveau code.
En suivant cette convention, nous pouvons définir et implémenter un slot qui répond aux clics de souris sur le bouton OK:
class ImageDialog : public QDialog, private Ui::ImageDialog
{
Q_OBJECT
public:
explicit ImageDialog(QWidget *parent = nullptr);
private slots:
void on_okButton_clicked();
};Un autre exemple de connexion automatique entre un signal et un slot serait le Text Finder avec son slot « on_findButton_clicked() ».
Nous utilisons le système de « QMetaObject » pour activer les connexions entre signaux et slots :
QMetaObject::connectSlotsByName(this);Cela nous permet d'implémenter le slot, comme illustré ci-dessous :
void TextFinder::on_findButton_clicked()
{
QString searchString = ui_lineEdit->text();
QTextDocument *document = ui_textEdit->document();
bool found = false;
// undo previous change (if any)
document->undo();
if (searchString.isEmpty()) {
QMessageBox::information(this, tr("Empty Search Field"),
tr("The search field is empty. "
"Please enter a word and click Find."));
} else {
QTextCursor highlightCursor(document);
QTextCursor cursor(document);
cursor.beginEditBlock();
...
cursor.endEditBlock();
if (found == false) {
QMessageBox::information(this, tr("Word Not Found"),
tr("Sorry, the word cannot be found."));
}
}
}La connexion automatique des signaux et des slots fournit à la fois une convention de nommage standard et une interface explicite sur laquelle les concepteurs de widgets peuvent s'appuyer. En fournissant un code source qui implémente une interface donnée, les concepteurs d'interfaces utilisateur peuvent vérifier que leurs conceptions fonctionnent réellement sans avoir à écrire de code eux-mêmes.
© 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.