Note préliminaire : il est préférable de lire chaque "étape" en entier (en particulier les notes ou remarques) avant de la traiter.
Créer un nouveau projet Qt. Pour aller plus vite on pourra dupliquer les fichiers source du premier TP que l'on adaptera ensuite à l'aide de QtCreator. Pour l'instant la principale différence avec le TP précédent sera le contenu de la zone centrale : au lieu d'y placer du texte on va mettre une zone de dessin qui sera une instance d'une classe que l'on va maintenant définir.
Déclarer et implémenter une classe "zone de dessin" (dans de nouveaux fichiers .h et .cpp). Pour l'instant cette classe ne fera pas grand chose : elle doit juste hériter de QWidget et avoir un constructeur adéquat. La taille par défaut d'un QWidget étant nulle (celle-ci étant calculée à partir des enfants qu'il contient), utiliser la méthode setMinimumSize() pour imposer une taille adéquate. Faire en sorte que l'instance apparaisse dans la zone centrale, compiler et tester.
Modifier la classe zone de dessin de telle sorte que l'on puisse interactivement y dessiner un segment de ligne (l'événement press commence le segment, le release le termine, les moves permettent d'ajuster).
Rajouter à la classe zone de dessin un slot setColor(const QColor& color) permettant de specifier la couleur du trait. Tester avec une couleur (par exemple Qt::red) pour vérifier que le comportement est correct.
En utilisant les QActions (comme au premier TP) rajouter des items pour changer la couleur (typiquement rouge, vert, bleu) dans un menu déroulant "Color" et dans la barre d'outils. Noter incidemment que l'on peut créer des QActions sans spécifier d'icone, auquel cas le nom de l'action s'affiche dans la barre d'outil (c'est moins joli mais ca évite de chercher des icones !) Afin de rendre ces actions exclusives (le trait ne pouvant être que d'une seule couleur à la fois) utiliser un QActionGroup.
Faire en sorte que ces actions changent effectivement la couleur du tracé. Afin de réduire le nombre de slots, utiliser un seul slot qui sera "partagé" par ces 3 actions. Mais ceci pose un problème : comment le slot peut-il savoir quelle action a été activée ? Il y a plusieurs solutions à ce probleme. Par exemple utiliser les spécificités de QActionGroup qui envoie un signal qui a une caractéristique intéressante, chaque fois qu'une action est activée. Une autre solution consiste à utiliser la méthode QObject::sender() qui renvoie l'objet qui a envoyé le signal (et ce uniquement pendant l'exécution du slot si c'est le même thread). Voyez-vous d'autres possibilités qui permettraient de partager les slots ?
Faire en sorte que l'on puisse tracer non plus un mais plusieurs traits à la suite. Ces traits auront des couleurs différentes si on change la couleur entre 2 traits (le changement de couleur prenant effet sur les traits qui suivent). On pourra au choix utiliser les list (ou à la rigeur les vector) de la STL ou bien les QList de Qt. Dans tous les cas, faire attention aux fuites mémoire. Enfin, rajouter une action pour supprimer le dernier trait et une autre pour supprimer tous les traits.
Il s'agit maintenant de généraliser l'étape précédente de telle sorte que l'on puisse au choix dessiner des traits, des rectangles ou des ellipses, qui pourront etre de differentes couleurs. Pour ce faire on rajoutera les actions adéquates (dans un menu et la toolbar) que l'on rendra exclusives. (Note: on pourra éventuellement utiliser des QPainterPath, qui ont l'avantage de permettre de généraliser facilement à d'autres formes par la suite si nécessaire).
Refaire l'étape précedédente en utilisant les machines à états de Qt (QState et QStateMachine). Par la même occasion on pourra rajouter d'autres formes géométriques, par exemple des polygones ou des polylines.
En s'inspirant du précédent TP, rendre les items Open et Save fonctionnels de telle sorte qu'ils permettent de sauvegarder ou de relire l'ensemble des objets crées (Noter que la plupart des objets Qt, et en particulier QPen et QPainterPath sont sérialisables).
Comme pour le TP précédent, faire en sorte que l'item Quit ouvre une boîte de dialogue pour demander confirmation avant de quitter le programme. Cependant ceci n'est pas suffisant car l'utilisateur peut également quitter le programme en cliquant sur le bouton de fermeture standard à côté du titre de la fenêtre. Modifier la MainWindow afin que cette action ouvre également la boîte de dialogue de confirmation (NB: il faudra redéfinir QWidget::closeEvent()).
En utilisant Designer, on va maintenant créer un QtDockWidget qui va permettre de contrôler la couleur plus finement ainsi que l'épaisseur des traits (les changements prendront effet sur les traits suivants). Pour utiliser Designer il suffit de cliquer sur le fichier .ui dans QtCreator. Notez aussi que le constructeur de MainWindow doit commencer par ui->setupUi(this) sinon les objets spécifiés à l'aide de Designer ne seront pas créés.
Le DockWidget contiendra 4 sliders horizontaux avec les titres adéquats: un pour chacune des 3 couleurs R, G B et un pour l'épaisseur. Créer ces widgets à l'aide de Designer et en faisant en sorte que l'on ne puisse pas faire disparaitre le DockWidget. Rajouter ensuite les slots adéquats et les lier aux sliders en utilisant l'auto-connexion pour les 3 premiers (couleurs) et l'éditeur de connexions pour l'épaisseur (Note: cf. Editer Signaux/Slots dans le menu Edition de Designer)
Même question sauf qu'il s'agit maintenant de modifier les attributs des formes géométriques qui ont déjà été créées. Ceci nécessite de pouvoir les sélectionner, typiquement en utilisant les fonctions d'interesection ou d'inclusion de QPainterPath. Le problème peut rapidement devenir non trivial si les formes sont superposées. Que faire ?