Catégories: Java | SWT | Langage | Article
Interface SWT selon le modèle MVC
Un article de ToutProgrammer.com.
Utiliser le paradigme MVC pour développer une application permet de mieux organiser la gestion des évènements et de mutualiser son code. Bref que du bonheur.
[modifier] Introduction
SWT est une librairie Java proposée par le projet OpenSource Eclipse pour développer des applications avec des interfaces graphiques riches. Le site officiel de ce toolkit est à l'adresse: http://www.eclipse.org/swt/.
[modifier] MVC les bases
MVC ou Model/View/Controller.
Le paradigme MVC est un schéma de programmation qui propose de séparer une application en 3 parties:
- Le modèle, qui contient la logique et l'état de l'application.
- La vue, qui représente l'interface utilisateur.
- Le contrôleur, qui gère la synchronisation entre la vue et le modèle. Il réagit aux actions de l'utilisateur en effectuant les actions nécessaires sur le modèle. Ce dernier notifie les vues lorsqu'il a été modifié.
Exemple de séquence d'un traitement :
- Le contrôleur et le modèle sont instanciés,
- Les vues sont instanciées et s'abonnent au modèle pour être à l'écoute des changements,
- L'utilisateur manipule l'interface homme/machine. Un évènement est envoyé. Cet évènement est récupéré par le contrôleur,
- Le contrôleur effectue l'action demandée par l'utilisateur en appelant les méthodes nécessaires sur le modèle,
- Le modèle étant modifié, il envoie une notification aux différentes vues du changement effectué,
- Chaque vue interroge le modèle afin de connaitre son état,
- L'utilisateur voit le résultat de son action.
[modifier] Les fonctions de l'application
L'application que nous allons mettre en oeuvre est composée d'une fenêtre et d'un icône qui apparait dans la barre des tâches.
Au lancement de l'application le fenêtre n'apparait pas. Si on double clique sur l'icône la fenêtre apparait. On peut également provoquer cette action en utilisant le menu contextuel associé à l'icône et en choisissant l'action 'Afficher détail'.
[modifier] Pré-requis
Pour lancer cette application vous devez utiliser la version Eclipse M8 ou supérieure. Le projet dans lequel est développé l'application doit contenir à la racine les DLLs : swt-awt-win32-3044.dll et swt-win32-3044.dll (versions pour Eclipse M8).
[modifier] Les packages
L'application est composée de trois packages :
- model
- controller
- view
Leur nom est assez explicite. Avant de détailler pas à pas l'application voici la vue d'ensemble des classes :
La classe Main.java est le point d'entrée de l'application. Elle a pour rôle d'instancier le controleur le modèle et les vues. L'icône correspond à une vue, et la fenêtre correspond à une autre vue du même modèle métier.
[modifier] Point d'entrée : Main.java
import controller.*; import model.*; import view.*; public class Main { DocumentModel document= new DocumentModel(); ControllerPrincipal controller = new ControllerPrincipal(); SystrayView systray = new SystrayView(); systray.setControllerPrincipal(controller ); systray.setDocumentModel(document); WindowView window = new WindowView(); window.setControllerPrincipal(controller ); window.setDocumentModel(document); window.run(); } }
La méthode run() met en attente la fenêtre. Tant que la fenêtre n'est pas fermée elle continue à fonctionner.
public class WindowView extends ViewAbstract{ Shell shell = null; Display display; ... public void run() { while (!shell.isDisposed ()) { if (!display.readAndDispatch()) display.sleep(); } display.dispose(); } ...
[modifier] Instanciation des éléments graphiques
Avant de détailler le modèle MVC et le système de notification décrivons rapidement la mise en place des vues.
Deux vues doivent être créées: SystrayVuequi correspond à l'icône dans la barre des tâches et WindowViewqui correspond à la fenêtre.
L'icône qui doit apparaître dans la barre des tâches est facilement mise en place depuis la version M8 d'Eclipse. Le snippet Tray montre un exemple.
Pour créer l'icône il suffit de mettre dans le constructeur de la vue SystrayViewles lignes suivantes :
public SystrayView() { // init icon & tray display = Display.getDefault(); shell = new Shell (display); Tray tray = display.getSystemTray(); item = new TrayItem (tray, SWT.NONE); item.setImage(logo2); item.setToolTipText("Test MVC"); ..
Comme on souhaite faire apparaitre un menu contextuel à cet icône on ajoute les lignes suivantes :
// Tray menu mi1.setText ("Lancement traitement"); .. mi2.setText ("Afficher détail"); .. mi3.setText ("Fermer"); .. // Affiche le menu si on clique sur l'icône item.addListener (SWT.MenuDetect, new Listener () { menu.setVisible (true); } });
La vue WindowViewcorrespond à une fenêtre qui contient une champ texte non éditable. Elle est mise en place par son constructeur :
public WindowView() { display = Display.getDefault(); shell = new Shell (display); shell.setText("TITRE"); shell.setBounds(50, 50, 300, 100); .. titre = new CLabel(shell,SWT.NULL); titre.setText("blablabla.."); }
[modifier] ViewAbstract et ViewInterface
Les vues SystrayViewet WindowViewdérivent dans la vue abstraite ViewAbstract. Cette classe implémente l'interface InterfaceView.
package view; import controller.*; import model.*; public abstract class ViewAbstract implements ViewInterface{ DocumentModel document; ControllerPrincipal controller; public void setDocumentModel(DocumentModel newdocument) { this.document= newdocument; document.addDocumentListener(this); } public void setControllerPrincipal(ControllerPrincipal newcontroller) { this.controller= newcontroller; } public ControllerPrincipal getControllerPrincipal() { return this.controller; } public DocumentModel getDocumentModel() { return this.document; } }
package view; import controller.*; import model.*; public interface ViewInterface { static int ACTION_FERMER = 0; static int ACTION_VERIFIER_DOCUMENT_LIBRE = 1; static int ACTION_CHANGERMODE = 2; static int ACTION_CACHERFENETRE = 3; public ControllerPrincipal getControllerPrincipal(); public DocumentModel getDocumentModel(); }
[modifier] Mise en place des listeners
Pour respecter le modèle MVC nous allons rediriger tous les évènements que l'on souhaite intercepter vers la classe Contrôleur. Pour cela nous allons créer une classe SwtControllerListenerqui va contenir toutes les actions à gérer.
package controller; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Event; import view.*; public class SwtControllerListener implements Listener{ private int actionCode; private ViewInterface view; public SwtControllerListener(ViewInterface view, int actionCode) { this.view = view; this.actionCode = actionCode; } this.view.getControllerPrincipal().actionPerformed(this.view, this.actionCode); } }
Lorsque l'on ajoute la capture d'un évênement sur un contrôle il devient possible de rediriger le traitement à la classe Controleur. Pour cela il suffit d'instancier la classe SwtControllerListeneren passant le code de l'action en paramêtre.
Par exemple si quand on double-clique sur l'icône de la barre des tâches on souhaite lancer une action on ajoutera après avoir créé l'objet tray la ligne suivante : item.addListener(int eventType, Listener listener). Exemple :
Tray tray = display.getSystemTray(); item = new TrayItem (tray, SWT.NONE); item.setImage(logo2); item.setToolTipText("Test MVC"); item.addListener (SWT.DefaultSelection, new SwtControllerListener(this, ViewInterface.ACTION_CHANGERMODE));
Les actions sont centralisées dans le contrôleur qui appelle la fonction demandée :
package controller; import model.*; import org.eclipse.swt.widgets.*; import view.*; public class ControllerPrincipal { public void actionPerformed(ViewInterface view, int actionCode) { switch (actionCode) { case ViewInterface.ACTION_FERMER : fermer(); break; case ViewInterface.ACTION_VERIFIER_DOCUMENT_LIBRE : verifier(view); break; .. default : break; } } void verifier(ViewInterface view) { DocumentModel document = view.getDocumentModel(); document.setStatus(DocumentInterface.STATUS_ENCOURS); .. Traitement .. document.setStatus(DocumentInterface.STATUS_TERMINE); } void fermer() { Display.getDefault().dispose(); }
[modifier] Ajout des abonnements
Dès que le modèle est modifié il notifie toutes les vues. Pour cela les vues doivent s'être abonnées au modèle. Cette action se fait au lancement de l'application lorsque l'on rattache le modèle à la vue :
systray.setDocumentModel(document);
Derrière ce code on effectue l'abonnement :
public abstract class ViewAbstract implements ViewInterface,DocumentInterface{ DocumentModel document; ControllerPrincipal controller; public void setDocumentModel(DocumentModel newdocument) { this.document = newdocument; document.addDocumentListener(this); } ..
public class DocumentModel{ String status; boolean detailVeille; private NotifyChangeDocument notifyChangeDocument; .. public void addDocumentListener(DocumentInterface documentListener) { notifyChangeDocument.addDocumentListener(documentListener); }
Tous les abonnements sont centralisés dans un attribut défini dans le modèle :
private NotifyChangeDocument notifyChangeDocument;
C'est la classe NotifyChangeDocumentqui permet de notifier les vues abonnées.
[modifier] Gestion de la notification
Lorsque le modèle souhaite envoyer une notification il appelle la méthode fire* qui concerne le changement effectué :
Exemple si le mode d'affichage change on appelle la méthode fireModeChanged() :
public class DocumentModel{ .. public void setDetailVeille(boolean newDetailVeille) { detailVeille = newDetailVeille; notifyChangeDocument.fireModeChange(); } ..
La classe NotifyChangeDocumentparcourt la liste des vues abonnées et appelle la méthode correspondant à ce changement.
public class NotifyChangeDocument { .. protected void fireModeChange() { Vector v; synchronized(this) { } while (iter.hasNext()) ((DocumentInterface) iter.next()).modeChanged(); } ..
Dans chaque vue la méthode prend en compte la notification et modifie la vue en conséquence.
Pour la vue systrayle changement de mode fait changer de logo.
public class SystrayView extends ViewAbstract{ .. public void modeChanged() { // Change d'icône selon le status if(getDocumentModel().isDetailVeille()) { item.setImage(logo1); mi2.setText ("Cacher détail"); } else { item.setImage(logo2); mi2.setText ("Afficher détail"); } } ..
Pour la vue windowle changement de mode fait apparaître ou disparaître la fenêtre :
public class WindowView extends ViewAbstract{ .. public void modeChanged() { // Affiche ou cache la fenêtre selon le status if(getDocumentModel().isDetailVeille()) { shell.setVisible(true); shell.forceActive(); } else { shell.setVisible(false); } } ..
[modifier] Ressources
Code source du projet de l'article
[modifier] Conclusion
Nous avons vu à l'aide d'un exemple simple comment il est possible de mettre en place une interface SWT selon le modèle MVC.
[modifier] Historique de l'article
Cet article, réalisé par Olivier MALE, a été publié pour la première fois sur ToutProgrammer.com (1ème version) le 10 mai 2004.




