Utilisation d'un fichier Designer UI dans une application C++
Qt Widgets Designer Les fichiers UI 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++ qui peut être compilé.
- Au moment del'exécution, ce qui signifie que les formulaires sont traités par la classe QUiLoader qui construit dynamiquement l'arbre des widgets tout en analysant le fichier XML.
Traitement des formulaires à la compilation
Vous créez des composants d'interface utilisateur avec Qt Widgets Designer et utilisez les outils de construction intégrés de Qt XML, qmake et uic, pour générer le code de ces composants 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 dispositions, les éléments de disposition, les groupes de boutons et les actions du formulaire.
- Une fonction membre appelée
setupUi()pour construire l'arbre des widgets sur le widget parent. - Une fonction membre appelée
retranslateUi()qui gère la traduction des propriétés de chaîne du formulaire. Pour plus d'informations, voir Réagir aux changements de langue.
Le code généré peut être inclus dans votre application et utilisé directement à partir de celle-ci. Vous pouvez également l'utiliser pour étendre les sous-classes des widgets standard.
Un formulaire traité au moment de la compilation peut être utilisé dans votre application selon l'une des approches suivantes :
- L'approche directe: vous construisez un widget qui servira d'emplacement pour le composant, et vous configurez l'interface utilisateur à l'intérieur de ce widget.
- L'approche de l'héritage unique: vous sous-classez la classe de base du formulaire (QWidget ou QDialog, par exemple) et vous incluez une instance privée de l'objet d'interface utilisateur du formulaire.
- L'approche de l'héritage multiple: vous sous-classez à la fois la classe de base du formulaire et l'objet d'interface utilisateur du formulaire. Cela permet d'utiliser les widgets définis dans le formulaire directement à partir de la portée de la sous-classe.
Pour le démontrer, nous créons une application simple de formulaire de calculatrice. Elle est basée sur l'exemple original du formulaire de calcul.
L'application se compose d'un fichier source, main.cpp et d'un fichier UI.
Le fichier calculatorform.ui conçu avec Qt Widgets Designer est illustré ci-dessous :

Lorsque vous utilisez CMake pour construire 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 est répertorié parmi les fichiers source C++ dans qt_add_executable(). L'option CMAKE_AUTOUIC indique à CMake d'exécuter l'outil uic pour créer un fichier ui_calculatorform.h qui peut être utilisé par les fichiers sources.
Lorsque vous utilisez qmake pour construire l'exécutable, un fichier .pro est nécessaire :
TEMPLATE = app FORMS = calculatorform.ui SOURCES = main.cpp
La particularité de ce fichier est 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 qui peut être utilisé par n'importe quel fichier énuméré dans la déclaration SOURCES.
Remarque : vous pouvez utiliser Qt Creator pour créer le projet Calculator Form. Il génère automatiquement le fichier main.cpp, l'interface utilisateur et un fichier de projet pour l'outil de construction souhaité, 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 la calculatrice en construisant un standard QWidget que nous utilisons pour héberger l'interface utilisateur décrite dans 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 du fichier ui_calculatorform.h qui définit tous les widgets de la boîte de dialogue et les connexions entre ses signaux et ses emplacements.
L'approche directe constitue un moyen rapide et facile 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 sera compilé et exécuté, 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 unique.
L'approche de l'héritage unique
Pour utiliser l'approche de l'héritage unique, nous sous-classons un widget Qt standard et incluons une instance privée de l'objet d'interface utilisateur du formulaire. Cela peut prendre la forme de :
- d'une variable membre
- Une variable membre de type pointeur
Utilisation d'une variable membre
Dans cette approche, nous sous-classons un widget Qt et configurons l'interface utilisateur à partir du constructeur. Les composants utilisés de cette manière exposent les widgets et les dispositions utilisés dans le formulaire à la sous-classe de widget Qt et fournissent un système standard pour établir des connexions de signaux et de fentes entre l'interface utilisateur et d'autres objets de votre application. La structure Ui::CalculatorForm générée est un membre de la classe.
Cette approche est utilisée dans l'exemple du formulaire de calculatrice.
Pour 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 des cas spécifiques, comme dans l'exemple ci-dessous où la directive include utilise un chemin relatif, qt_add_ui peut être utilisé pour générer le fichier ui_calculatorform.h au lieu de s'appuyer sur AUTOUIC.
Quand préférer qt_add_ui à AUTOUIC
#include "src/files/ui_calculatorform.h"qt_add_ui(calculatorform SOURCES calculatorform.ui INCLUDE_PREFIX src/files)
Pour qmake:
HEADERS = calculatorform.h
La 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 la classe est l'objet privé ui qui fournit le code pour la mise en place et la gestion de l'interface utilisateur.
Le constructeur de la sous-classe construit et configure tous les widgets et toutes les dispositions du dialogue en appelant simplement la fonction setupUi() de l'objet ui. Une fois cette opération effectuée, il est possible de modifier l'interface utilisateur en fonction des 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); }
Nous pouvons connecter des signaux et des emplacements dans les 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 sont l'utilisation simple de l'héritage pour fournir une interface basée sur QWidget et l'encapsulation des variables des widgets de l'interface utilisateur dans le membre de données ui. Nous pouvons utiliser cette méthode pour définir un certain nombre d'interfaces utilisateur dans le même widget, chacune étant contenue dans son propre espace de noms, et les superposer (ou les composer). Cette approche peut être utilisée pour créer des onglets individuels à partir de formulaires existants, par exemple.
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 ne devons pas inclure le fichier ui_calculatorform.h généré dans l'en-tête. Le formulaire peut alors être modifié sans recompiler les fichiers sources 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 grandes applications. Pour plus d'informations, voir Création de bibliothèques partagées.
L'approche de l'héritage multiple
Les formulaires créés avec Qt Widgets Designer peuvent être sous-classés avec une classe standard basée sur QWidget. Cette approche rend tous les composants de l'interface utilisateur définis dans le formulaire directement accessibles dans le cadre de la sous-classe et permet d'établir des connexions de signaux et d'emplacements de la manière habituelle avec la fonction connect().
Nous devons inclure le fichier d'en-tête que uic génère à partir du fichier calculatorform.ui, comme suit :
#include "ui_calculatorform.h"La classe est définie d'une manière similaire à celle utilisée dans l'approche de l'héritage unique, sauf que cette fois 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 de manière privée pour nous assurer que les objets de l'interface utilisateur sont privés dans notre sous-classe. Nous pouvons également en hériter avec les mots-clés public ou protected, de la même manière que nous aurions pu rendre ui public ou protégé dans le cas précédent.
Le constructeur de la sous-classe exécute de nombreuses tâches identiques à celles du constructeur utilisé dans l'exemple de l'héritage unique:
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 langue
Qt notifie les applications si la langue de l'interface utilisateur change en envoyant un événement du type QEvent::LanguageChange. Pour appeler la fonction membre retranslateUi() de l'objet 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 en temps réel
Les formulaires peuvent également être traités au moment de l'exécution, produisant ainsi des interfaces utilisateur générées dynamiquement. Cela peut se faire à l'aide du module QtUiTools qui fournit la classe QUiLoader pour gérer les formulaires créés avec Qt Widgets Designer.
L'approche UiTools
Un fichier de ressources contenant un fichier d'interface utilisateur est nécessaire pour traiter les formulaires au moment de l'exécution. L'application doit également ê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 veillant à ce que l'application soit compilée et liée de manière approprié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 += uitools
La classe QUiLoader fournit un objet chargeur de formulaire pour construire l'interface utilisateur. Cette interface utilisateur peut être récupérée à partir de n'importe quel QIODevice, par exemple un objet QFile, pour obtenir un formulaire stocké dans le fichier de ressources d'un projet. La fonction QUiLoader::load() construit le widget du formulaire en utilisant 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 invoquée comme le montre le code de l'exemple Text Finder:
statique 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 au moment 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 leur nom d'objet et de leur type de widget :
ui_findButton = findChild<QPushButton*>("findButton"); ui_textEdit = findChild<QTextEdit*>("textEdit"); ui_lineEdit = findChild<QLineEdit*>("lineEdit");
Le traitement des formulaires au moment de l'exécution donne au développeur la liberté de modifier l'interface utilisateur d'un programme, en changeant simplement le fichier d'interface utilisateur. Cela est utile pour personnaliser les programmes afin de répondre aux différents besoins des utilisateurs, tels que des icônes plus grandes ou un schéma de couleurs différent pour la prise en charge de l'accessibilité.
Connexions automatiques
Les connexions entre les signaux et les emplacements définis pour les formulaires de compilation ou d'exécution peuvent être établies manuellement ou automatiquement, en utilisant la capacité de QMetaObject à établir des connexions entre les signaux et les emplacements convenablement nommés.
En général, dans un formulaire QDialog, si nous voulons traiter les informations saisies par l'utilisateur avant de les accepter, nous devons connecter le signal clicked() du bouton OK à un slot personnalisé dans notre boîte de dialogue. Nous montrerons d'abord un exemple de dialogue dans lequel le slot est connecté à la main, puis nous le comparerons à un dialogue qui utilise 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 nous incluons maintenant 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() sera utilisé pour valider les valeurs fournies par l'utilisateur.
Dans le constructeur de la boîte de dialogue, nous configurons les widgets comme précédemment et nous connectons le signal clicked() du bouton Cancel 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 l'éditeur de ligne gère les événements de touche de retour :
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é fait 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é donné à l'image.
Widgets et boîtes de dialogue avec connexion automatique
Bien qu'il soit facile d'implémenter un slot personnalisé dans le dialogue et de le connecter dans le constructeur, nous pourrions plutôt utiliser les fonctions d'auto-connexion de QMetaObject pour connecter le signal clicked() du bouton OK à un slot dans notre sous-classe. uic génère automatiquement du code dans la fonction setupUi() du dialogue pour le faire, de sorte que nous n'avons qu'à déclarer et à implémenter un slot avec un nom qui suit une convention standard :
void on_<object name>_<signal name>(<signal parameters>);
Note : 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. Pour cette raison, nous recommandons de ne pas utiliser cette méthode dans le nouveau code.
En utilisant 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 de signaux et d'emplacements :
QMetaObject::connectSlotsByName(this);
Cela nous permet d'implémenter le slot, comme indiqué 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 dénomination standard et une interface explicite sur laquelle les concepteurs de widgets peuvent s'appuyer. En fournissant un code source qui met en œuvre une interface donnée, les concepteurs d'interfaces utilisateur peuvent vérifier que leurs conceptions fonctionnent réellement sans avoir à écrire eux-mêmes le code.
© 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.