La solution que je propose pour le client.
Une seule classe générale :
- Une classe TcpSocket, héritant de QTcpSocket, permettant tout comme la classe TcpServer du serveur de gérer les envois/réception des données (du serveur au client et inversement).
Tout comme pour TcpServer, il s'agit d'une classe d'interface, ce n'est pas non plus à elle de dire quoi faire à la réception de tel ou tel message.
Il est important de savoir que chaque module, dans le cas du client, est relié à l'interface graphique. Par exemple, le module GestionRecettes du client va être relié à une interface particulière dédiée à l'affichage des recettes et à deux trois choses comme des boutons "Ajouter/Retirer aux/des favoris" ou encore "Modifier la recette".
Bref, on va réaliser une interface Model/View : on va faire en sorte que chaque module soit relié à une vue (une interface graphique, par exemple réalisée avec Qt Designer). Du genre, la classe GestionRecettes aura à l'intérieur une instance de la classe GestionRecettesInterface qui n'est ni plus ni moins l'interface graphique de la frame de visualisation des recettes. De même, la classe GestionConnexion aura à l'intérieur une instance de la classe GestionConnexionInterface, l'interface graphique où on permet à l'utilisateur de se connecter au serveur avec un identifiant et un mot de passe.
Une question se pose : comment transiter d'une interface d'un module à une autre, à savoir qu'on aurait par exemple un menu sur le côté nous permettant de changer de vue ? Réponse simple : les machines à états.
MainWindow sera en fait une machine à états qui, selon l'état qu'on va lui demander d'adopter ("EtatAffichageRecettesInterface", "EtatAffichageConnexionInterface", etc.), va faire du setVisible(true/false) ou ce qu'elle souhaite sur les interfaces graphiques des modules afin de n'avoir visible que les interfaces que l'on souhaite avoir à l'écran. L'étude de QStateMachine peut être intéressante.
Jusque là, on a abordé la partie view de l'approche Model/View. Qu'en est-il de la partie Model ? L'interface graphique ne doit aucunement être responsable des décisions, c'est le module qui gère ça. Le module gère également à l'intérieur de sa description de classe des propriétés (Q_PROPERTY ou autre), en fait des variables contenant les données importantes au bon fonctionnement du module.
Illustration d'un changement d'états et des conséquences :
- L'utilisateur, une fois connecté, appuie sur le bouton "Recettes favorites" du menu.
- Appel d'une fonction de changement d'état de MainWindow pour lui demander de passer à l'état "EtatAffichageRecettesFavorites".
- MainWindow masque l'ancienne frame, s'il y en avait (par exemple, si "EtatAffichageRecettesInterface" était l'ancien état, MainWindow masque la GestionRecettesInterface) puis affiche la nouvelle (GestionRecettesFavoritesInterface).
- La classe module (GestionRecettesFavorites) est notifiée du changement d'états par signaux slots (elle serait connectée à un stateChanged() ou autre de MainWindow) et entre dans la fonction initInterface().
- Dans cette fonction, envoi d'un message du client au serveur par le biais de TcpSocket (type : RequeteRecettesFavoritesListe, valeurs : aucune, vu que le serveur doit être capable de déterminer de qui vient le message).
- On zappe la partie TcpSocket, c'est la même que pour TcpServer : envoi du message puis tôt ou tard, réception d'un message du serveur, interprétation, envoi du signal et catch du signal par le module GestionRecettesFavorites qui dit gogogo, on interprète.
- Dans la fonction de réception de la liste des favoris, on va juste mettre à jour une propriété interne (FavListe, contenant les Ids et les noms des recettes en favoris - c'est là l'aspect Model de l'architecture) et notifier via un signal/fonction la classe interface de mettre à jour ses données selon la nouvelle liste.
- La classe interface (GestionRecettesInterface) met à jour ses données graphiques, fin de l'histoire.
Illustration d'une action utilisateur nécessitant (déclenchement d'une action depuis l'interface graphique associée au module, en gros) :
- L'utilisateur clique sur le bouton "Ajouter aux favoris".
- Envoi d'un signal depuis l'interface graphique disant que le bouton s'est fait cliquer dessus ;
- La classe module étant connectée au signal, on entre dans une de ses fonctions.
- Envoi d'un message au serveur (type : AjoutRecetteFavorites, valeurs : [Id: 4953, Nom: "Tarte aux pommes"]).
- Le serveur va répondre en envoyant la nouvelle liste des favoris. Réception dans le module TcpSocket des données, on rattrape les opérations de la liste illustrant blablabla, c'est-à-dire (let's copy/paste) :
- Déjà implémentée, à ne pas dupliquer : Dans la fonction de réception de la liste des favoris, on va juste mettre à jour une propriété interne (FavListe, contenant les Ids et les noms des recettes en favoris - c'est là l'aspect Model de l'architecture) et notifier via un signal/fonction la classe interface de mettre à jour ses données selon la nouvelle liste.
- Déjà implémentée, à ne pas dupliquer : La classe interface (GestionRecettesInterface) met à jour ses données graphiques, fin de l'histoire.
Partager