Utilisation du designer avec Qt4
Par Nicolas le mercredi, avril 4 2007, 21:33 - Documentation - Lien permanent
- Comment intégrer un widget créé avec le designer ?
- Comment utiliser les fichiers .ui créés par le designer ?
- Quelle est la différence entre les trois méthodes d'intégration?
Cet article va détailler l'utilisation du designer et l'intégration d'un widget créé avec le designer dans notre programme. Cet article est une adaptation libre de la documentation de Qt : Using a Component in Your Application.
Version : Qt4 (supérieur ou égal à Qt 4.1)
Auteur : Nicolas Arnaud-Cormos (nikikko)
Test : Linux (Qt 4.2)
Introduction
Depuis Qt4, le designer a repris sa fonction première : la création WYSIWYG de forms(terme générique pour désigner soit une fenêtre principale, soit une boîte de dialogue, soit un widget personnalisé). L'utilisation du designer en lui-même et les différences entre ces forms ne seront pas abordés ici.
Une fois que l'utilisateur a créé une form et l'enregistre sur le disque, il crée un fichier .ui qui n'est autre qu'un fichier XML décrivant la form :
- widgets qui composent la form
- organisation (layout) et position des différents widgets
- connexions entre les différents widgets
- ...
Ce fichier .ui n'est pas directement compilable : pour être utilisé, il doit être converti en fichier C++ à l'aide de l'outil uic. Avec l'utilisation combinée de uic et de qmake, le code C++ est généré automatiquement lors de la compilation de l'application.
Il existe trois méthodes différentes pour intégrer les forms créées avec le designer. Pour l'exemple, vous pouvez utiliser n'importe quelle form, vous en trouverez une en annexe de cet article (qui s'appelle mywidget.ui) :

Fichier généré
Le fichier généré par uic n'est pas un QWidget (ou QMainApplication/QDialog), mais c'est juste une classe de description de l'interface contenant le code de création et d'organisation des widgets, ainsi que les connexions définies dans le designer.
Par exemple, à partir du fichier mywidget.ui :
- un fichier
ui_mywidget.hva être créé, - ce fichier contient une classe dans le namespace Ui :
Ui::MyWidget(pour rappel, MyWidget est le nom que j'ai donné à la form dans le designer), - cette classe contient une fonction importante :
void setupUi(QWidget *MyWidget).
Méthode directe
La méthode directe est relativement simple : un widget va être créé pour servir de conteneur à notre form. Voici le .pro utilisé.
TEMPLATE = app FORMS = mywidget.ui SOURCES = main.cpp
Note : il ne faut pas indiquer le fichier créé par
uic, iciui_mywidget.h, ce dernier étant implicitement déclaré par leFORMS
La fonction main va créer un widget qui servira de conteneur à l'interface : c'est la ligne ui.setupUi(window);. Voici le fichier complet :
#include <QApplication> #include "ui_mywidget.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); QWidget *window = new QWidget; Ui::MyWidget ui; ui.setupUi(window); window->show(); return app.exec(); }
Bien que rapide et simple d'utilisation, cette méthode est très limitée car elle ne permet pas de définir des signaux et slots, de connecter des éléments entre eux... cette méthode est utile pour des fenêtres statiques, comme par exemple des fenêtres A propos
.
Méthode héritage simple
Ici, plutôt que de créer un widget puis de lui appliquer une interface à l'aide de la fonction setupUi, nous allons créer une classe qui hérite de QWidget (ou QDialog/QMainWindow, en fonction du type de form créée) et lui appliquer l'interface dans le constructeur : une instance de la classe Ui::MyWidget est déclarée comme membre de notre classe.
#include "ui_mywidget.h" #include <QWidget> class MyWidget : public QWidget { Q_OBJECT public: MyWidget(QWidget *parent = 0); ... private: Ui::MyWidget ui; };
Il est toujours nécessaire d'appeler la fonction setupUi, mais cette fois-ci dans le constructeur de notre classe MyWidget :
MyWidget::MyWidget(QWidget *parent) : QWidget(parent) { ui.setupUi(this); }
L'avantage ici, c'est que la déclaration de l'interface est un membre de la classe, les différents widgets ainsi que les layouts/signaux/méthodes sont donc directement accessible dans notre classe MyWidget. Nous allons par exemple ajouter un slot clickMe() qui sera connecté sur un clic sur le bouton Cliquez moi !!
(qui porte bien son nom) :
MyWidget::MyWidget(QWidget *parent) : QWidget(parent) { ui.setupUi(this); connect(ui.pushButton, SIGNAL(clicked()), this, SLOT(clickMe())); } void MyWidget::clickMe() { ui.lineEdit->setText("YEAH !!!"); }
Note : remarquez ici que tous les widgets qui composent la form sont appelés à l'aide du membre ui.
Cette méthode offre de nombreux avantages au développeur :
- accès aux différents widgets de la form,
- encapsulation de l'interface dans un widget facilement utilisable,
- possibilité d'utiliser plusieurs interfaces...
A propos des noms
Comme vous l'avez remarqué, j'ai employé le même nom partout :
- 3 fichiers :
mywidget.ui,mywidget.hetmywidget.cpp, - dans le designer, l'interface s'appelle
MyWidgetet lors de la compilation, la classeUi::MyWidgetest créée, - la classe dérivée s'appelle aussi
MyWidget(pas de problème de nommage, la classe précédente étant dans un namespace).
C'est une préférence personnelle, mais il est tout à fait possible d'utiliser des noms différents entre l'interface et la classe dérivée. On peut par exemple imaginer deux classes différentes qui partagent la même interface.
Compilation
Il est nécessaire de modifier notre fichier .pro utilisé pour la compilation, en ajoutant la classe créée :
TEMPLATE = app
FORMS = mywidget.ui
SOURCES = main.cpp \
mywidget.cpp
HEADERS = mywidget.h
Note : il ne faut pas indiquer le fichier créé par
uic, iciui_mywidget.h, ce dernier étant implicitement déclaré par leFORMS
Méthode héritage multiple
Cette méthode est très proche de la méthode précédente (les notes sur le choix des noms et sur la compilation sont toujours valables), la différence étant que l'interface n'est plus un membre de la classe héritée, mais elle est aussi héritée par le widget :
#include "ui_mywidget.h" #include <QWidget> class MyWidget : public QWidget, private Ui::MyWidget { Q_OBJECT public: MyWidget(QWidget *parent = 0); };
Nous avons effectué un héritage privé, pour ne pas exposer l'interface dans les éventuelles sous-classes de notre classe. Mais nous pourrions très bien faire un héritage protégé ou publique afin qu'elle soit accessible.
Là encore, il est nécessaire d'appeler la méthode setupUi, mais les méthodes et les widgets sont directement accessibles dans le code du widget (il n'est plus nécessaire d'utiliser ui) :
MyWidget::MyWidget(QWidget *parent) : QWidget(parent) { setupUi(this); }
Personnellement, c'est ma méthode préférée : elle offre une meilleure lisibilité du code, par contre il n'est plus possible de modifier l'interface (mais était-ce vraiment un problème ?).
Connexions automatiques
Depuis Qt4, il n'est plus possible de créer des slots directement dans le designer et de les connecter graphiquement... il est donc nécessaire de définir la connexion dans le constructeur (comme nous l'avons fait pour la méthode héritage simple). Mais il est possible de définir des connexions automatiques avec des noms de slots utilisant une certaine convention :
on_<nom du widget>_<nom du signal>(<paramètres du signal>);
Lors de la compilation de la form par uic, il génère le code nécessaire à la création automatique des connexions. Dans notre exemple, nous aurions pu utiliser le slot suivant :
void MyWidget::on_pushButton_clicked() { ui.lineEdit->setText("YEAH !!!"); }
Commentaires
Salut
Clarté et précision.
Quelle astuce pour lier un texte à une image, (une fenêtre pour chacun) ?
Bon courage !
inf
Merci bcp... Enfin je viens de compiler ce fichu .ui
Merci pour ce tuto.
ça m'a bcp aidé !!
On aura le droit à d'autres??
Malheureusement, je suis très pris en ce moment...
Vriament, j'ai tout sauf le courage de faire un tuto (et même pas celui d'aller sur le forum). Mais qui sait, peut-être un jour.
>Personnellement, c'est ma méthode préférée : elle offre une meilleure lisibilité du code, par >contre il n'est plus possible de modifier l'interface (mais était-ce vraiment un problème ?).
Je ne comprends pas bien pourquoi , ou du moins ce que tu entends par :
il n'est plus possible de modifier l'interface ?
Merci pour cette méthode. Mais j'ai toujours un problème: lorsque j'utilise les exemples (fichiers annexés) et je les compile ça marche parfaitement mais mon propre fichier que je n'arrive encore pas à compiler ne marche pas du tout; et voilà le message que la console me renvoie:
C:\Users\Eric\Documents\Test Programmation\QtDesigner\Test1>qmake -project
C:\Users\Eric\Documents\Test Programmation\QtDesigner\Test1>qmake
C:\Users\Eric\Documents\Test Programmation\QtDesigner\Test1>make
mingw32-make -f Makefile.Debug
mingw32-make[1]: Entering directory `C:/Users/Eric/Documents/Test Programmation/
QtDesigner/Test1'
c:\Qt\4.3.4\bin\uic.exe mywidget.ui -o ui_mywidget.h
g++ -c -g -frtti -fexceptions -mthreads -Wall -DUNICODE -DQT_LARGEFILE_SUPPORT -
DQT_DLL -DQT_GUI_LIB -DQT_CORE_LIB -DQT_THREAD_SUPPORT -DQT_NEEDS_QMAIN -I"c:\Qt
\4.3.4\include\QtCore" -I"c:\Qt\4.3.4\include\QtCore" -I"c:\Qt\4.3.4\include\QtG
ui" -I"c:\Qt\4.3.4\include\QtGui" -I"c:\Qt\4.3.4\include" -I"." -I"c:\Qt\4.3.4\i
nclude\ActiveQt" -I"debug" -I"." -I"c:\Qt\4.3.4\mkspecs\win32-g++" -o debug\main
.o main.cpp
In file included from main.cpp:2:
mywidget.h:15: error: using-declaration for non-member at class scope
mywidget.h:15: error: expected `;' before "ui"
mingw32-make[1]: *** [debug/main.o] Error 1
mingw32-make[1]: Leaving directory `C:/Users/Eric/Documents/Test Programmation/Q
tDesigner/Test1'
mingw32-make: *** [debug] Error 2
Pourriez vous m'aider ?
Salut, essaye de virer les espaces dans les noms de dossier.
Pour résoudre ton erreur édite ton fichier ui
Il faut que ton QWidget ai le même nom que la classe que tu as définie !