JGraphX : Les bases

JGraphX est une API qui permet de dessiner des graphes dans une application Java. Le principal reproche qui est fait contre cette API est le manque de documentation. Ce tutoriel va vous apporter les bases pour prendre en main cette API. 4 commentaires Donner une note à l'article (5)

Article lu   fois.

L'auteur

Site personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

1. Introduction

1-A. JGraphX : c'est quoi

JGraphx est une bibliothèque Java qui permet de dessiner des graphes en 2D dans une application. Vous allez dire, c'est quoi un graphe. Pour cela, il suffit de lire le billet sur Wikipédia qui décrit la théorie des graphes à cette adresse

http://fr.wikipedia.org/wiki/Théorie_des_graphes .

Ce n'est pas pour vous faire peur, mais maintenant vous allez comprendre la raison du développement de cette bibliothèque.

Dans ce tutoriel, JGraphX n'est pas là pour résoudre des parcours de graphes, mais va plutôt servir à les dessiner, les représenter schématiquement et fournir un modèle de données qui va permettre de parcourir le graphe qui a été dessiné.

1-B. Deux versions mxGraph et JGraphx

A l'origine du projet il existait JGraph, une bibliothèque Java gratuite permettant de dessiner et parcourir des graphes. Deux versions distinctes ont été créées.

  • JGraphx est la version gratuite du produit. Elle permet de dessiner des graphes dans une application Java. C'est cette version qui sera décrite dans le tutoriel.
  • mxGraph, quant à elle, permet de dessiner des graphes dans des applications Web, tel que c'est décrit dans le site Web de JGraph. Vous avez ici , un exemple d'application. Cette version n'est pas gratuite, il faut bien évidemment que son auteur puisse vivre.

1-C. Téléchargement de JGraphX

Le téléchargement de JGraphX se fait ici .

Le fichier à télécharger est un fichier ZIP que vous pouvez décompresser sur votre disque et est constitué ainsi :

  • le répertoire doc contient la documentation de JGraphX et est constitué d'un manuel utilisateur (assez rudimentaire) et de la Javadoc' ;
  • le répertoire examples contient quelques applications pour comprendre comment utiliser cette bibliothèque, et je peux vous assurer que c'est bien utile' ;
  • le répertoire lib contient la bibliothèque Java que vous devez inclure dans le classpath pour pouvoir faire une application basée sur JGraphX' ;
  • le répertoire src contient les sources de la bibliothèque. Cela peut aider aussi pour mieux comprendre comment elle fonctionne.

2. Projet Java pour JGraphX

Afin de pouvoir utiliser la bibliothèque JGraphX, il suffit simplement de mettre le fichier jgraphx.jar dans le class-path de l'application. Ce jar se trouve dans le répertoire lib du ZIP que l'on a téléchargé.

3. Une première application

3-A. Comprendre le code de l'application

Pour débuter, on va créer une petite application simple qui affiche un "hello World" dans une JFrame. Je ne l'ai pas écrite, elle provient directement du manuel.

UN premier exemple
Sélectionnez
package com.bpy.jgraph.tutorial;
 
import javax.swing.JFrame;
 
import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.view.mxGraph;
 
public class JGraphExemple1 extends JFrame {
 
  /** Pour éviter un warning venant du JFrame */
  private static final long serialVersionUID = -8123406571694511514L;
 
  public JGraphExemple1() {
    super("JGrapghX tutoriel: Exemple 1");
 
    mxGraph graph = new mxGraph();
    Object parent = graph.getDefaultParent();
 
    graph.getModel().beginUpdate();
    try {
      Object v1 = graph.insertVertex(parent, null, "Hello", 20, 20, 80, 30);
      Object v2 = graph.insertVertex(parent, null, "World!", 240, 150, 80, 30);
      graph.insertEdge(parent, null, "Edge", v1, v2);
    } finally {
      graph.getModel().endUpdate();
    }
 
    mxGraphComponent graphComponent = new mxGraphComponent(graph);
    getContentPane().add(graphComponent);
  }
 
  /**
   * @param args
   */
  public static void main(String[] args) {
    JGraphExemple1 frame = new JGraphExemple1();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(400, 320);
    frame.setVisible(true);
  }
}

L'exécution de ce code permet d'obtenir la fenêtre suivante.

Image non disponible

Que voit-on dans cette fenêtre ?

  • Deux rectangles 'Hello' et 'World' de couleur bleue ; ces deux éléments sont nommés vertex dans le langage JGraphX.
  • Une flèche qui relie ces deux éléments. Cette flèche est un Edge et on peut remarquer aussi que cette flèche est aussi nommée. La flèche indique, dans ce cas, la destination.

Eh bien, ce n'est pas tout, car il existe aussi un élément non visible qui est le graphe par lui-même. C'est le parent des trois éléments que l'on voit.

En regardant de plus près le code.

 
Sélectionnez
mxGraph graph = new mxGraph();

Tout graphe doit commencer par ceci. C'est le constructeur du graphe.

other 0 1
Object parent = graph.getDefaultParent();

On commence par récupérer le point d'entrée du graphe. Pour en comprendre la raison, il faut imaginer que votre graphe se présente sous la forme d'une structure de données de type arbre. Dans un arbre, il existe toujours une racine et ce parent est la racine de votre graphe.

other 0 1
Object v1 = graph.insertVertex(parent, null, "Hello", 20, 20, 80, 30);
Object v2 = graph.insertVertex(parent, null, "World!", 240, 150, 80, 30);

Ici, on crée les deux vertex (rectangles) en définissant leurs propriétés. Explication des paramètres.

  • parent : les vertex sont liés directement à la racine du graphe.
  • null : ici on attend une chaîne de caractères qui sert d'identifiant pour le vertex, cette chaîne est optionnelle, on peut ne pas la renseigner.
  • Objet contenu par le vertex. Ici c'est une string mais, cela pourrait être n'importe quel objet, tout dépend de ce que l'on veut faire avec ce graphe après.
  • Les quatre derniers paramètres correspondent aux x, y, largeur et hauteur du rectangle.
other 0 1
graph.insertEdge(parent, null, "Edge", v1, v2);

Et ici, on ajoute le lien entre les deux vertex, ce que l'on nomme un edge.

  • parent : les vertex sont liés directement à la racine du graphe
  • null : ici on attend une chaîne de caractères qui sert d'identifiant pour ce edge, cette chaîne est optionnelle, on peut ne pas la renseigner.
  • 'Egde' : un objet contenu par le vertex, ici un String.
  • V1 est le vertex source du edge.
  • V2 est vertex destination du edge.

Pour finir, on voit que la création du graphe est encapsulée dans le code suivant :

other 0 1
graph.getModel().beginUpdate();
try {
'€¦..
} finally {
graph.getModel().endUpdate();
}

Cette encapsulation bloque l'affichage du graphe pendant la modification du modèle. Cette protection est basée sur le principe d'un compteur, beginUpdate incrémente le compteur, endUpdate le décrémente et l'affichage n'est possible que si le compteur vaut 0.

3-B. Comportement des objets dessinés

Jouons un peu avec notre graphe.

Si on place le curseur de la souris sur un vertex, on peut voir que le curseur réagit différemment en fonction de sa position sur le vertex.

  • Vers le centre du vertex on a le curseur en forme de main. Cela indique que vous pouvez faire de l'édition sur cet objet.
Image non disponible
    • Double-clic permet de modifier le texte dans la boite. Pour valider le nouveau texte, il suffit de cliquer ailleurs dans le graphe.
    • Simple clic permet de rajouter un edge à partir de ce vertex. Pour cela, clic gauche sans relâcher le bouton de la souris et ensuite déplacer la souris dans le graphe. Au relâchement de la souris, un nouvel edge est créé.
Image non disponible
    • Proche du bord, on a un curseur en forme de quatre flèches. Ce curseur indique que l'on peut modifier la position et la taille du vertex.
Image non disponible
    • Un simple clic permet de sélectionner le vertex, son affichage est modifié pour indiquer les points d'action possibles (petits rectangles aux coins et centre du contour). En utilisant ces points d'action, vous pouvez modifier la taille du vertex. Si on ne clique pas dans les points d'action, on peut déplacer le vertex dans le graphe. On peut remarquer aussi que l'edge qui lie les deux vertex suit le mouvement et reste connecté aux vertex.

L'egde est aussi modifiable, comme pour le vertex. On peut le sélectionner en cliquant dessus.

Image non disponible

Dans ce cas, on remarque qu'un edge qui est sélectionné affiche deux petits rectangles à ses extrémités. La couleur de ces rectangles est importante, car elle indique que l'edge est bien relié aux vertex. En cliquant sur l'edge, on peut le déplacer et obtenir ceci.

Image non disponible

On voit que les petits rectangles ont changé de couleur, ils sont maintenant verts. Cela signifie qu'ils ne sont plus liés aux vertex. Pour le vérifier, il suffit de bouger un vertex pour s'apercevoir que l'edge ne le suit pas. Pour reconnecter votre edge au vertex, il suffit de ramener un des petits rectangles vers le centre du vertex pour le reconnecter comme ceci :

Image non disponible

et on obtient' :

Image non disponible

En conclusion, on s'aperçoit que ce simple bout de code inclut déjà un grand nombre de mécanismes complexes pour le dessin des graphes.

3-C. Compréhension du modèle de donnée

JGraphX est basé sur le principe Modèle/Vue, c'est-à -dire que vous voyez la représentation graphique d'un modèle. Ce modèle est basé sur une structure de données de type arbre. Afin de visualiser l'arbre de données qui a servi on va ajouter le code suivant à notre application.

 
Sélectionnez
   public JGraphExemple1() {
      '€¦..
      displayModel((mxCell) parent,"");
   }
 
   private void displayModel(mxCell cell, String indent) {
      System.out.println(indent+cell.getValue()+"("+cell.getClass().getName()+")");
      int nbChilds = cell.getChildCount();
      indent = indent + "  ";
      for (int i=0; i<nbChilds ; i++) {
        displayModel((mxCell) cell.getChildAt(i), indent);
      }
   }

La méthode displayModel va permettre d'afficher le contenu de notre modèle de graph.

Avec notre modèle, on va obtenir l'affichage suivant:

 
Sélectionnez
null(com.mxgraph.model.mxCell)
  Hello(com.mxgraph.model.mxCell)
  World!(com.mxgraph.model.mxCell)
  Edge(com.mxgraph.model.mxCell)

Ceci montre une architecture avec un élément null (c'est l'élément parent) qui contient trois enfants (Hello, World! et Egde).

Maintenant, on va compliquer un peu notre graphe pour voir comment notre modèle va être impacté.

Un modèle plus complexe
Sélectionnez
graph.getModel().beginUpdate();
  try {
    Object level1 = graph.insertVertex(parent, null, "Bloc1", 10, 10, 350, 120); 
    Object level2 = graph.insertVertex(parent, null, "Bloc2", 10, 150, 350, 120); 
    Object level1_1 = graph.insertVertex(level1, null, "SubBloc11", 10, 50, 100, 40); 
    Object level1_2 = graph.insertVertex(level1, null, "SubBloc12", 240, 50, 100, 40); 
    Object level2_1 = graph.insertVertex(level2, null, "SubBloc21", 10, 50, 100, 40); 
    Object level2_2 = graph.insertVertex(level2, null, "SubBloc22", 240, 50, 100, 40); 
 
    graph.insertEdge(level1, null, "lien11_12", level1_1, level1_2);
    graph.insertEdge(level2, null, "lien21_22", level2_1, level2_2);
    graph.insertEdge(parent, null, "lien1_2", level1, level2);
  }
  finally {
    graph.getModel().endUpdate();
  }

On va alors obtenir le graphe suivant:

Image non disponible

et l'affichage du modèle donnera :

 
Sélectionnez
null(com.mxgraph.model.mxCell)
  Bloc1(com.mxgraph.model.mxCell)
    SubBloc11(com.mxgraph.model.mxCell)
    SubBloc12(com.mxgraph.model.mxCell)
    lien11_12(com.mxgraph.model.mxCell)
  Bloc2(com.mxgraph.model.mxCell)
    SubBloc21(com.mxgraph.model.mxCell)
    SubBloc22(com.mxgraph.model.mxCell)
    lien21_22(com.mxgraph.model.mxCell)
  lien1_2(com.mxgraph.model.mxCell)

On voit que nous avons ajouté un nouveau niveau à notre modèle (Root, enfants, petits-enfants).

Du point de vue comportement graphique, on peut noter les points suivants :

  • les coordonnées d'un élément du graphe sont toujours données par rapport à son parent.
Image non disponible
  • lorsque l'on déplace un bloc parent, les blocs enfants suivent le mouvement.

4. Comment agir sur le comportement du graphe

Ce chapitre va expliquer les possibilités offertes par JGraphX pour customiser les actions que l'on peut faire sur le graphe. Par exemple, on peut souhaiter ne pas pouvoir éditer le texte dans un Vertex ou un Edge, interdire les déplacements, etc.

4-A. Comportement général du graphe

Si on prend, par exemple, le cas du déplacement, JGraphX offre la possibilité d'autoriser ou d'interdire le déplacement des objets d'une manière globale ou individuelle.

Pour interdire globalement le déplacement de tous les objets dans un graphe, c'est au niveau graphe que l'on doit indiquer notre choix. Pour cela, il faut utiliser la méthode setCellsMovable comme ceci :

 
Sélectionnez
public JGraphExemple1() {
  super("JGrapghX tutoriel: Exemple 1");
 
  mxGraph graph = new mxGraph();
  Object parent = graph.getDefaultParent();
 
  graph.getModel().beginUpdate();
  try
  {
    Object level1 = graph.insertVertex(parent, null, "Bloc1", 10, 10, 350, 120); 
    Object level2 = graph.insertVertex(parent, null, "Bloc2", 10, 150, 350, 120); 
    Object level1_1 = graph.insertVertex(level1, null, "SubBloc11", 10, 50, 100, 40); 
    Object level1_2 = graph.insertVertex(level1, null, "SubBloc12", 240, 50, 100, 40); 
    Object level2_1 = graph.insertVertex(level2, null, "SubBloc21", 10, 50, 100, 40); 
    Object level2_2 = graph.insertVertex(level2, null, "SubBloc22", 240, 50, 100, 40); 
 
    graph.insertEdge(level1, null, "lien11_12", level1_1, level1_2);
    graph.insertEdge(level2, null, "lien21_22", level2_1, level2_2);
    graph.insertEdge(parent, null, "lien1_2", level1, level2);
  }
  finally
  {
    graph.getModel().endUpdate();
  }
 
  graph.setCellsMovable(false);
 
  mxGraphComponent graphComponent = new mxGraphComponent(graph);
  getContentPane().add(graphComponent);
}

Si on relance l'application, on peut vérifier que les objets dans le graphe ne peuvent plus se déplacer. On peut aussi remarquer que le curseur de la souris n'est plus modifié.

Image non disponible

'

4-B. Comportement individuel des objets

On sait maintenant modifier le comportement en général des objets d'un graphe. Mais on peut aussi vouloir modifier le comportement de quelques objets seulement. JGraphX nous offre aussi cette possibilité en jouant sur les styles des objets.

On souhaite, par exemple, ne psa pouvoir bouger les vertex level1 et level2 tout en autorisant le déplacement des vertex qui sont contenus par ces deux vertex parents.

Pour cela, lorsque l'on crée ces deux vertex, on ajoute un style comme ceci.

 
Sélectionnez
  String style = mxConstants.STYLE_MOVABLE +"=0";
  Object level1 = graph.insertVertex(parent, null, "Bloc1", 10, 10, 350, 120,style); 
  Object level2 = graph.insertVertex(parent, null, "Bloc2", 10, 150, 350, 120,style); 

Quand on relance l'application, on peut vérifier que ces deux objets ne peuvent plus se déplacer, par contre, les objets contenus par ces deux vertex peuvent bouger.

Que peut-on configurer par styles' ?

Le tableau ci-dessous va décrire quelques comportements 'customisables' fournis par la bibliothèque.

Comportement Méthode globale Propriété
Déplacement graph.setCellsMovable(false);
graph.setCellsMovable(true);
mxConstants.STYLE_MOVABLE +"=0"
mxConstants.STYLE_MOVABLE +"=1"
Edition graph.setCellsEditable(false);
graph.setCellsEditable(true);
mxConstants.STYLE_EDITABLE +"=0"
mxConstants.STYLE_EDITABLE +"=1"
Redimensionnement graph.setCellsResizable(true);
graph.setCellsResizable(false);
mxConstants.STYLE_RESIZABLE +"=0";
mxConstants.STYLE_RESIZABLE +"=1";

Cette liste n'étant pas exhaustive, il faut se référer à la Javadoc de la classe mxConstants pour avoir la lite complète des options possibles.

Pour en finir avec ce chapitre, on peut cumuler plusieurs options comme ceci, chaque propriété étant séparée pars des ';'

 
Sélectionnez
String style = mxConstants.STYLE_RESIZABLE +"=0;" +
                     mxConstants.STYLE_MOVABLE+"=0";
Object level1 = graph.insertVertex(parent, null, "Bloc1", 10, 10, 350, 120,style); 
Object level2 = graph.insertVertex(parent, null, "Bloc2", 10, 150, 350, 120,style); 

5. Comment agir sur l'aspect visuel d'un graphe

JGraphX offre la possibilité de modifier l'aspect visuel d'un graphe en jouant sur les styles des objets dessinés. Comme vu dans le chapitre précédent, il suffit de passer un String lors de la création de l'objet pour en modifier l'aspect.

5-A. Aspect visuel des edges

Pour ces éléments on peut modifier le début et la fin d'une flèche.

5-A-1. Modification de la fin d'une flèche

La fin de flèche se modifie avec la propriété mxConstants.STYLE_ENDARROW. La Javadoc spécifie que l'on peut utiliser les constantes débutant par ARROW, c'est vrai, mais il faut appliquer une limitation supplémentaire' : du type String.

Voici quelques exemples :

Propriété : Aperçu
mxConstants.STYLE_ENDARROW + "=" + mxConstants.ARROW_BLOCK Image non disponible
mxConstants.STYLE_ENDARROW + "=" + mxConstants.ARROW_CLASSIC Image non disponible
mxConstants.STYLE_ENDARROW + "=" + mxConstants.ARROW_DIAMOND Image non disponible
mxConstants.STYLE_ENDARROW + "=" + mxConstants.ARROW_OPEN Image non disponible
mxConstants.STYLE_ENDARROW + "=" + mxConstants.ARROW_OVAL Image non disponible

5-A-2. Modification du début d'une flèche

Comme pour la fin d'une flèche, on peut utiliser les mêmes styles pour la constante mxConstants.STYLE_STARTARROW.

La question qui reste posée pourrait être : mais comment fait-on pour supprimer un bout de flèche? Toutes les constantes débutant par ARROW du type string ont été utilisées, mais aucune ne supprime le bout de flèche.

La solution à cette question est :

 
Sélectionnez
String edgeStyle = mxConstants.STYLE_STARTARROW + "=" + mxConstants.NONE;

C'est logique, puisque NONE commence bien par ARROW.

5-A-3. Le tracé des lignes

Par défaut les lignes des edges sont des lignes pleines. Mais il est possible d'utiliser d'autres types de lignes comme par exemple les pointillées. Le plus simple est d'utiliser le style mxConstants.STYLE_DASHED qui donne le résultat suivant

Image non disponible

On peut vouloir aussi modifier l'apparence du pointillé. On le fait en combinant les constantes mxConstants.STYLE_DASHED et mxConstants.STYLE_DASH_PATTERN .

 
Sélectionnez
String edgeStyle = mxConstants.STYLE_DASHED + "=1;" +
                            mxConstants.STYLE_DASH_PATTERN + "=10"; 
 
Object edge11 = graph.insertEdge(level1, 
                             null, 
                             "lien11_12", 
                             level1_1,
                             level1_2,
                             edgeStyle);

On obtient ce tracé.

Image non disponible

5-A-4. La forme des lignes

De base les edges sont représentés par des lignes droites, mais il est possible de modifier cet aspect aussi. Le tableau suivant montre les différentes options possibles.

Propriété Aperçu
Valeur par défaut Image non disponible
mxConstants.STYLE_EDGE + "=" + mxConstants.EDGESTYLE_TOPTOBOTTOM; Image non disponible
mxConstants.STYLE_EDGE + "=" + mxConstants.EDGESTYLE_SIDETOSIDE; Image non disponible
mxConstants.STYLE_EDGE + "=" + mxConstants.EDGESTYLE_ENTITY_RELATION; Image non disponible
mxConstants.STYLE_EDGE + "=" + mxConstants.EDGESTYLE_LOOP; Image non disponible

En associant ces styles avec mxConstants.STYLE_ROUNDED+"=1" on arrondit alors les angles et cela donne

Image non disponible

5-B. Aspect visuel des vertex

Comme pour les edges, il est possible aussi de modifier l'aspect visuel des vertex en jouant avec leurs styles.

5-B-1. Les couleurs des vertex

On peut facilement jouer sur les couleurs de fond et du tour des edges. Prenons l'exemple suivant.

 
Sélectionnez
 String styleParent = mxConstants.STYLE_FILLCOLOR + "=#0000ff";
 Object level1 = graph.insertVertex(parent, null, "", 10, 10, 350, 120,styleParent); 
 
 String styleEnfant1 = mxConstants.STYLE_FILLCOLOR + "=#00ff00"; 
 String styleEnfant2 = mxConstants.STYLE_FILLCOLOR + "=#ff0000"; 
 Object level1_1 = graph.insertVertex(level1, null, "SubBloc11", 10, 50, 100,
    40,styleEnfant1); 
 Object level1_2 = graph.insertVertex(level1, null, "SubBloc12", 240, 10, 100,
    40,styleEnfant2); 

Donnera le résultat suivant :

Image non disponible

Les couleurs de fond des vertex ont donc été modifiées. Le codage de la couleur respecte le principe #RRVVBB ou RR est la valeur hexadécimale du rouge, VV la valeur hexadécimale du vert et BB celle du bleu.

5-B-2. Forme des vertex

Il est possible aussi de modifier la forme des vertex en jouant avec le style mxConstants.STYLE_SHAPE et de lui associer une des constantes commençant par SHAPE.

Voici quelques exemples :

 
Sélectionnez
    Object level1 = graph.insertVertex(parent, null, "", 10, 10, 600, 300); 
 
    graph.insertVertex(level1, null, "SHAPE_ACTOR", 10, 30, 100, 80,
           mxConstants.STYLE_SHAPE + "="+mxConstants.SHAPE_ACTOR); 
    graph.insertVertex(level1, null, "SHAPE_CYLINDER", 150, 30, 100, 80,
           mxConstants.STYLE_SHAPE + "="+mxConstants.SHAPE_CYLINDER); 
    graph.insertVertex(level1, null, "SHAPE_DOUBLE_ELLIPSE", 290, 30, 100, 80,
           mxConstants.STYLE_SHAPE + "="+mxConstants.SHAPE_DOUBLE_ELLIPSE); 
    graph.insertVertex(level1, null, "SHAPE_HEXAGON", 430, 30, 100, 80,
           mxConstants.STYLE_SHAPE + "="+mxConstants.SHAPE_HEXAGON); 
 
    graph.insertVertex(level1, null, "SHAPE_RHOMBUS", 10, 160, 100, 80,
           mxConstants.STYLE_SHAPE + "="+mxConstants.SHAPE_RHOMBUS); 
     graph.insertVertex(level1, null, "SHAPE_SWIMLANE", 150, 160, 100, 80,
           mxConstants.STYLE_SHAPE + "="+mxConstants.SHAPE_SWIMLANE); 
     graph.insertVertex(level1, null, "SHAPE_TRIANGLE", 290, 160, 100, 80,
           mxConstants.STYLE_SHAPE + "="+mxConstants.SHAPE_TRIANGLE); 
     graph.insertVertex(level1, null, "SHAPE_CLOUD", 430, 160, 100, 80,
           mxConstants.STYLE_SHAPE + "="+mxConstants.SHAPE_CLOUD);

qui donneront le résultat suivant' :

Image non disponible

Je ne listerai pas ici tous les styles que l'on peut utiliser pour modifier l'aspect visuel d'un vertex, je vous laisse le soin de les rechercher dans la Javadoc de la classe mxConstants.

6. Conclusion

Ce petit tutoriel vous a expliqué les mécanismes de base pour afficher un graphe dans une application Java. à vous de jouer maintenant.

Un grand merci à djibril , claude Leloup et Jacques_jean pour la relecture et leurs remarques judicieuses.

Je tiens à remercier aussi Thierry et mlny84 pour l'aide apportée pour la mise en ligne de ce tutoriel

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2012 Patrick Briand. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.