Modifications entre les versions 1 et 8 (s'étendant sur 7 versions)
Version 1 à la date du 2013-05-21 14:19:51
Taille: 39
Éditeur: iml138
Commentaire:
Version 8 à la date du 2014-05-14 14:13:58
Taille: 7914
Éditeur: 178
Commentaire:
Texte supprimé. Texte ajouté.
Ligne 1: Ligne 1:
#redirect Un arbre dans le disque dur = Interfaces graphiques en Python et Tkinter =

Ceci correspond à la séance 1B4 de la formation 2013-2014.

Il y a de nombreuses bibliothèques pour créer des interfaces graphiques: [[http://www.gtk.org/|Gtk+]], [[http://qt-project.org/|Qt]], [[http://www.wxwidgets.org/|wxWidgets]], qui sont utilisables en Python. Cela fonctionne toujours un peu pareil, même si chacune a ses spécificités. Ici, on utilisera la bibliothèque [[https://docs.python.org/3/library/tkinter.html|tkinter]], qui est la présentation en Python de la bibliothèque [[http://www.tcl.tk/|Tcl/Tk]]. La raison principale est qu'elle fait partie des bibliothèques standard de Python et qu'à ce titre elle est installée d'office et interagit bien avec les environnements de développement comme Idle.

/!\ La documentation standard de `tkinter` est assez succinte: elle se limite à une introduction et ne donne pas une liste exhaustive de toutes les classes et de toutes les options. Il faut se reporter aux documentations portant sur Tk pour en savoir plus.

== Démarrage ==

L'incontournable premier programme avec `tkinter` ressemble à ceci:

{{{#!highlight python
from tkinter import *
fenetre = Frame()
fenetre.pack()
texte = Label(fenetre, text="Salut!")
texte.pack()
fenetre.mainloop()
}}}

Tapez ce programme et lancez-le, vous verrez apparaître une fenêtre qui dit juste "Salut". Le programme se terminera quand vous fermerez la fenêtre.

Quelques explications:

 * La bibliothèque s'appelle simplement `tkinter`. On l'inclut avec cette forme pour simplifier l'écriture des programmes.
   /!\ Les exemples sont en Python 3. En Python 2, tout est pareil sauf qu'on écrit `Tkinter` avec une majuscule!

 * Les éléments d'interface sont créés par les appels à `Frame` et `Label` qui construisent des objets des classes portant ce nom. Le premier désigne la fenêtre principale (celle qui contient l'interface) et le deuxième désigne l'élément de texte qui y est affiché. Chaque élément est contenu dans un autre élément (l'agument du constructeur), sauf la fenêtre principale. Le deuxième argument (nommé) du constructeur `Label` sert à ''configurer'' l'élément que l'on crée: `text` précise le texte, d'autres `options` précisent d'autres aspects.

 * La méthode `pack()` sert à positionner un élément dans son conteneur. Tant qu'on ne l'appelle pas, l'élément n'est pas affiché.

 * La méthode `mainloop()` passe la main à l'interface graphique: elle affiche ce qui doit être affiché et attend des actions de l'utilisateur. Elle rend la main quand toutes les fenêtres ouverts ont été fermées.

== Actions et réactions ==

Ajoutons un bouton à la fenêtre: avant l'appel à `mainloop`, on ajoute

{{{#!highlight python
bouton = Button(fenetre, text="Hop")
bouton.pack()
}}}

Et on a un bouton en dessous du message, mais ce bouton ne fait rien.

Pour ajouter une action, on utilise le paramètre `command` qui attend comme valeur une fonction. On remplace alors les deux lignes précédentes par:

{{{#!highlight python
def action():
    print("Clic.")

bouton = Button(fenetre, text="Hop", command=action)
bouton.pack()
}}}

Cette fonction est l'action à exécuter lorsque le bouton est cliqué. Ici elle se contente d'afficher un message dans le terminal où a été lancé le programme, ce qui n'est pas très utile pour une interface graphique. On peut modifier la définition de la fonction `action` pour qu'elle change le texte affiché dans le `Label`:

{{{#!highlight python
def action():
    texte['text'] = "Message reçu!"
}}}

L'affectation qui a lieu dans cette fonction modifie les propriétés de l'object `texte`, en changeant le texte affiché. On parle de ''configuration'' d'un objet, dans le jargon de Tk.

=== Exercice ===

Adapter ce programme pour que le texte affiché indique combien de fois le bouton a été cliqué depuis le début.

== Utilisation de classes ==

L'exemple du bouton qui modifie le message, et plus encore l'exercice qui compte le nombre de clics, montrent qu'il est nécessaire de maintenir un ''état'' et utilise pour cela des variables globales. Cela fonctionne, mais ce n'est pas très modulaire et cela peut être source d'erreurs. Pour améliorer la situation, la bonne méthode est d'utiliser la programmation par objets en définissant une classe pour notre boîte de dialogue. On peut procéder comme ceci:

{{{#!highlight python
from tkinter import *

class Dialogue (Frame):
    def __init__(self):
        Frame.__init__(self)

        self.texte = Label(self, text="Salut!")
        self.texte.pack()

        bouton = Button(self, text="Hop", command=self.action)
        bouton.pack()

    def action(self):
        self.texte['text'] = "Message reçu!"

dialogue = Dialogue()
dialogue.pack()
dialogue.mainloop()
}}}

La classe de notre boîte de dialogue ''dérive'' de la classe `Frame` de tkinter: une boîte de dialogue est uen fenêtre principale (`Frame`) avec des propriétés supplémentaires. La méthode `__init__` sert à effectuer les opérations nécessaires à la ''construction'' du dialogue:

 * On initialise l'objet comme un `Frame` avec la première instruction.

 * On crée l'élément pour le message et le bouton et on les inclut dans le dialogue. Le nom `self` désigne par convention l'objet que l'on est en train de créer.

 * Le message est stocké dans `self.texte`: c'est une variable propre à l'objet, on pourra y faire référence depuis les autres méthodes.

La fonction qui correspond à l'action à effectuer est maintenant `self.action`: c'est la méthode `action` de l'objet qui correspond au dialogue. C'est ici que l'on a gagné quelque chose: `self.action` connaît l'objet (c'est son premier argument, `self`) et elle peut donc accéder au champ `texte`, sans avoir besoin d'une variable globale.

Les trois dernières lignes de cette version illustrent que notre classe `Dialogue` peut être utilisée à peu près comme une classe standard de `tkinter`: on crée un objet, on le place avec `pack()` et on lui passe la main avec `mainloop`. Ces méthodes existent parce que `Dialogue` dérive de `Frame`.

=== Exercice ===

Refaire l'exemple du compteur en style objet.

=== Exercice ===

Étendre ce programme pour réaliser une calculatrice avec les quatre opérations!

Pour démarrer, le code suivant dispose les 10 chiffres et donne un bouton + et un bouton = et fait des additions:

{{{#!highlight python
from tkinter import *

class Chiffre (Button):
    def __init__(self, master, val):
        Button.__init__(self, master, text=val, command=self.clic)
        self.val = val
        self.dialogue = master

    def clic(self):
        self.dialogue.chiffre(self.val)

class Dialogue (Frame):
    def __init__(self):
        Frame.__init__(self)

        self.nombre = 0
        self.memoire = 0

        self.texte = Label(self, text="0")
        self.texte.grid(row=0, column=0)

        bouton = Chiffre(self, 0)
        bouton.grid(row=4, column=1)
        for i in range(1, 10):
            bouton = Chiffre(self, i)
            bouton.grid(row=3-(i-1)//3, column=(i-1)%3)

        add = Button(self, text="+", command=self.plus)
        add.grid(row=1, column=4)

        egal = Button(self, text="=", command=self.egal)
        egal.grid(row=2, column=4)

    def chiffre(self, val):
        self.nombre = self.nombre * 10 + val
        self.texte['text'] = self.nombre

    def plus(self):
        self.memoire += self.nombre
        self.nombre = 0
        self.texte['text'] = self.memoire

    def egal(self):
        self.plus()

dialogue = Dialogue()
dialogue.pack()
dialogue.mainloop()
}}}

Ce n'est pas encore uen calculatrice totalement satisfaisante! À vous de l'améliorer...

Interfaces graphiques en Python et Tkinter

Ceci correspond à la séance 1B4 de la formation 2013-2014.

Il y a de nombreuses bibliothèques pour créer des interfaces graphiques: Gtk+, Qt, wxWidgets, qui sont utilisables en Python. Cela fonctionne toujours un peu pareil, même si chacune a ses spécificités. Ici, on utilisera la bibliothèque tkinter, qui est la présentation en Python de la bibliothèque Tcl/Tk. La raison principale est qu'elle fait partie des bibliothèques standard de Python et qu'à ce titre elle est installée d'office et interagit bien avec les environnements de développement comme Idle.

/!\ La documentation standard de tkinter est assez succinte: elle se limite à une introduction et ne donne pas une liste exhaustive de toutes les classes et de toutes les options. Il faut se reporter aux documentations portant sur Tk pour en savoir plus.

Démarrage

L'incontournable premier programme avec tkinter ressemble à ceci:

   1 from tkinter import *
   2 fenetre = Frame()
   3 fenetre.pack()
   4 texte = Label(fenetre, text="Salut!")
   5 texte.pack()
   6 fenetre.mainloop()

Tapez ce programme et lancez-le, vous verrez apparaître une fenêtre qui dit juste "Salut". Le programme se terminera quand vous fermerez la fenêtre.

Quelques explications:

  • La bibliothèque s'appelle simplement tkinter. On l'inclut avec cette forme pour simplifier l'écriture des programmes.

    • /!\ Les exemples sont en Python 3. En Python 2, tout est pareil sauf qu'on écrit Tkinter avec une majuscule!

  • Les éléments d'interface sont créés par les appels à Frame et Label qui construisent des objets des classes portant ce nom. Le premier désigne la fenêtre principale (celle qui contient l'interface) et le deuxième désigne l'élément de texte qui y est affiché. Chaque élément est contenu dans un autre élément (l'agument du constructeur), sauf la fenêtre principale. Le deuxième argument (nommé) du constructeur Label sert à configurer l'élément que l'on crée: text précise le texte, d'autres options précisent d'autres aspects.

  • La méthode pack() sert à positionner un élément dans son conteneur. Tant qu'on ne l'appelle pas, l'élément n'est pas affiché.

  • La méthode mainloop() passe la main à l'interface graphique: elle affiche ce qui doit être affiché et attend des actions de l'utilisateur. Elle rend la main quand toutes les fenêtres ouverts ont été fermées.

Actions et réactions

Ajoutons un bouton à la fenêtre: avant l'appel à mainloop, on ajoute

   1 bouton = Button(fenetre, text="Hop")
   2 bouton.pack()

Et on a un bouton en dessous du message, mais ce bouton ne fait rien.

Pour ajouter une action, on utilise le paramètre command qui attend comme valeur une fonction. On remplace alors les deux lignes précédentes par:

   1 def action():
   2     print("Clic.")
   3 
   4 bouton = Button(fenetre, text="Hop", command=action)
   5 bouton.pack()

Cette fonction est l'action à exécuter lorsque le bouton est cliqué. Ici elle se contente d'afficher un message dans le terminal où a été lancé le programme, ce qui n'est pas très utile pour une interface graphique. On peut modifier la définition de la fonction action pour qu'elle change le texte affiché dans le Label:

   1 def action():
   2     texte['text'] = "Message reçu!"

L'affectation qui a lieu dans cette fonction modifie les propriétés de l'object texte, en changeant le texte affiché. On parle de configuration d'un objet, dans le jargon de Tk.

Exercice

Adapter ce programme pour que le texte affiché indique combien de fois le bouton a été cliqué depuis le début.

Utilisation de classes

L'exemple du bouton qui modifie le message, et plus encore l'exercice qui compte le nombre de clics, montrent qu'il est nécessaire de maintenir un état et utilise pour cela des variables globales. Cela fonctionne, mais ce n'est pas très modulaire et cela peut être source d'erreurs. Pour améliorer la situation, la bonne méthode est d'utiliser la programmation par objets en définissant une classe pour notre boîte de dialogue. On peut procéder comme ceci:

   1 from tkinter import *
   2 
   3 class Dialogue (Frame):
   4     def __init__(self):
   5         Frame.__init__(self)
   6 
   7         self.texte = Label(self, text="Salut!")
   8         self.texte.pack()
   9 
  10         bouton = Button(self, text="Hop", command=self.action)
  11         bouton.pack()
  12 
  13     def action(self):
  14         self.texte['text'] = "Message reçu!"
  15 
  16 dialogue = Dialogue()
  17 dialogue.pack()
  18 dialogue.mainloop()

La classe de notre boîte de dialogue dérive de la classe Frame de tkinter: une boîte de dialogue est uen fenêtre principale (Frame) avec des propriétés supplémentaires. La méthode __init__ sert à effectuer les opérations nécessaires à la construction du dialogue:

  • On initialise l'objet comme un Frame avec la première instruction.

  • On crée l'élément pour le message et le bouton et on les inclut dans le dialogue. Le nom self désigne par convention l'objet que l'on est en train de créer.

  • Le message est stocké dans self.texte: c'est une variable propre à l'objet, on pourra y faire référence depuis les autres méthodes.

La fonction qui correspond à l'action à effectuer est maintenant self.action: c'est la méthode action de l'objet qui correspond au dialogue. C'est ici que l'on a gagné quelque chose: self.action connaît l'objet (c'est son premier argument, self) et elle peut donc accéder au champ texte, sans avoir besoin d'une variable globale.

Les trois dernières lignes de cette version illustrent que notre classe Dialogue peut être utilisée à peu près comme une classe standard de tkinter: on crée un objet, on le place avec pack() et on lui passe la main avec mainloop. Ces méthodes existent parce que Dialogue dérive de Frame.

Exercice

Refaire l'exemple du compteur en style objet.

Exercice

Étendre ce programme pour réaliser une calculatrice avec les quatre opérations!

Pour démarrer, le code suivant dispose les 10 chiffres et donne un bouton + et un bouton = et fait des additions:

   1 from tkinter import *
   2 
   3 class Chiffre (Button):
   4     def __init__(self, master, val):
   5         Button.__init__(self, master, text=val, command=self.clic)
   6         self.val = val
   7         self.dialogue = master
   8 
   9     def clic(self):
  10         self.dialogue.chiffre(self.val)
  11 
  12 class Dialogue (Frame):
  13     def __init__(self):
  14         Frame.__init__(self)
  15 
  16         self.nombre = 0
  17         self.memoire = 0
  18 
  19         self.texte = Label(self, text="0")
  20         self.texte.grid(row=0, column=0)
  21 
  22         bouton = Chiffre(self, 0)
  23         bouton.grid(row=4, column=1)
  24         for i in range(1, 10):
  25             bouton = Chiffre(self, i)
  26             bouton.grid(row=3-(i-1)//3, column=(i-1)%3)
  27 
  28         add = Button(self, text="+", command=self.plus)
  29         add.grid(row=1, column=4)
  30 
  31         egal = Button(self, text="=", command=self.egal)
  32         egal.grid(row=2, column=4)
  33 
  34     def chiffre(self, val):
  35         self.nombre = self.nombre * 10 + val
  36         self.texte['text'] = self.nombre
  37 
  38     def plus(self):
  39         self.memoire += self.nombre
  40         self.nombre = 0
  41         self.texte['text'] = self.memoire
  42 
  43     def egal(self):
  44         self.plus()
  45 
  46 dialogue = Dialogue()
  47 dialogue.pack()
  48 dialogue.mainloop()

Ce n'est pas encore uen calculatrice totalement satisfaisante! À vous de l'améliorer...

WikISN: 1B4 (dernière édition le 2014-05-14 14:15:15 par 178)