l'asile.fr


Programmation événementielle 2 : Premiers pas avec Qt

article de Def , publié le 28 mars 2006 à 21:24
Si vous avez suivi mes précieux conseils, vous avez réussi la 1ère étape de votre transformation en bombe sexuelle en installant correctement Qt.
Il serait pas mal maintenant d'apprendre à l'utiliser.

Dans ce 2ème article, je vais vous parler de certains widgets de Qt, vous montrer comment gérer des événements et enfin, vous apprendre à utiliser Qt Assistant, votre nouveau meilleur ami.
Maintenant que nous disposons d'un environnement de développement en place (si ce n'est pas le cas, on lira cet article), nous allons pouvoir nous lancer dans la création d'applications.
La première étape va consister à réperer où se trouve de l'aide.
Effectivement, quelle que soit la puissance d'une libraire, son utilisation risque d'être un calvaire si elle n'est pas bien documentée.
Heureusement pour nous, l'aide de Qt est un exemple en la matière.


Mon ami Qt Assistant

L'aide de Qt répond au doux nom de Qt Assistant.
Mais commençons par le lancer pour découvrir le visage de la chose : Menu démarrer >Qt >Assistant.

La page de présentation de l'aide s'affiche avec un bon nombre de liens vers différents sujets.
Pas de raison de paniquer devant cette quantité ; le seul qui va nous intéresser est Tutorial and Examples.

Enfin, "intéresser" est un bien grand mot dans ce cas. Je trouve personnellement que le tutoriel n'est pas des plus pratiques.
Par contre, les différents exemples se révèleront très utiles par la suite. Mes connaissances sur le sujet ne sont peut-être plus à jour mais ce tutoriel me semble être le seul existant sur Qt4. D'où mon envie d'en faire un plus à mon goût.

Mais j'arrête de raconter ma vie. Revenons brièvement à l'assistant en question.
Les choses les plus utiles se trouveront par le biais de l'onglet index. Nous avons là un listing de toutes les classes, méthodes et tout ce que l'on pourrait souhaiter trouver à propos de Qt.
Nous y reviendrons en temps utile.
Laissons donc Qt Assistant de côté pour le moment et réouvrons notre projet créé dans le 1er article.


Structure d'une application Qt

Observons le code de notre fichier main.cpp:

QApplication app(argc, argv);
QDialog *window = new QDialog;

window->show();

return app.exec(); var is_code=true;

Qu'allons nous faire ?

- on instancie un objet QApplication qui sert de base à toute application Qt.
- on instancie l'objet principal de notre application (en principe dérivé de QDialog ou de QMainWindow. On reviendra plus tard sur ce sujet).
- on appelle la méthode show de notre objet principal pour l'afficher.
- on termine le programme avec la valeur renvoyée par la méthode exec().

Ces notions vont être plus faciles à assimiler en démarrant notre application.

Mon premier widget !

Mais qu'est-ce qu'un widget ?

Un widget est un composant graphique. Un bouton est un widget, une fenêtre est un widget, une zone de texte est un widget...

Et si on en ajoutait un de ces widgets dans notre fenêtre toute remplie de vide ?

Commençons par y mettre un champs texte non éditable.
En Qt, c'est l'objet QLabel.
On va donc remplacer l'objet QDialog instancié par défaut par un QLabel, ce qui nous donne le code suivant (en n'oubliant pas d'inclure QLabel à la place de QDialog):

#include <QApplication>
#include <QLabel>

int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QLabel * label = new QLabel();

label->show();

return app.exec();
} var is_code=true;

On compile, on lance et hormis la taille plus petite de la fenêtre, on ne voit aucune différence.
Nous avons créé un QLabel vide. Il ne fallait pas s'attendre à de grands changements. Il faudra maintenant modifier notre objet.
Reprenons Qt Assistant et recherchons, toujours par l'onglet index, la classe QLabel:



Intéressons-nous en premier à ses constructeurs:

QLabel ( QWidget * parent = 0, Qt::WFlags f = 0 )
QLabel ( const QString & text, QWidget * parent = 0, Qt::WFlags f = 0 )

Nous avons utilisé le 1er constructeur en laissant les paramètres par défaut. Le 2ème demande en plus un QString (classe de Qt pour gérer les chaines de caractères mais je pense que ça, tout le monde l'avait deviné).

Si l'on clique sur le constructeur, on peut lire:
Constructs a label that displays the text, text.

Ça tombe bien, c'est exactement ce que l'on voulait faire !

Remplaçons notre instanciation d'objet par:

QLabel * label = new QLabel("Hello world !"); var is_code=true;

Compilation, exécution:



Un peu petite, notre QLabel, quand même.
Regardons les méthodes qui nous sont proposées.
Hmm... y a rien pour redimensionner !

Mais n'oublions pas que nous nous sommes lancés dans de la programmation orientée objet.
Ccomme nous pouvons le voir dans Qt Assistant, QLabel hérite de QFrame, QWidget, QObject et de QPaintDevice. En haut de la page sur QLabel, il y a un lien intitulé "List of all members, including inherited members".
Cliquons dessus !

On arrive sur une nouvelle page listant l'intégralité des méthodes pouvant être utilisées avec notre QLabel. Après une petite recherche, nous découvrons:
resize ( int, int )
Allons-y !

label->resize(200, 50); var is_code=true;



C'est déjà mieux !


Ajoutons un bouton !

Instancions un 2ème widget, plus précisément, un QPushButton:

QPushButton * bouton = new QPushButton();
bouton->show(); var is_code=true;

A l'exécution, on remarque avec horreur que deux fenêtres se lancent !
Pourquoi donc ?
La raison est très simple: pour rester dans une seule fenêtre, on ne peut utiliser la méthode show que sur un seul widget.

- Allons-nous donc être obligés de se contenter de programmes avec un seul widget ?
- Mais non Roger, y'a un moyen pour faire mieux.

Ce que nous allons faire, c'est créer un nouveau widget contenant notre QLabel et notre QPushButton et enfin, afficher ce widget.

Prenons l'habitude de mettre nos classes dans des fichiers séparés pour ne pas finir avec un unique fichier de 12'000 lignes de codes.
Créons 2 fichiers (project -> new file) que l'on sauvegardera sous les noms de "MonWidget.h" et "MonWidget.cpp".

Nous obtiendrons donc:

MonWidget.h :
#include <QDialog>
#include <QLabel>
#include <QPushButton>

class MonWidget : public QDialog
{
Q_OBJECT

public:
MonWidget();

private:
QLabel * label;
QPushButton * bouton;
}; var is_code=true;

Pas grand chose à expliquer ici si ce n'est que nous dérivons de QDialog qui est le widget de base pour les boîtes de dialogue et que nous utilisons la macro Q_OBJECT.
Rien de bien sorcier là dedans; elle sert juste à indiquer à qmake, l'utilitaire qui crée notre makefile, qu'il faudra appeler MOC (Meta Object Compiler) pour traduire le code Qt en code c++.

On peut résumer par : "on s'en bat, on la met sinon ça compile pas".

MonWidget.cpp :
#include "MonWidget.h"

MonWidget::MonWidget()
{
label = new QLabel("Hello World !", this);
label->resize(200, 50);
bouton = new QPushButton("OK", this);
bouton->move(0, 50);
} var is_code=true;

Ici aussi, le code est bien explicite.
Deux précisions, néanmoins: dans les constructeurs de label et de bouton, j'utilise This comme 2ème paramètre. On donne simplement au widget l'objet MonWidget comme parent.

L'utilité ?
Lorsqu'un objet est détruit, tous ses "enfants" le sont également. Un poids en moins pour la gestion de la mémoire.

Deuxième précision: j'utilise la méthode move sur mon bouton. Comme nous n'utilisons pas encore de layout, tous les widgets vont être créés les uns sur les autres. Je descends donc le bouton de 50 pixels pour que le label soit lisible.

main.cpp
#include <QApplication>
#include "MonWidget.h"

int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MonWidget * widget = new MonWidget();

widget->resize(200, 100);
widget->show();

return app.exec();
} var is_code=true;

Evidemment, on n'oublie pas de refaire le makefile puisque nous avons ajouté des fichiers.
Compilons et lançons enfin notre projet:




Utilisons notre bouton !

Je vais refaire un peu de théorie pour vous expliquer comment Qt gère les événements.
Celui-ci utilise un système de signaux et de slots. Un signal est émis par un widget pour annoncer un événement et un widget reçoit des signaux par un slot afin d'effectuer des actions. Il suffit alors de connecter le signal d'un widget au slot d'un autre pour pouvoir gérer l'événement.

Pour connecter, nous allons utiliser connect:

connect(objet1, SIGNAL(signal()), objet2, SLOT(slot())); var is_code=true;

Il est très important d'avoir un signal et un slot avec exactement les mêmes nombre et type de paramètres. Pour l'instant nous allons en utiliser sans paramètre. Cette question fera l'objet d'un article prochain.

Il est bien entendu possible de créer ses propres signaux et slots mais il en existe aussi des prédéfinis pour nous simplifier la tâche.

En pratique, nous allons utiliser le signal "void clicked()" de QPushButton (hérité de QAbstractButton, Qt Assistant est toujours ton ami) et le connecter à un slot de MonWidget que nous allons créer. Lorsque MonWidget recevra le signal sur son slot, nous lui ferons modifier le label.

Commençons par créer le slot dans MonWidget.h:

private slots:
void changeLabel(); var is_code=true;

Puis dans MonWidget.cpp:

void MonWidget::changeLabel()
{
static int nb = 0;
nb++;
label->setText("Nombre de clic : "+QString::number(nb));
} var is_code=true;

Que faisons-nous à ce stade ?
On commence par créer une variable nb qui contiendra le nombre de clics et on l'incrémente à chaque appel de changeLabel.
Ensuite, on modifie le label (setText. Oui, Qt Assistant est plus que jamais ton ami) en affichant le nombre de clics.

Petite précision sur QString::number(nb): la méthode setText demande un QString et nb est un int. On va le transtyper en QString avec cette méthode (Qt Assis... oui, okay, j'arrête).

Le signal existe déjà, le slot a été créé; il ne nous reste plus qu'à les connecter ensemble dans le constructeur de MonWidget grâce à connect:

connect(bouton, SIGNAL(clicked()), this, SLOT(changeLabel())); var is_code=true;

On peut compiler, lancer et hop:



\o/

Nous allons nous arrêter là pour aujourd'hui.
Le prochain article devrait vous présenter de nouveaux widgets pour pouvoir faire un peu plus qu'un bête compteur de clics.

Les sources du programme final peuvent être téléchargées ici.
article de Def — publié le 28 mars 2006 à 21:24
Lien permanent vers cet article | Aller sur le blog de Def
321 oui (sur 657 votes)

Article intéressant ?

Écrire un article
Ceacy
#1 Me, Myself and I

Intéressant ... mais si j'ai bien compris, ce n'est pas exactement en C++ qu'on code, mais dans un truc très ressemblant et modifié pour les besoins de Qt ?

(et, au cas où, si un admin' passe : il y a un problème dans l'affichage des #include, les <Qtmachin> sont interprétés comme du code HTML et sont donc invisibles)


mardi
28 mars 2006, 21:34
 
 

Ah, super tuto, pas trop rapide, pas trop compliqu&eacute;.
Bon, ca a l'air un poil plus complexe que VB6.


mardi
28 mars 2006, 21:39
 
 

Ellendhel
#3 gnagnagna

VB6 c'est loin d'être un langage de programmation qui arrive à la cheville de C++ et d'un paquet de bibliothèques aussi..

Sinon bon article !


mardi
28 mars 2006, 22:40
 
 

Ceacy a écrit :Intéressant ... mais si j'ai bien compris, ce n'est pas exactement en C++ qu'on code, mais dans un truc très ressemblant et modifié pour les besoins de Qt ?


Si on veut. C'est du c++ tout ce qu'il y a de plus standard avec certaines extensions (connect par exemple et... en fait c'est le seul que je connaisse ou auquel je pense pour le moment) qu'on peut considérer comme des simplifications. Moc va se charger de "traduire" ces extensions en code c++ conventionnel.
Si tu regardes dans le dossier debug ou release générés à la compilation, tu vois qu'il y a pour chaque header un fichier moc_toto.cpp. C'est le fichier créé par moc pour remplacer la macro Q_OBJECT. Franchement je ne connais pas les mécanismes employés plus en détail donc si tu veux plus d'explication -> Qt Assistant :)


mardi
28 mars 2006, 23:00
 
 

Ceacy a écrit :
(et, au cas où, si un admin' passe : il y a un problème dans l'affichage des #include, les <Qtmachin> sont interprétés comme du code HTML et sont donc invisibles)

Merci, c'est corrigé.


mercredi
29 mars 2006, 00:15
 
 

gwendal
#6 Détourageophile

Super, merci.
A cette vitesse on va pouvoir apprendre tranquillement, sans manquer un épisode

Ma seul demande serait un bon cours sur la POO appliqué par l'exemple par la suite, mais vraiment plus tard :)


- les fautes
mercredi
29 mars 2006, 08:09
 
 


Ajouter un commentaire

Vous devez être identifié pour poster un commentaire.

# 18:28:50
(Sarki) par contre question comm c'est un zéro pointé, même leur site n'indique pas la sortie officielle...
# 18:25:29
(Sarki) En parlant de AAA, est sorti sur Steam le 14 Novembre un remake plutot pas mal de Little Big Adventure, avec un gameplay moins frustrant
# 07:52:22
(hohun) Et niveau gaming ya la masse de bons jeux qui ne sont plus des AAA qui font turbiner la CG donc le besoin se ressent moins qu'en 2005
# 07:51:51
(hohun) perso j'ai un portable gamer de 2019 qui continue à fonctionner impec, bon évidemment je joue pas des masses à des jeux dernière génération mais les jeux des années 2010 tournent bien en haut niveau de détail
# 07:50:35
(hohun) À mon sens ce sont les deux choses les plus importantes. Après tu peux prendre un bon proc et une bonne CG mais pas besoin de faire dans l'excès
# 07:49:55
(hohun) Et un bon SSD NVMe dernière génération
# 07:49:16
(hohun) Si c'est pour 10 ans tape dans les 32 Go de ram
# 17:38:42
(plantmann) 07:32:35 Oui, je ne savais pas si ça existait encore quand j'ai posé la question. Je pense que je vais me baser là dessus, avec sans doute plus de RAM pour gérer le scénario "Firefox avec 200+ onglets" tranquillement
# 17:35:42
(plantmann) 08:42:13 Fixe, avec un usage hybride jeu/bureautique (et j'inclue des trucs genre Canva en mode montage de vidéo et un firefox avec régulièrement 200+ onglets ouverts dans ce dernier terme)
# 17:32:23
(plantmann) 18:46:42 10 ans pour ma pomme, mais la config commence à souffler fort et j'ai (enfin) un peu de budget. Par contre je vais prendre mon temps pour bien choisir, vu qu'apparemment c'est fait pour une décennie !
lire la suite de la tribune