Les événements

Les événements sont un concept fondamental qu’il vous sera important de bien comprendre. Ce n’est pas compliqué, vous allez voir. On retrouve ce concept dans les autres bibliothèques graphiques comme Qt ou TKinter.

Nous apprendrons également comment faire un bouton et l’utiliser. Ce chapitre est donc fondamental, prenez votre temps pour le lire !

Présentation

Les événements, je vous le dis tout de suite, vous allez en manger ! Matin, midi et soir en n’omettant pas le goûter bien sûr. :p

Nous allons découvrir ensemble la programmation événementielle. C’est-à-dire que nous allons réagir aux actions de l’utilisateur. Un clique sur un bouton, la validation d’une zone de texte, etc. Ce sont des événements ! Vous imaginez bien que sans ça, votre programme serait d’un ennui… Toute la puissance de l’interface graphique se trouve là-dedans.

Vos programmes ne suivront désormais plus du tout le même plan. Je m’explique :

  • Avant, vos programmes étaient dits linéaires : votre code s’exécutait dans l’ordre où vous l’aviez écrit (du haut vers le bas), vous étiez d’ailleurs assez limités par cette contrainte. De plus, l’utilisateur n’avait aucune liberté, il ne pouvait rien faire d’autre que ce que vous lui proposiez.
  • Maintenant, c’est une toute autre façon de penser. Dans votre programme, vous définissez votre interface, les boutons et tout ça, on lance le tout, puis on passe la main à l’utilisateur. C’est lui qui choisi ce qu’il fait, quand, et comment.

Cette méthode de programmation est dite événementielle, elle suit le principe action → réaction.

L’objectif du jour est de savoir faire réagir notre application à la réception de ces signaux. Le principe des signaux est vraiment simple : lorsque l’utilisateur utilise votre widget, celui-ci annonce a qui veut entendre ce que vous lui avez fait, c’est ce que l’on appelle un signal (qui sera différent pour chaque action que le widget gère).

GTK s’occupe de tout, donc la partie technique ne nous regarde pas. Cependant il faut bien comprendre comment ça fonctionne.

Il n’y a qu’une seule chose à faire. On va utiliser une fonction qui dira à GTK :

  • De quel widget on veut traiter un événement.
  • Le signal (l’événement) que l’on veut traiter.
  • Le nom d’une fonction (faite par vous), à appeler lorsque cet événement survient. Ce type de fonction est appelé fonction callback.

Cette partie n’a pas été écrite par ma propre plume. Elle se trouve originellement sur OpenClassroom et est publiée sous la licence CC-BY−SA. Je pense que je n’aurais pas pu vous expliquer aussi bien le concept des événements qu’eux.

En gros, on peut résumer avec un petit schéma :

La vie d’un événement

Premier contact

Reprenons notre fenêtre avec le bouton que vous avez (normalement :p ) réalisée dans le chapitre précédant :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#!/usr/bin/env python3
# coding: utf-8

from gi.repository import Gtk

window = Gtk.Window()
window.set_title('Un hello world')

button = Gtk.Button(label='Dire bonjour')  # Je modifie juste le label du bouton
window.add(button)

window.connect('delete-event', Gtk.main_quit)

window.show_all()
Gtk.main()

Ce que je voudrais, c’est que quand je clique sur le bouton, la fenêtre me dise bonjour. Vous l’aurez deviné, il va falloir utiliser les événements. Voici ce que ça donne :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/env python3
# coding: utf-8

from gi.repository import Gtk

# On créer une fonction toute simple
def say_hello(button):
    ''' Fonctions qui dit bonjour quand on clique sur le bouton '''
    print('Bonjour les zesteux !')

window = Gtk.Window()
window.set_title('Un hello world')

button = Gtk.Button(label='Dire bonjour')  # Je modifie juste le label du bouton
window.add(button)

# On connecte ce bouton à la fonction
button.connect('clicked', say_hello)

window.connect('delete-event', Gtk.main_quit)

window.show_all()
Gtk.main()

Normalement c’est là que vous me posez toutes vos questions. Bon là ça va être un peu compliqué. ;) Je vais quand même essayer de répondre aux questions que vous pourriez vous poser.

La notion d'événement

Nous allons nous intéresser à cette ligne : button.connect('clicked', say_hello).

Dans GTK, chaque widget possède une méthode connect. Voici sa syntaxe :

1
handler_id = widget.connect('event', callback, data)

L’objet widget représente n’importe quel widget de GTK. Pour nous, ce sera un bouton. Cette méthode prend deux arguments obligatoires et un facultatif. Le premier est le nom le l’événement. Par exemple, si vous utilisez un bouton, vous voulez qu’un signal soit émis quand l’utilisateur clique dessus, vous allez utiliser 'clicked'. Chaque widget a ses propres événements.

Le second est la fonction dite callback. C’est à dire que vous allez passer en argument une fonction qui va réagir au signal. Le widget est automatiquement passé en argument.

Pour finir, vous allez mettre en dernier argument les arguments que vous souhaitez passer à votre fonction callback. Nous étudierons ce cas plus tard, ne vous inquiétez pas. ;)

La fonction connect retourne un nombre qui identifie cette connexion. Celui-ci peut être utile si vous avez besoin de déconnecter le widget de cet événement ou de lui attribuer un autre callback.

Reprenons notre ligne qui va tout à coup vous paraître bien plus simple :

1
button.connect('clicked', say_hello)

Nous demandons à GTK+ de connecter notre button à la fonction callback say_hello quand l’événement 'clicked' est émis.

Entrainons-nous

Quoi ? Vous pensiez réellement que j’allais vous laisser tranquille cette fois ? Certainement pas ! :D

Les événements étant un concept phare de GTK+, je vous conseille vraiment de réaliser ces petits défis (sans tricher bien sûr). Ce que je vous propose :

  • Un bouton qui change de label quand on clique dessus
  • Un bouton qui quitte le programme
  • Un bouton qui créerait une nouvelle fenêtre contenant un autre bouton qui ferme tout (plus difficile celui-ci !)

Allez, à vos éditeurs ! ^^

Pour le premier défi, vous avez sûrement utilisé la documentation afin de savoir comment changer le label d’un bouton. Voici ma solution :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
from gi.repository import Gtk


def on_button_clicked(button):
   ''' Change le label du bouton '''
   button.set_label('Pouet!')

window = Gtk.Window()

button = Gtk.Button(label='Chiche de cliquer ?')
window.add(button)

button.connect('clicked', on_button_clicked)
window.connect('delete-event', Gtk.main_quit)

window.show_all()
Gtk.main()

Pour ce second défi, la réponse n’a pas dû être bien longue à trouver puisqu’elle se trouvait déjà dans votre code. Voici ma solution :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from gi.repository import Gtk


window = Gtk.Window()

button = Gtk.Button(label='Chiche de cliquer ?')
window.add(button)

button.connect('clicked', Gtk.main_quit)
window.connect('delete-event', Gtk.main_quit)

window.show_all()
Gtk.main()

Enfin, ce dernier défi a dû vous demander un peu plus de réflexion. Voici ce que je vous propose :

 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
from gi.repository import Gtk


def open_window(button):
   ''' Ouvre une nouvelle fenêtre '''
   sub_window = Gtk.Window()  # On créer une nouvelle fenêtre

   close_btn = Gtk.Button(label='Fermer l\'application')
   sub_window.add(close_btn)

   # On connecte l'événement clicked du bouton au callback main_quit de Gtk
   close_btn.connect('clicked', Gtk.main_quit)

   sub_window.show_all()  # On affiche le tout


window = Gtk.Window()

open_btn = Gtk.Button(label='Chiche de cliquer ?')
window.add(open_btn)

open_btn.connect('clicked', open_window)
window.connect('delete-event', Gtk.main_quit)

window.show_all()
Gtk.main()

Vous voilà capable de créer des fenêtres qui réagissent à votre utilisateur ! Mais au fait… Celle-ci ne se compose que d’un seul widget non ? C’est un peu nul.

Donc le prochain chapitre sera consacré aux layouts ! Vous allez pouvoir mettre autant de widgets que vous voudrez dans vos fenêtres grâce à eux.