IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Apprendre à utiliser le module Python PyGTK 2.0


précédentsommairesuivant

XXIV. Chapitre 24. Scribble, Un programme simple de dessin

XXIV-A. Aperçu de Scribble

Dans cette section, nous construirons un programme simple de dessin. Ce faisant, nous examinerons comment gérer les événements souris, comment dessiner dans une fenêtre, et comment mieux dessiner en utilisant un pixmap en arrière-plan.

Image non disponible

XXIV-B. Gestion des événements

Les signaux GTK+ que nous avons déjà vus concernent les actions de haut niveau, comme la sélection du choix d'un menu. Cependant, il est utile quelquefois de connaître des possibilités de plus bas niveau, comme le déplacement de la souris, ou l'appui sur une touche. Il existe aussi des signaux GTK+ correspondants à ces événements de bas niveau. Les gestionnaires de ces signaux possèdent un paramètre supplémentaire : un objet gtk.gdk.Event contenant des informations sur l'événement. Par exemple, les gestionnaires des événements de déplacement reçoivent un paramètre gtk.gdk.Event contenant une information GdkEventMotion qui comporte (en partie) ces attributs :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
  type    # type
  window  # fenêtre
  time    # temps
  x
  y
    ...
  state   # état
    ...

window est la fenêtre dans laquelle l'événement est survenu.

x et y fournissent les coordonnées de l'événement.

type sera initialisé avec le type de l'événement, ici MOTION_NOTIFY. Ces types (du module gtk.gdk) sont :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
NOTHING                un code spécial pour indiquer un événement nul.

DELETE                 le gestionnaire de fenêtre a demandé que la fenêtre de plus haut niveau soit
                       cachée ou détruite, d'habitude quand l'utilisateur clique sur
                       une icône spéciale dans la barre de titre.

DESTROY                la fenêtre a été détruite.

EXPOSE                 tout ou partie de la fenêtre est devenu visible et doit être
                       redessiné.

MOTION_NOTIFY          le pointeur (d'habitude une souris) a bougé.

BUTTON_PRESS           on a effectué un clic sur un bouton de la souris.

_2BUTTON_PRESS         on a effectué un double-clic sur un bouton de la souris 
                       (deux clics en un bref temps) sur un bouton de la souris.
                       Note : chaque clic génère aussi un événement BUTTON_PRESS.

_3BUTTON_PRESS         on a effectué un triple-clic sur un bouton de la souris sur 
                       un temps bref. Note : chaque clic génère aussi un 
                       événement BUTTON_PRESS.

BUTTON_RELEASE         le bouton de la souris a été relâché.

KEY_PRESS              on a appuyé sur une touche.

KEY_RELEASE            la touche a été relâchée.

ENTER_NOTIFY           le pointeur est entré dans la fenêtre.

LEAVE_NOTIFY           le pointeur est sorti de la fenêtre.

FOCUS_CHANGE           le focus de clavier est dans ou a quitté la fenêtre.

CONFIGURE              la taille, position ou ordre d'empilage a changé. Noter que GTK+ 
                       n'utilisera pas ces événements pour les fenêtres enfants GDK_WINDOW_CHILD.

MAP                    la fenêtre a été mappée.

UNMAP                  la fenêtre n'est plus mappée.

PROPERTY_NOTIFY        une propriété de la fenêtre a été modifiée ou supprimée.

SELECTION_CLEAR        l'application a perdu la propriété d'une sélection.

SELECTION_REQUEST      une autre application a réclamé la sélection.

SELECTION_NOTIFY       une sélection a été reçue.

PROXIMITY_IN           un dispositif d'entrée a bougé en contact avec une surface sensible
                       (par ex. un écran tactile ou tablette graphique).

PROXIMITY_OUT          un dispositif d'entrée a bougé en coupant le contact avec une surface sensible

DRAG_ENTER             la souris est entrée dans la fenêtre pendant une opération glisser.

DRAG_LEAVE             la souris est sortie de la fenêtre pendant une opération glisser.

DRAG_MOTION            la souris a bougé dans la fenêtre pendant une opération glisser.

DRAG_STATUS            l'état de l'opération glisser démarrée par la fenêtre a changé.

DROP_START             une opération déposer sur la fenêtre a démarré.

DROP_FINISHED          une opération déposer initiée par la fenêtre est accomplie.

CLIENT_EVENT           un message d'une autre application a été reçu.

VISIBILITY_NOTIFY      l'état de visibilité de la fenêtre a changé.

NO_EXPOSE              indique que la région source est totalement disponible lorsque des extraits du 
                       dessinable sont copiés. Ceci ne présente pas d'intérêt.

SCROLL                 ?

WINDOW_STATE           ?

SETTING                ?

Le paramètre state indique l'état du modificateur lorsque l'événement s'est produit (c'est-à-dire quelles sont les touches de modification et les boutons de souris qui ont été pressés). Il s'agit d'une opération de bit OR de certaines des valeurs (du module gtk.gdk) suivantes :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
  SHIFT_MASK    # masque de majuscules
  LOCK_MASK     # masque de majuscules bloquées
  CONTROL_MASK  # masque de contrôle
  MOD1_MASK     # masque du modificateur 1   
  MOD2_MASK     # masque du modificateur 2    
  MOD3_MASK     # masque du modificateur 3    
  MOD4_MASK     # masque du modificateur 4    
  MOD5_MASK     # masque du modificateur 5    
  BUTTON1_MASK  # masque du bouton 1  
  BUTTON2_MASK  # masque du bouton 2  
  BUTTON3_MASK  # masque du bouton 3  
  BUTTON4_MASK  # masque du bouton 4  
  BUTTON5_MASK  # masque du bouton 5

Comme pour les autres signaux, on appelle la méthode connect() pour déterminer ce qui se passe lorsqu'un événement survient. Mais on doit aussi faire en sorte que GTK+ sache de quels événements nous voulons être avertis. Pour ce faire, on appelle la méthode :

 
Sélectionnez
  widget.set_events(events)

events définit les événements qui nous intéressent. Il s'agit d'une opération de bit OR de constantes qui indiquent différents types d'événements. Pour référence ultérieure, les types d'événements (du module gtk.gdk) sont :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
  EXPOSURE_MASK
  POINTER_MOTION_MASK
  POINTER_MOTION_HINT_MASK
  BUTTON_MOTION_MASK     
  BUTTON1_MOTION_MASK    
  BUTTON2_MOTION_MASK    
  BUTTON3_MOTION_MASK    
  BUTTON_PRESS_MASK      
  BUTTON_RELEASE_MASK    
  KEY_PRESS_MASK         
  KEY_RELEASE_MASK       
  ENTER_NOTIFY_MASK      
  LEAVE_NOTIFY_MASK      
  FOCUS_CHANGE_MASK      
  STRUCTURE_MASK         
  PROPERTY_CHANGE_MASK
  VISIBILITY_NOTIFY_MASK
  PROXIMITY_IN_MASK      
  PROXIMITY_OUT_MASK
  SUBSTRUCTURE_MASK

Il y a quelques points subtils qui doivent être observés lorsque l'on appelle la méthode set_events(). D'abord, elle doit être appelée avant que la fenêtre X d'un widget GTK soit créée. En pratique, cela signifie que l'on doit l'appeler immédiatement après avoir créé le widget. Ensuite, le widget doit faire partie de ceux réalisés avec une fenêtre X associée. Pour des raisons d'efficacité, de nombreux types de widgets n'ont pas de fenêtre propre, mais se dessinent dans la fenêtre de leur parent. Ces widgets sont :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
  gtk.Alignment
  gtk.Arrow
  gtk.Bin
  gtk.Box
  gtk.Image
  gtk.Item
  gtk.Label
  gtk.Layout
  gtk.Pixmap
  gtk.ScrolledWindow
  gtk.Separator
  gtk.Table
  gtk.AspectFrame
  gtk.Frame
  gtk.VBox
  gtk.HBox
  gtk.VSeparator
  gtk.HSeparator

Pour capturer les événements pour ces widgets, on doit utiliser un widget EventBox. Voir la Section 10.1, « La boite à événement (EventBox) »La boîte à évènement (EventBox) pour plus de détails.

Voici les attributs d'événement qui sont définis par PyGTK pour chaque type d'événement :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
événement               type              # type
                        window            # fenêtre
                        send_event        # événement transmis

NOTHING
DELETE
DESTROY                                   # pas d'attribut supplémentaire

EXPOSE                 area               # zone
                       count              # nombre

MOTION_NOTIFY          time               # temps
                       x
                       y
                       pressure           # pression
                       xtilt              # inclinaison en x
                       ytilt              # inclinaison en y
                       state              # état
                       is_hint            # est indice
                       source             # source
                       deviceid           # identifiant de dispositif
                       x_root             # racine x
                       y_root             # racine y

BUTTON_PRESS
_2BUTTON_PRESS
_3BUTTON_PRESS
BUTTON_RELEASE         time               # temps
                       x
                       y
                       pressure           # pression
                       xtilt              # inclinaison en x
                       ytilt              # inclinaison en y
                       state              # état
                       button             # bouton
                       source             # source
                       deviceid           # identifiant de dispositif
                       x_root             # racine x
                       y_root             # racine y

KEY_PRESS
KEY_RELEASE            time               # temps
                       state              # état
                       keyval             # valeur de clé
                       string             # chaine de caractères

ENTER_NOTIFY
LEAVE_NOTIFY           subwindow          # sous-fenêtre
                       time               # temps
                       x
                       y
                       x_root             # racine x
                       y_root             # racine y
                       mode               # mode
                       detail             # détail
                       focus              # focus
                       state              # état

FOCUS_CHANGE           _in                # dans

CONFIGURE              x
                       y
                       width              # largeur
                       height             # hauteur

MAP
UNMAP                                     # pas d'attribut supplémentaire

PROPERTY_NOTIFY        atom               # atome
                       time               # temps
                       state              # état

SELECTION_CLEAR
SELECTION_REQUEST
SELECTION_NOTIFY       selection          # sélection
                       target             # cible
                       property           # propriété
                       requestor          # demandeur
                       time               # temps

PROXIMITY_IN
PROXIMITY_OUT          time               # temps
                       source             # source
                       deviceid           # identifiant de dispositif

DRAG_ENTER
DRAG_LEAVE
DRAG_MOTION
DRAG_STATUS
DROP_START
DROP_FINISHED          context            # contexte
                       time               # temps
                       x_root             # racine x
                       y_root             # racine x

CLIENT_EVENT           message_type       # type de message
                       data_format        # format de données
                       data               # données

VISIBILTY_NOTIFY       state              # état

NO_EXPOSE                                 # pas d'attribut supplémentaire

XXIV-B-1. Gestion des événements dans Scribble

Pour notre programme de dessin, on veut savoir quand le bouton de la souris est pressé et quand la souris est déplacée, nous indiquons donc POINTER_MOTION_MASK et BUTTON_PRESS_MASK. On veut aussi savoir quand il est nécessaire de redessiner notre fenêtre, on indique donc EXPOSURE_MASK. Bien que nous voulions être avertis via un événement Configure, d'un redimensionnement de la fenêtre, on n'a pas besoin de préciser le drapeau STRUCTURE_MASK correspondant, car il est automatiquement signalé pour chaque fenêtre.

Il peut cependant y avoir un problème en indiquant seulement POINTER_MOTION_MASK. Cela fera que le serveur ajoutera un nouvel événement de déplacement à la file des événements à chaque fois que l'utilisateur déplace la souris. Imaginons que cela prenne 0,1 seconde pour gérer un événement de déplacement, mais si le serveur X ajoute un nouvel événement de déplacement dans la queue toutes les 0,05 seconde, nous serons vite à la traîne de l'utilisateur. Si l'utilisateur dessine pendant 5 secondes, cela nous prendra 5 secondes de plus pour le traiter après qu'il ait relâché le bouton de la souris ! Ce que l'on voudrait, c'est ne récupérer qu'un événement de déplacement pour chaque événement que l'on traite. Pour cela, il faut préciser POINTER_MOTION_HINT_MASK.

Quand nous indiquons POINTER_MOTION_HINT_MASK, le serveur nous envoie un événement de déplacement la première fois que le pointeur se déplace après son entrée dans la fenêtre, ou après un événement d'appui ou de relâchement d'un bouton. Les événements de déplacement suivants seront supprimés jusqu'à ce que l'on demande explicitement la position du pointeur en utilisant la méthode gtk.gdk.Window :

 
Sélectionnez
  x, y, mask = window.get_pointer()

window est un objet gtk.gdk.Window, les paramètres x et y sont les coordonnées du pointeur et mask est le masque modificateur pour détecter les touches pressées. Il existe une méthode get_pointer() pour gtk.Widget qui fournit la même information que la méthode gtk.gdk.Window.get_pointer(), mais ne retourne pas l'indication de masque des touches

Le programme exemple scribblesimple.py montre l'utilisation de base des événements et des gestionnaires d'événements. La Figure 24.2, « Exemple de Scribble simple » illustre le programme :

Image non disponible

Les gestionnaires d'événements sont connectés à la zone de dessin grâce aux ligne suivantes :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
    # Signaux utilisés pour gérer le pixmap hors écran
    zone_dessin.connect("expose_event", expose_event)
    zone_dessin.connect("configure_event", configure_event)

    # Signaux d'événements
    zone_dessin.connect("motion_notify_event", motion_notify_event)
    zone_dessin.connect("button_press_event", bouton_press_event)

    zone_dessin.set_events(gtk.gdk.EXPOSURE_MASK
                            | gtk.gdk.LEAVE_NOTIFY_MASK
                            | gtk.gdk.BUTTON_PRESS_MASK
                            | gtk.gdk.POINTER_MOTION_MASK
                            | gtk.gdk.POINTER_MOTION_HINT_MASK)

Les gestionnaires d'événements button_press_event() et motion_notify_event() dans scribblesimple.py sont ainsi :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
def bouton_press_event(widget, event):
    if event.button == 1 and pixmap != None:
        brosse_dessin(widget, event.x, event.y)
    return True

def motion_notify_event(widget, event):
    if event.is_hint:
        x, y, etat = event.window.get_pointer()
    else:
        x = event.x
        y = event.y
        etat = event.state
    
    if etat & gtk.gdk.BUTTON1_MASK and pixmap != None:
        brosse_dessin(widget, x, y)
  
    return True

Les gestionnaires expose_event() et configure_event() seront décrits plus tard.

XXIV-C. Le widget zone de dessin (DrawingArea) et le dessin

Passons au processus de dessin sur l'écran. Le widget que l'on utilise pour cela est le widget DrawingArea (voir le Chapitre 12, La zone de dessin (Drawing Area)Chapitre 12. La zone de dessin (Drawing Area)). Un tel widget est essentiellement une fenêtre X et rien de plus. Il s'agit d'une toile vide sur laquelle nous pouvons dessiner ce que nous voulons. On crée ce widget par l'appel à :

 
Sélectionnez
  darea = gtk.DrawingArea()

On peut lui donner une taille par défaut par l'appel :

 
Sélectionnez
  darea.set_size_request(largeur, hauteur)

Cette taille par défaut peu être surchargée, comme pour tous les widgets, en appelant la méthode set_size_request() et celle-ci, à son tour, peut être surchargée si l'utilisateur modifie manuellement la taille de la fenêtre contenant la zone de dessin.

Il faut noter que lorsque l'on crée un widget DrawingArea, on est complètement responsable du dessin du contenu. Si la fenêtre est cachée puis redécouverte, on reçoit un événement d'exposition et on doit redessiner ce qui avait été caché auparavant.

Devoir se rappeler tout ce qui a été dessiné à l'écran pour pouvoir correctement le redessiner peut s'avérer, et c'est un euphémisme, pénible. De plus, cela peut être visible si des portions de la fenêtre sont effacées puis redessinées étape par étape. La solution à ce problème est d'utiliser un pixmap d'arrière-plan hors écran. Au lieu de dessiner directement sur l'écran, on dessine sur une image stockée dans la mémoire du serveur et non affichée puis, lorsque l'image change ou lorsque de nouvelles parties de l'image sont affichées, on copie les parties correspondantes sur l'écran.

Pour créer un pixmap hors écran, on appelle la fonction :

 
Sélectionnez
  pixmap = gtk.gdk.Pixmap(fenêtre, largeur, hauteur, profondeur=-1)

Le paramètre fenêtre désigne une fenêtre gtk.gdk.Window d'où ce pixmap tire certaines de ses propriétés. Les paramètres largeur et hauteur précisent la taille du pixmap, profondeur précise la profondeur de couleur (c'est-à-dire le nombre de bits par pixel) de la nouvelle fenêtre. Si cette profondeur vaut -1 ou n'est pas indiquée, elle correspondra à celle de fenêtre.

On crée le pixmap dans notre gestionnaire "configure_event". Cet événement est généré à chaque fois que la fenêtre change de taille, y compris lors de sa création initiale.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
# Création d'un nouveau pixmap d'arrière-plan de la taille voulue
def configure_event(widget, event):
    global pixmap

    x, y, largeur, hauteur = widget.get_allocation()
    pixmap = gtk.gdk.Pixmap(widget.window, largeur, hauteur)
    pixmap.draw_rectangle(widget.get_style().white_gc,
                          True, 0, 0, largeur, hauteur)

    return True

L'appel à draw_rectangle() initialise le pixmap à blanc. Nous en dirons plus tout à l'heure.

Le gestionnaire d'événement d'exposition copie alors simplement la partie utile du pixmap sur la zone de dessin (widget) en utilisant la méthode draw_pixmap(). On détermine la zone à redessiner en utilisant l'attribut event.area de l'événement d'exposition) :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
# Redessine l'écran à partir du pixmap d'arrière-plan
def expose_event(widget, event):
    x , y, largeur, hauteur = event.area
    widget.window.draw_drawable(widget.get_style().fg_gc[gtk.STATE_NORMAL],
                                pixmap, x, y, x, y, largeur, hauteur)
    return False

On a vu comment garder l'écran à jour avec notre pixmap, mais comment dessine-t-on réellement ce que l'on veut dans le pixmap ? Il existe un grand nombre d'appels dans PyGTK pour dessiner sur des "dessinables". Un "dessinable" est simplement quelque chose sur lequel on peut dessiner. Cela peut être une fenêtre, un pixmap, ou un bitmap (une image en noir et blanc). On a déjà vu plus haut deux de ces appels, draw_rectangle() et draw_pixmap(). En voici La liste complète :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
  drawable.draw_point(gc, x, y)

  drawable.draw_line(gc, x1, y1, x2, y2)

  drawable.draw_rectangle(gc, fill, x, y, width, height)

  drawable.draw_arc(gc, fill, x, y, width, height, angle1, angle2)

  drawable.draw_polygon(gc, fill, points)

  drawable.draw_drawable(gc, src, xsrc, ysrc, xdest, ydest, width, height)

  drawable.draw_points(gc, points)

  drawable.draw_lines(gc, points)

  drawable.draw_segments(gc, segments)

  drawable.draw_rgb_image(gc, x, y, width, height, dither, buffer, rowstride)

  drawable.draw_rgb_32_image(gc, x, y, width, height, dither, buffer, rowstride)

  drawable.draw_gray_image(gc, x, y, width, height, dither, buffer, rowstride)

Les méthodes des zones de dessin sont identiques à celles des "dessinables", ainsi on peut se reporter aux méthodes décrites dans la Section 12.2, « Les méthodes pour dessiner »Les méthodes pour dessiner pour plus de détails sur celles-ci. Toutes ces méthodes partagent les mêmes premiers arguments, le premier étant le contexte graphique (gc).

Un contexte graphique encapsule l'information sur des éléments comme la couleur de premier et d'arrière-plan et la largeur de ligne. PyGTK possède un ensemble complet de fonctions pour créer et manipuler les contextes graphiques, mais pour faire simple, nous n'utiliserons que les contextes graphiques prédéfinis. Reportez-vous à la Section 12.1, « Le contexte graphique »Le contexte graphique pour plus d'informations sur les contextes graphiques. Chaque widget possède un style associé (qui peut être modifié dans un fichier gtkrc, voir la Chapitre 23, Les fichiers de style rc GTKChapitre 23. Les fichiers de style rc GTK. Celui-ci, entre autres choses, stocke plusieurs contextes graphiques. Quelques exemples d'accès à ces contextes graphiques :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
  widget.get_style().white_gc

  widget.get_style().black_gc

  widget.get_style().fg_gc[STATE_NORMAL]

  widget.get_style().bg_gc[STATE_PRELIGHT]

Les champs fg_gc, bg_gc, dark_gc et light_gc sont indexés par un paramètre qui peut prendre les valeurs suivantes :

 
Sélectionnez
1.
2.
3.
4.
5.
  STATE_NORMAL,
  STATE_ACTIVE,
  STATE_PRELIGHT,
  STATE_SELECTED,
  STATE_INSENSITIVE

Par exemple, pour STATE_SELECTED, la couleur de premier plan par défaut est le blanc, la couleur d'arrière-plan par défaut est le bleu foncé.

La fonction draw_brush(), qui réalise le dessin sur le pixmap est alors :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
# Dessine un rectangle sur l'écran
def brosse_dessin(widget, x, y):
    rect = (int(x-5), int(y-5), 10, 10)
    pixmap.draw_rectangle(widget.get_style().black_gc, True,
                          rect[0], rect[1], rect[2], rect[3])
    widget.queue_draw_area(rect[0], rect[1], rect[2], rect[3])

Après avoir dessiné le rectangle représentant la brosse sur le pixmap, on appelle la fonction :

 
Sélectionnez
  widget.queue_draw_area(x, y, width, height)

qui indique à X que cette zone nécessite d'être mise à jour. X générera éventuellement un événement d'exposition (en combinant peut-être les zones passées dans plusieurs appels à draw()) ce qui forcera le gestionnaire d'événement d'exposition à recopier les parties adéquates à l'écran.

Nous avons maintenant couvert entièrement le programme de dessin, sauf quelques détails banals comme la création de la fenêtre principale.


précédentsommairesuivant

Copyright © 2005 John Finlay. Aucune reproduction, même partielle, ne peut être faite de ce site ni 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.