I. Introduction▲
Le but et l'intérêt premier de ce tutoriel est, à partir d'une version basique d'un éditeur GMF, d'obtenir quelque chose de visuellement agréable et possédant une utilisation facilitée.
I-A. Prérequis▲
Afin de savourer pleinement ce tutoriel avancé sur GMF, je recommande d'avoir une certaine expérience sur EMF et GMF. Pour cela, il est possible de trouver plusieurs tutoriels sur ce même site ou simplement en parcourant la toile.
I-B. Installation▲
I-B-1. Version d'Eclipse▲
Le tutoriel a été développé sur la version 4.2 (Juno).
http://www.eclipse.org/juno/Redirection vers Eclipse.org
I-B-2. Les différents plugins pour faire marcher GMF▲
Pour réaliser un éditeur exploitant la technologie GMF, il est obligatoire d'installer quelques plug-ins nécessaires au bon fonctionnement du framework. Il suffit d'aller dans Help --> Install New Software. Sélectionner All site available dans la liste Work with. Et enfin, taper GMF dans la barre de recherche. Sélectionner tous les plug-ins présents dans le sous-menu modeling. Nous sommes enfin prêt pour l'aventure GMF avancé.
I-C. Exemple▲
Ce tutoriel est une suite logique d'un précédent cours basé sur EMF :
http://mbaron.developpez.com/tutoriels/eclipse/emf/creation-instanciation-modeles/Création et instanciation EMF
Le modèle EMF ici utilisé est celui du carnet d'adresses. Voici son UML :
En suivant toutes les étapes de la partie II, nous obtiendrons éditeur simple, permettant de gérer des carnets d'adresses.
Cependant, cet éditeur n'est absolument pas utilisable. Le comprendre n'est pas évident, l'exploiter encore moins. C'est pour cela que nous allons y appliquer une surcouche via le framework GMF.
II. Une version basique de GMF▲
Ce framework va permettre de mettre en place un éditeur graphique utilisable et compréhensible par la majorité des utilisateurs. Dans cette partie, nous allons brièvement rappeler les étapes permettant d'obtenir l'éditeur de base de GMF. Si vous souhaitez obtenir plus de détails sur cette étape primordiale du développement, référez-vous à ces différents tutoriels : [1], [2] et [3]
II-A. Modification du modèle original▲
Afin que le modèle puisse être parfaitement adapté à GMF, il est nécessaire d'y apporter quelques modifications :
- Changer le lien location de la classe Person en 0..1,
- La relation entre Person et Adress devient une agrégation,
- Créer un lien entre AddressBook et Address. En effet, nous souhaitons modéliser un seul carnet d'adresses, et la représentation par GMF des liens entre classes nécessite une connection entre la racine et chaque classe que l'on souhaite afficher.
II-B. Le premier résultat GMF▲
Pour les absents, voici un bref rappel des différentes étapes pour obtenir votre premier éditeur graphique.
Créer un nouveau projet dans Graphical Modeling Framework --> Graphical Editor Project, choisir un nom et cocher la case permettant d'afficher le gmf dashboard. Cette fenêtre est l'outil principal de génération.
Récupérer le fichier ecore du précédent tutoriel sur EMF. Nous allons en faire notre fichier Domain Model. Déplacer le fichier dans le dossier model du projet. Retour sur le dashboard.
Cliquer sur Select de la case Domain Model et choisir le modèle EMF importé via précédent tutoriel.
Après cette étape, nous devons générer le fichier .genmodel. Pour cela, il suffit de cliquer sur Derive à gauche du Domain Model.
Nous obtenons un fichier .genmodel qui permettra de générer les codes d'implémentation du modèle. Pour cela, il nous faut ouvrir le fichier et effectuer un clique droit sur la racine.
Cliquer sur Generate All. Cela aura pour effet de générer 3 nouveaux projets et le code d'implémentation du modèle ecore de l'adressBook.
Cette étape passée, nous pouvons attaquer les attributs propres à GMF. Pour organiser au mieux le projet, je conseille de créer un nouveau dossier afin d'y stocker les fichiers propres à la modélisation graphique (view par exemple).
II-B-1. Le .gmftool▲
Maintenant, nous pouvons générer l'aspect que va prendre les noeuds ainsi que les outils de création d'instances EMF. Nous allons commencer par les outils.
Nous obtenons donc un fichier .gmftool. Ce dernier permet de définir 3 outils permettant de créer une personne, une adresse et un lien entre eux. Nous y reviendrons plus tard pour y apporter quelques modifications.
II-B-2. Le .gmfgraph▲
Maintenant, il faut générer le fichier permettant de définir la forme et l'apparence des noeuds et Labels du futur diagramme.
Après tout ça, nous obtenons donc un fichier permettant de générer l'aspect de l'éditeur. Tout est modifiable, nous y reviendrons au prochain chapitre.
II-B-3. Le .gmfmap▲
La prochaine étape concerne la liason des 3 principaux fichiers. En effet, toutes les informations sont stockées dans le modèle ecore, le .gmftool et .gmfgraph.
Il existe un souci lors de la génération du .gmfmap. En effet, il faut bien vérifier que les outils choisis soit les bons pour les différentes représentations. Regardez bien dans les propriétés que les bons outils sont liés aux bonnes figures (Node Mapping <Address/Address> --> creation tool Address).
Maintenant que vous avez toutes les pièces en main, il vous suffit de les transformer en un générateur de code unique.
Et voilà, nous pouvons générer le diagramme. Nous obtenons un nouveau projet : AdressBook.diagram. Pour jeter un oeil à l'application de base, éxecuter le diagramme. Pour cela, créer une nouvelle application Eclipse.
Dans l'onglet plug-ins, launch with : plug-ins selected below only. Sélectionner tous les plug-ins AdressBook du workspace. Ensuite, cliquer sur Add Required Plug-ins. Il est probable que l'application ne fonctionne pas. Ayant eu des problèmes avec mon application aussi, ajouter org.eclipse.ui.ide.application et org.eclipse.core.net
Dans la nouvelle instance d'Eclipse, créer un nouveau projet. Dans ce projet, y ajouter un nouvelle exemple et choisir AddressBook Diagram.
Et là, révélation : "Mon dieu, que c'est laid ! Comment puis-je faire pour améliorer mon interface et la rendre merveilleusement utilisable ?". La suite de ce tutoriel repose sur des trucs et astuces que j'ai découvert pendant la réalisation d'un projet. La liste que je propose est loin d'être exhaustive. Mais ce sont des choses que j'ai découverte au fur et à mesure que j'utilisais le framework GMF. Ainsi, je ne peux que vous conseiller de mettre les mains dans le cambouis et essayer de découvrir par vous même. Cependant, je mets à disposition quelques idées et quelques méthodes pour mettre en place un éditeur correct et pas désagréable à regarder.
III. Personnalisation d'un noeud▲
Comme vous le savez sûrement, les figures utilisées par GMF sont issus de l'API Draw2d. Donc avant toute chose, je vous conseille d'aller jeter un oeil sur cette API et d'explorer par vous même les possibilités de cette partie. Le framework GMF peut s'utiliser de deux façons :
- La méthode de génération automatique depuis les fichiers évoqués dans la partie précédente finalisés par des retouches dans le code.
- Écrire tout le code à la main (seulement pour les vrais barbus).
III-A. Forme du noeud : Utilisation de draw2d et / ou d'image SVG▲
Pour cela, il suffit de changer le type de forme dans le gmfgraph. Changer le Rectangle PersonFigure en ce que vous souhaitez. Il est également possible de dessiner la forme qui nous intéresse le plus. Pour cela, je vous conseille le site suivant : [4].
Il est aussi possible d'utiliser des images SVG pour mettre en forme votre noeud. Ici, nous allons changer notre rectangle d'adresse par une enveloppe. Pour cela, il suffit de se servir du plugin org.eclipse.gmf.runtime.lite.svg [5]. Dans le fichier gmfgraph, remplacer le rectangle AddressFigure par un SVGFigure pour obtenir une nouvelle forme.
Transformer et générer le diagramme. Vous devriez obtenir le résultat suivant :
III-B. Couleur du noeud▲
Il est également possible de customiser les couleurs des noeuds. Pour cela, il suffit de rajouter un fils à l'élément de figure de votre choix :
III-C. Les labels et les icônes▲
Nous avons envie de modifier le noeud représentant une personne. L'objectif ici est d'obtenir un affichage des différentes informations considérées pertinentes de l'objet Personne. Pour cela, direction le gmfgraph. Je conseille fortement de créer un rectangle pour chaque sous partie d'un noeud afin de ne pas compliquer chaque layout :
Il est important d'appliquer un layout à tout les labels sans quoi ils ne seront pas affichés dans la figure. Il est également nécessaire de modifier les propriétés des layout des labels PersonIdentifierFigure et PersonAgeFigure.
Passer les paramètres de Grab Excess Horizontal Space et Grab Excess Vertical Space à true. Modifier les Horizontal Alignement de FamilyName et FirstName dans leur GridLayout Data : respectivement BEGINNING et END.
Les labels permettent aussi d'afficher les icônes. Par défaut, l'affichage est activé pour tous les labels. Pour les désactiver, il suffit de passer la propriété Element Icon des éléments suivant à false :
Indiquer à GMF dans le GMFMap l'affichage souhaité :
Les feature labels permettent d'afficher les labels contenant un attribut. Ici, nous souhaitons afficher le nom de famille, le prénom et l'âge. Ce seront trois labels automatiques générés par GMF. L'identifiant sera fait à la main.
Pour l'âge, passer le read only à true. Il est également possible de changer l'affichage d'un texte via ces labels. Pour le feature label de l'âge, modifier le view pattern : {0} ans. Le {0} correspond au premier attribut qui sera affiché par le label.
Transformer et ouvrir le fichier gmfgen. Modifier les noms de EditPart :
Transformer et générer à nouveau l'éditeur. Accéder au fichier PersonEditPart. Il faut vérifier les placements dans le BorderLayout, GMF ne sachant pas les placer lors de la génération. Changer l'assignation de chaque sous rectangle. Dans la fonction createContents() :
this
.add
(rectangleTopFigure0, BorderLayout.TOP);this
.add
(rectangleCenterFigure0, BorderLayout.CENTER);this
.add
(rectangleBottomFigure0, BorderLayout.BOTTOM);
Rajouter également la ligne suivante :
fFigurePersonIdentifierFigure.
setText
("
Identifier
:
nullnull0
"
);
Pour avoir un renouvellement automatique de l'identifiant, overrider la fonction handleNotificationEvent de PersonEditPart :
/**
*
@generated
NOT
*/
@
Override
protected
void
handleNotificationEvent
(Notification notification){
if
(notification.getNotifier
()instanceof
Person){
if
(notification.getFeature
()instanceof
EAttributeImpl){
String name
=
((EAttributeImpl) notification.getFeature
()).
getName
();if
(name.equals
("
age
"
)|
|
name.equals
("
firstName
"
)|
|
name.equals
("
familyName
"
)){
this
.getPrimaryShape
().
getFigurePersonIdentifierFigure
().
setText
("
Identifier
:
"
+
((Person) notification.
getNotifier
()).
getIdentifier
());}
}
}
super
.handleNotificationEvent
(notification);}
Pour modifier l'icône, se placer dans PersonFamilyNameEditPart et changer la fonction getLabel pour retourner une image.
/**
*
@generated
NOT
*/
protected
ImagegetLabel
(){
Bundle bundle
=
Platform.getBundle
("
AdressBook.diagram
"
);IPath path
=
new
Path
("
icons/node/person.gif
"
);URL url
=
FileLocator.find
(bundle, path,null
);ImageDescriptor desc
=
ImageDescriptor.createFromURL
(url);return
desc.createImage
();}
IV. Personnalisation d'un lien▲
IV-A. Les différents attributs des liens▲
GMF est un outil permettant une customisation très avancée d'un éditeur graphique. Il est donc possible d'améliorer l'apparence d'un lien entre deux noeuds. Ces modifications peuvent être apportées directement dans l'éditeur. Cependant, il est souvent souhaitable de changer les paramètres par défaut. Par exemple, nous désirons obtenir des liens orthogonaux et assez intelligent pour éviter les autres formes présentes sur le graphique.
Pour cela, ouvrir le fichier PersonLocationEditPart et modifier la fonction createConnectionFigure() :
protected
ConnectioncreateConnectionFigure
(){
PersonLocationFigure figure
=
new
PersonLocationFigure
();AbstractEMFOperation emfOp
=
new
AbstractEMFOperation
(getEditingDomain
(),"
line
routing
setting
"
){
@
Override
protected
IStatusdoExecute
(IProgressMonitor monitor,IAdaptable info)
throws
ExecutionException{
RoutingStyle style
=
(RoutingStyle) ((View)getModel
()).
getStyle
(NotationPackage.Literals.ROUTING_STYLE);style.
setRouting
(Routing.RECTILINEAR_LITERAL);//
or
//
Routing.TREE_LITERAL
style.
setAvoidObstructions
(true
);style.
setClosestDistance
(true
);return
Status.OK_STATUS;}
}
;try
{
OperationHistoryFactory.
getOperationHistory
().execute
(emfOp,null
,null
);}
catch
(ExecutionException e){
throw
e;}
return
figure;}
IV-B. Ajouter un label▲
Nous pouvons également choisir de rajouter un label à chaque lien pour mieux le détailler. Aller dans le fichier genmap, et supprimer le label mapping du numéro d'adresse pour le noeud correspondant. Ensuite, créer un fils pour le link mapping de Location. Nous pouvons également créer un label dynamique permettant l'affichage d'une variable. Pour ce dernier, je préconise d'utiliser une entité sous forme de lien plutôt qu'une relation entre deux objets [6].
V. Personnalisation de la fenêtre▲
V-A. Grille d'alignement▲
Afin d'afficher les lignes permettant d'aligner les figures entre elles, il est nécessaire rajouter la fonction suivante dans la classe DiagramRulersAndGridPreferencePage :
/**
*
@generated
NOT
*
@param
preferenceStore
*/
public
static
void
initDefaults
(IPreferenceStore preferenceStore){
preferenceStore.
setDefault
(IPreferenceConstants.PREF_SNAP_TO_GEOMETRY,true
);preferenceStore.
setDefault
(IPreferenceConstants.PREF_SNAP_TO_GRID,false
);}
V-B. Règle, grille et unités▲
Afficher une règle ainsi qu'une grille est également possible. Pour cela, rajouter ces quelques lignes à la fonction précédente :
preferenceStore.
setDefault
(IPreferenceConstants.PREF_SHOW_RULERS,true
);preferenceStore.
setDefault
(IPreferenceConstants.PREF_GRID_SPACING,1
);preferenceStore.
setDefault
(IPreferenceConstants.PREF_RULER_UNITS,RulerProvider.UNIT_CENTIMETERS);
preferenceStore.
setDefault
(IPreferenceConstants.PREF_SHOW_GRID,true
);
VI. Personnalisation de la Palette▲
VI-A. Changer les différentes icônes▲
Modifier les paramètres du fichier gmftool. En effet, chacun des outils possède des enfants lui permettant de retrouver son icône. Afin de les modifier, supprimer les enfants déjà présents et les remplacer par Small Icon Bundle Image. Dans les propriétés, changer le Bundle par AdressBook.diagram. Ensuite, changer le chemin par celui de l'icône au sein du plug-in :
VI-B. Ranger pour mieux régner▲
Il est possible de créer des menus pour ranger les outils et ainsi mieux les ordonner. Créer un nouveau tool group, et modifier ses propriétés :
Déplacer les outils précédemments créés. Faire de même pour les liens :
VII. Personnalisation avancée : la popup-bar▲
VII-A. Qu'est-ce à dire que ceci ?▲
Le popup-bar est une des politiques d'attitudes des attributs GMF. En effet, l'exemple le plus simple serait de citer la petite popup que vous obtenez si vous laissez votre souris immobile dans la fenêtre princpale. Utile me direz-vous. Et vous auriez totalement raison. Si dans un cas bien précis, vous souhaitez créer plusieurs versions d'un noeud, par exemple, un homme ou une femme, vous devez d'abord créer le noeud par défaut puis le modifier. Et bien, je vais vous montrer comment, sur une popup, avoir les deux !
Tout d'abord, modifier le modèle Ecore :
Regénérer l'éditeur.
VII-B. Marche à suivre▲
Il existe une classe en Singleton dans GMF nommée ElementInitializer. Cette classe permet d'appliquer des changements à un objet avant qu'il ne soit afficher en tant que noeud dans l'éditeur. Ici, nous aimerions choisir homme ou femme à la création. Nous allons y ajouter deux fonctions et un attribut :
private
Sex _sexe;/**
*
@generated
NOT
*/
public
void
setSex
(Sex value){
this
._sexe=
value;}
/**
*
@generated
NOT
*/
public
void
initializePerson
(Person newPerson){
newPerson.
setSexe
(this
._sexe);this
._sexe=
Sex.HOMME;}
Maintenant, nous devons indiquer à GMF d'appliquer cette modification à la création d'une personne. Pour cela, il faut modifier le ficher PersonCreateCommand et plus précisement, la fonction doExecutWithResult() :
/**
*
@generated
NOT
*/
protected
CommandResultdoExecuteWithResult
(IProgressMonitor monitor,IAdaptable info)
throws
ExecutionException{
Person newElement
=
AddressbookFactory.eINSTANCE.createPerson
();ElementInitializers.
getInstance
().initializePerson
(newElement);AddressBook owner
=
(AddressBook)getElementToEdit
();owner.
getContainsPerson
().add
(newElement);doConfigure
(newElement, monitor, info);((CreateElementRequest)
getRequest
()).setNewElement
(newElement);return
CommandResult.newOKCommandResult
(newElement);}
Le modificateur est maintenant prêt. Le plus compliqué reste à faire. Télécharger le fichier AdressBookPopupBarEditPolicy. J'y ai rajouté plusieurs éléments :
- Rajout du sexe dans le PopupBarDescriptor. Ce descriptor contient l'image, le texte et le type d'objet à ajouter. Maintenant, il contient le sexe que nous pourrons, par la suite, envoyer à nos précédentes méthodes.
- Rajout du descriptor dans le PopupBarLabelHandle. Pourquoi ? Simplement pour pouvoir y retrouver notre sexe contenu dans ce même descriptor. Cette étape est importante puisque que le listener permettant de sélectionner notre sexe se trouve sur cette handle. Il se trouve dans la méthode handleMousePressed(MouseEvent event).
- Dans cette fonction, nous rajoutons la ligne suivante : ElementInitializers.getInstance().setSex(this.getDesc().getSex());
- Modification de la fonction populatePopupBars() :
protected
void
populatePopupBars
(){
fillPopupBarDescriptors
();IElementType type
=
AddressbookElementTypes.Person_2001;Bundle bundle
=
Platform.getBundle
("
AdressBook.diagram
"
);for
(Sex s : Sex.VALUES){
Path path
=
new
Path
("
icons/popup/
"
+
s.getValue
()+
"
.gif
"
);URL url
=
FileLocator.find
(bundle, path,null
);ImageDescriptor desc
=
ImageDescriptor.createFromURL
(url);addPopupBarDescriptor
(type, desc.createImage
(), s);}
}
VIII. Conclusion et liens▲
Ce tutoriel est purement à titre indicatif. GMF est une technologie complexe et vaste. Je me permets de faire passer quelques liens, qui m'ont été très utiles, ils pourront sûrement aider dans la quête du parfait éditeur.
[1] http://sbachmann.developpez.com/eclipse/generer-editeurgraphique-gmf/
[2] http://gmfsamples.tuxfamily.org/wiki/doku.php
[3] http://wiki.eclipse.org/Graphical_Modeling_Framework/Tutorial#Get_started
[4] http://gmfsamples.tuxfamily.org/wiki/doku.php?id=gmf_tutorial4
[5] https://github.com/YattaSolutions/org.eclipse.mylyn.mft.taipan/tree/master/org.eclipse.gmf.runtime.lite.svg
[6] http://gmfsamples.tuxfamily.org/wiki/doku.php?id=gmf_tutorial8
Petits liens utiles :
http://www.jevon.org/wiki/GMF_Code_Samples
http://wiki.eclipse.org/GMF_Newsgroup_Q_and_A
http://wiki.eclipse.org/GMF/Tips
https://gist.github.com/snursmumrik/5532388 -- Ajouter des outils à une palette