Python et les interfaces graphiques

Python et Tkinter

Tkinter

Le module Tkinter cite{tkinter-intro} est basé sur Tk, la librairie développée par J.~Ousterout pour faire des interfaces graphiques en Tclcite{tcltk}, et disponible sur plusieurs plates-formes (dont X11, Mac OS X, MS-Windows). Tkinter est le standard de facto pour Python offrant une vision objet de Tk. Toutefois, Tcl est un langage où tout est chaînes de caractères. Il en résulte qu’un certain nombre de valeur utilisées dans le cas de Tkinter sont des chaînes de caractères (utilisées directement comme tel ou bien sous couvert de variables offertes par le module).

Comme tout programme avec une interface graphique, l’exécution est dirigée par les événements. Les interactions entre Python et l’interface graphique peut prendre plusieurs formes:

  • Traitement GUI Python puis Tkinter puis Tk puis Librairie graphique.
  • Evénement graphique puis Tk puis Tkinter puis Traitements Python.

Premiers pas

Le «hello world!» version graphique tient en quatre lignes, présentées dans l’exemple suivant. Le module Tkinter est chargé. Un widget (ici un Label) est créé puis ajouté à l’environnement graphique (pack). Enfin, la boucle d’événements est démarrée. Elle est active tant que la fenêtre n’est pas fermée (et le prompt de l’interpréteur reste suspendu).

>>> import Tkinter
>>> widget = Tkinter.Label(None, text='hello world!')
>>> widget.pack()
>>> widget.mainloop()
_images/ch6sample01-mwm.png

Configuration de widgets

Dans le cas du Label de «hello world!», il n’y a pas de widget parent (premier argument None). Il est attaché directement à la fenêtre par défaut. Le texte du Label représente sa configuration. La configuration peut être réalisée ou changée après la création avec la méthode config() des widgets. L’exemple suivant est identique en terme de résultat à l’exemple précédent.

>>> widget = Tkinter.Label(None)
>>> widget.config(text='hello world!')
>>> widget.pack()
>>> widget.mainloop()

Composition de widgets avec pack

Les composition des widgets est délégué au gestionnaire de géométrie.

  • Le placement du widget se fait par rapport à son contenant (avec l’option de configuration side) en utilisant un placement cardinal (Tkinter.TOP, Tkinter.LEFT, Tkinter.RIGHT, Tkinter.BOTTOM). Par défaut, un widget est attaché en haut, ou au-dessous des widgets existants
  • Si l’on souhaite une occupation maximale de l’espace, utiliser l’option de configuration expand=YES. La direction de l’occupation maximale est précisée par fill. Elle peut être en largeur (Tkinter.X), en hauteur (Tkinter.Y) ou les deux (Tkinter.BOTH).

Lancer des commandes

Pour initier un traitement depuis une interface graphique, la méthode courante est de définir un bouton, un menu, une scrollbar, etc. et d’y associer un traitement (fonction ou méthode sans argument). L’exemple suivant crée un bouton et y associe la commande sys.exit pour quitter l’exécution du programme (dans le cas courant l’interpréteur complet). L’option text précise le texte qui apparaît sur le bouton.

>>> import sys
>>> widget = Tkinter.Button(None)
>>> widget.config(text='press to quit', command=sys.exit)
>>> widget.pack()
>>> widget.mainloop()
_images/ch6sample03-mwm.png

Composition et redimensionnement

Relations entre la composition et le redimensionnement:

  • par défaut le widget conserve sa taille initiale,
  • pour suivre les changements utiliser expand.

L’exemple suivant configure le bouton de telle sorte qu’il occupe toujours tout l’espace en largeur lors du redimensionnement de la fenêtre. Par contre, le bouton n’occupe pas tout l’espace en hauteur.

>>> import Tkinter
>>> import sys
>>> widget = Tkinter.Button(None)
>>> widget.config(text='press to quit', command=sys.exit)
>>> widget.pack(expand=Tkinter.YES, fill=Tkinter.X)
>>> widget.mainloop()
_images/ch6sample04-mono.png

Boutons et traitements

Un bouton ne peut déclencher qu’un traitement, il est donc souvent nécessaire d’utiliser une fonction qui regroupe les traitements. Dans l’exemple suivant, la fonction handler affiche «hello world» sur la sortie standard avant de quitter l’application.

>>> import Tkinter
>>> import sys
>>> def handler():
...     print 'hello world!'
...     sys.exit()
...
>>> widget = Tkinter.Button(None)
>>> widget.config(text='press to quit', command=handler)
>>> widget.pack(expand=Tkinter.YES, fill=Tkinter.X)
>>> widget.mainloop()

Une classe d’utilisation d’un widget graphique est une bonne manière de créer une relation forte entre widget, traitements et données. Cet ensemble (bouton, données, traitements) est facilement réutilisable. L’exemple présente un exemple de bouton pour quitter une application où le message est paramétrable.

>>> import Tkinter
>>> import sys
>>> class HelloQuitButton:
...     def __init__(self, msg='Quit'):
...         self.msg = msg
...         b = Tkinter.Button(None)
...         b.config(text=self.msg, command=self.handle)
...         b.pack()
...     def handle(self):
...         print self.msg
...         sys.exit()
...
>>> hqb = HelloQuitButton()
>>> Tkinter.mainloop()

Un handler peut être défini comme un objet: définition d’une classe représentant une fonction et instanciation pour l’utilisation. L’exemple suivant redéfinit le handler précédent comme une classe.

>>> import Tkinter
>>> import sys
>>> class Callable:
...     def __init__(self):
...             self.msg = 'hello world!'
...     def __call__(self):
...             print self.msg
...             import sys
...             sys.exit()
...
>>> widget = Tkinter.Button(None)
>>> widget.config(text='hello', command=Callable())
>>> widget.pack()
>>> Tkinter.mainloop()

Définition de bindings

La fonction bind permet d’associer une fonction (à 1 argument) à un événement et à un widget. Les événements les plus courants sont les clics souris et la saisie au clavier. L’exemple suivant crée deux fonctions pour afficher «hello world!» et pour afficher «bye» sur la sortie standard avant de quitter le programme. Ensuite, il crée un label et y associe la fonction hello au clic gauche et la fonction quit au double clic.

>>> import Tkinter
>>> import sys
>>> def hello(event):
...     print 'hello world!'
...
>>> def quit(event):
...     print 'bye'
...     sys.exit()
...
>>> widget = Tkinter.Label(None, text='press')
>>> widget.pack()
>>> widget.bind('<Button-1>', hello)
'805810704hello'
>>> widget.bind('<Double-1>', quit)
'805810224quit'
>>> widget.mainloop()

L’événement «reçu» par un handler contient des informations qui peuvent être interprétées dans le cadre du traitement. L’exemple suivant définit deux fonctions dont le but est d’afficher un caractère tapé au clavier et d’afficher les coordonnées de la souris. La classe Tk représente la fenêtre principale du programme. Le label créé est associé à cette fenêtre (premier paramètre du constructeur). Puis, les deux fonctions sont associées au clavier et au clic sur le label. L’utilisation de focus sélectionne le label pour recevoir les événements clavier. Enfin, la boucle d’événements est démarré sur la fenêtre principale. Lorsque la fenêtre est active et que l’on tape une touche au clavier, cela génère un événement passé à la fonction onKey qui va en extraire le caractère associé. (Même principe pour le clic de souris.)

>>> import Tkinter
>>> def onKey(event):
...     print 'got key', event.char
...
>>> def onClick(event):
...     print event.widget, event.x, event.y
...
>>> root = Tkinter.Tk()
>>> lab = Tkinter.Label(root, text='hello world')
>>> lab.bind('<KeyPress>', onKey)
'805787968onKey'
>>> lab.bind('<Button-1>', onClick)
'808388888onClick'
>>> lab.focus()
>>> lab.pack()
>>> root.mainloop()
.805789368 30 14
.805789368 44 11
got key e
got key r

Quelques noms de bindings courants

Nom Signification
<KeyPress> Pression sur une touche
<KeyPress-a> Pression sur la touche ‘A’ (minuscule)
<KeyPress-A> Pression sur la touche ‘A’ (majuscule)
<Return> Pression sur entrée
<Escape> Touche d’échappement
<Up> <Down> Pression sur les flèches
<Button-1> Click souris gauche
<Button-2> Click souris milieu (ou les deux)
<Button-3> Click souris droit
<ButtonRelease> Fin de click gauche
<Motion> Mouvement de la souris
<B1-Motion> Mouvement de la souris avec click gauche
<Enter> <Leave> Entrée et sortie souris d’un widget
<Configure> Redimensionnement de la fenêtre
<Map> <Unmap> Ouverture et iconification de la fenêtre

Assemblage de widgets

L’assemblage de widgets se fait par utilisation de containers graphiques, principalement en définissant des Frames auxquels sont rattachés les widgets. L’exemple suivant utilise une Frame pour regrouper deux boutons. La commande associée au second bouton permet la fermeture de la fenêtre sans arrêter l’interpréteur.

>>> import Tkinter
>>> def hello():
...     print 'hello world!'
...
>>> win = Tkinter.Frame()
>>> win.pack()
>>> Tkinter.Label(win, text='hello world!').pack(side=Tkinter.TOP)
>>> b1 = Tkinter.Button(win, text='hello', command=hello)
>>> b1.pack(side=Tkinter.LEFT)
>>> b2 = Tkinter.Button(win, text='quit', command=win.quit)
>>> b2.pack(side=Tkinter.RIGHT)
>>> win.mainloop()
_images/ch6sample10-mwm.png

Widgets orientés objet

L’approche objet s’applique bien aux interfaces graphiques en utilisant l’héritage pour définir de nouveaux widgets par spécialisation ou extension. L’exemple suivant présente une spécialisation du widget Button en bouton pour quitter l’application (en fait le bouton ne détruit que lui).

>>> class MyByeButton(Tkinter.Button):
...     def __init__(self, parent=None, **config):
...         Tkinter.Button.__init__(self, parent, config)
...         self.pack()
...         self.config(command=self.callback)
...     def callback(self):
...         print 'bye...'
...         self.destroy()
...
>>> MyByeButton(text='hello world')
>>> Tkinter.mainloop()

A propos de l’aspect

La spécification de la taille et de la couleur est valable pour tous les widgets de Tkinter. La spécification des polices, tailles et styles de caractères est valable pour tout widget contenant du texte. L’exemple suivant définit un label écrit en jaune sur fond bleu avec la police Courrier 20 gras dans une surface totale de 3 lignes sur 20 caractères.

>>> root = Tkinter.Tk()
>>> msg = Tkinter.Label(root, text='hello world')
>>> msg.config(font=('courier', 20, 'bold'))
>>> msg.config(bg='blue', fg='yellow')
>>> msg.config(height=3, width=20)
>>> msg.pack(expand=Tkinter.YES, fill=Tkinter.BOTH)
_images/ch6sample12-mwm.png

Application multi-fenêtres

Il est possible de définir dans une même applications des fenêtres indépendantes (non contenues visuellement dans la même fenêtre). L’exemple suivant définit deux fenêtres indépendantes contenant les labels «hello» et «world».

>>> root = Tkinter.Tk()
>>> win1 = Tkinter.Toplevel(root)
>>> Tkinter.Label(win1, text='hello').pack()
>>> win2 = Tkinter.Toplevel(root)
>>> Tkinter.Label(win2, text='world').pack()

Terminaison d’une application multi-fenêtres:

  • destroy() supprime récursivement la fenêtre concernée,
  • quit() termine la boucle d’événements sans détruire la fenêtre.
>>> root = Tkinter.Tk()
>>> win1 = Tkinter.Toplevel(root)
>>> b1 = Tkinter.Button(win1)
>>> b1.config(text='moi', command=win1.destroy)
>>> b1.pack()
>>> win2 = Tkinter.Toplevel(root)
>>> b2 = Tkinter.Button(win2)
>>> b2.config(text='nous', command=root.destroy)
>>> b2.pack()
>>> root.mainloop()

Petit tour des widgets courants

Dans la suite du chapitre nous considérons que le contenu module Tkinter est importé dans l’espace courant. Bien qu’étant plutôt un défenseur de l’utilisation des modules avec leur préfixe, il est vrai que, en mode interactif, cela a ses avantages de tout importer dans le scope global. Mais faites toujours attention aux risques de conflits de nom!

>>> from Tkinter import *

Champ de texte

Le widget Entry offre un champ de saisie de texte sur une seule ligne. Il dépend des options de configuration suivantes:

  • state='disable' lecture seule,
  • show='*' saisie masquée.

L’exemple suivant construit une petit interface avec une zone de saisie et deux boutons. La touche entrée est associée dans la boite de saisie à la fonction fetch(). Le premier bouton affiche sur la sortie standard le contenu de la zone de saisie. Pour cela, il utilise aussi la commande fetch(). Le second bouton ferme l’interface.

>>> def fetch():
...     print 'Texte: <%s>' % ent.get()
...
>>> root = Tk()
>>> ent = Entry(root)
>>> ent.insert(0, 'Saisir texte')
>>> ent.pack(side=TOP, fill=X)
>>> ent.bind('<Return>', (lambda event: fetch()))
>>> b1 = Button(root, text='Fetch', command=fetch)
>>> b1.pack(side=LEFT)
>>> b2 = Button(root, text='Quit', command=root.destroy)
>>> b2.pack(side=RIGHT)
>>> root.mainloop()
_images/ch6sample16-mwm.png

Manipulations de texte

  • index dans une entrée de texte: De '0' à END,
  • position d’insertion du curseur INSERT,
  • get(deb, fin) retourne le texte à la position donnée,
  • insert(pos, txt) insert le texte txt à la position pos,
  • delete(deb [,fin]) supprime le texte entre les position deb et fin (le second est optionnel).

Agencement de widgets

Un formulaire de saisie est un exemple type d’agencement de plusieurs widgets Label et Entry. Afin d’avoir un formulaire bien aligné, l’utilisation du gestionnaire cardinal (au travers de pack) est remplacé par l’utilisation du gestionnaire «grille». La méthode grid(row=X, column=Y) permet l’alignement des différents éléments dans une grille. L’exemple suivant construit un formulaire à trois entrées.

>>> root = Tk()
>>> r = 0
>>> for line, item in enumerate(['foo', 'bar', 'stuff']):
...     l = Label(root, text=item, width=10)
...     e = Entry(root, width=10)
...     l.grid(row=line, column=0)
...     e.grid(row=line, column=1)
_images/ch6sample17-mwm.png

Attention

Les deux gestionnaires (cardinal et grille) ne se marient pas toujours bien.

Zone de texte

Le widget Text offre une zone de saisie et de manipulation de texte sous la forme d’une chaîne de caractères multi-lignes. L’exemple suivant définit une zone de texte et un bouton. L’utilisation de ce dernier affiche le contenu de la zone de texte sur la sortie standard à l’aide de la fonction fetch(). L’option wrap=WORD de la zone de texte précise que le découpage de son contenu se fait sur les mots (lorsqu’un mot ne tient pas sur la ligne, la zone de texte passe automatiquement à la ligne).

>>> def fetch():
...     print txt.get('1.0', END+'-1c')
...
>>> root = Tk()
>>> txt = Text(root, height=5, width=20, wrap=WORD)
>>> txt.insert('1.0', 'Saisir du texte sans couper les mots...')
>>> txt.pack(side=TOP)
>>> Button(root, text='fetch', command=fetch).pack()
>>> root.mainloop()
_images/ch6sample18-mwm.png

Manipulation de texte

  • index dans une zone de texte: de '1.0' (ligne.colonne) à END,
  • INSERT donne la position d’insertion du curseur,
  • SEL_FIRST, SEL_LAST contient les position de début et fin de la sélection,
  • get(deb, fin) retourne le texte à la position donnée,
  • get('1.0', END+'-1c') retourne tout le texte contenu dans la zone.
  • insert('li.co', txt) ajoute le texte à la position ‘li.co’,
  • delete(deb [,fin]) supprime le texte entre les position deb et fin,
  • search(chaine, deb, fin) retourne la position de début de la chaîne dans la zone de texte,
  • see('li.co') Fait défiler l’affichage pour rendre la position donnée visible,
  • pour les déplacements relatifs dans une zone de texte par rapport à une position: verb@+/-@ un certain nombre de 'c' (caractère), 'w' (mot) ou 'l' (ligne).

Listboxes

Le widget Listbox permet de gérer des listes d’éléments et offre des méthodes pour l’ajout, le retrait et la sélection d’éléments. L’exemple suivant définit une listbox contenant 10 entrée et un bouton. L’utilisation du bouton déclenche la fonction fetch() qui affiche sur la sortie standard l’entrée de la liste sélectionnée (ou la première ligne par défaut).

>>> def fetch():
...     print list.get(ACTIVE)
...
>>> root = Tk()
>>> list = Listbox(root)
>>> list.pack(side=TOP)
>>> Button(root, text='fetch', command=fetch).pack()
>>> for index in range(10):
...     list.insert(index, 'ligne-' + str(index))
...
>>> root.mainloop()

Manipulation de listes

  • index dans une liste: de '0' à END.
  • curselection() retourne l’index sélectionné,
  • get(idx) retourne l’élément à l’index idx,
  • get(ACTIVE) Retourne l’élément sélectionné,
  • insert(idx, element) ajoute l’élément à l’index idx,
  • delete(deb [,fin]) supprime les éléments entre les index deb et fin (le second est optionnel).

Barres de défilement

Le widget scrollbar permet de gérer le défilement (horizontal ou vertical) des zones de texte et des listboxes. Il utilise des méthodes de défilement fournies par ces deux widgets. L’exemple suivant reprend le précédent en y ajoutant une barre de défilement pour gérer une liste de 100 éléments. Dans un premier temps, il crée une frame pour regrouper la liste et la barre. Ces deux dernières sont configurées pour que la liste évolue en fonction de l’utilisation de la barre.

>>> root = Tk()
>>> frame = Frame(root)
>>> frame.pack(side=TOP)
>>> list = Listbox(frame)
>>> sbar = Scrollbar(frame)
>>> sbar.config(command=list.yview)
>>> list.config(yscrollcommand=sbar.set)
>>> sbar.pack(side=RIGHT, fill=Y)
>>> list.pack(side=LEFT, expand=YES, fill=BOTH)
>>> Button(root, text='fetch', command=fetch).pack(side=BOTTOM)
>>> for index in range(100):
...     list.insert(index, 'ligne-' + str(index))
...
>>> root.mainloop()
_images/ch6sample20-mwm.png

Boîtes à cocher et boutons radio

Les deux widgets suivants permettent de gérer des collections de données:

  • checkbutton permet un choix multiple (boîtes à cocher),
  • radiobutton permet un choix unique (boutons radio).

Les options de ces deux widgets sont:

  • command pour associer une action,
  • variable pour fixer et récupérer la valeur.

La variable doit être déclarée comme une variable Tkinter. Cela permet une interaction entre Tk et Python initiée par Tk (principe du callback): IntVar et StringVar permettent de créer des objets de stockage de valeurs entières ou de chaînes.

Boîte à cocher

L’exemple suivant crée un ensemble de trois boîtes à cocher dont la valeur va être représentée par un entier. Chaque boîte est indépendante, et c’est le fait de mémoriser les trois variables Tkinter dans une liste qui les relie. La fonction state() déclenchée par le bouton affiche l’état des différentes boîtes.

>>> vars = list()
>>> root = Tk()
>>> for item in ['foo', 'bar', 'stuff']:
>>>     var = IntVar()
>>>     Checkbutton(root, text=item,
...         variable=var).pack(side=LEFT)
...     vars.append(var)
...
>>> def state():
...     for v in vars:
...         print v.get(),
...
>>> b = Button(root, text='State', command=state)
>>> b.pack(side=LEFT)
>>> root.mainloop()
_images/ch6sample21-mwm.png

Boutons radio

L’exemple suivant crée un ensemble de trois boutons radio dont la valeur va être représentée par une chaîne. Les trois boutons sont indépendant et c’est le fait d’utiliser la même variable Tkinter qui les relie. La fonction state() déclenchée par le bouton affiche le nom du bouton radio actuellement sélectionné.

>>> root = Tk()
>>> var = StringVar()
>>> for item in ['foo', 'bar', 'stuff']:
...     rb = Radiobutton(root, text=item,
...                       value=item,
...                       variable=var)
...     rb.pack(side=LEFT)
...
>>> def state():
...     print var.get()
...
>>> b = Button(root, text='State', command=state)
>>> b.pack()
>>> root.mainloop()
_images/ch6sample22-mwm.png

Autres widgets prêt à l’emploi

Boîtes de dialogue

Le module tkMessageBox offre un certain nombre de boîtes de dialogue standard pour l’affichage de messages (avec une icône associée). Ces boîtes prennent le contrôle de la boucle d’événements (tant qu’elle ne sont par fermées). Les trois boîtes de base sont:

  • showinfo pour donner de l’information,
  • showwarning pour signaler un warning,
  • showerror pour signaler une erreur.
>>> from Tkinter import *
>>> from tkMessageBox import *
>>> root = Tk()
>>> showinfo('Information', "Python c'est simple")
'ok'
>>> showwarning('Attention', "Risque de dependance")
'ok'
>>> showerror('Trop tard', "Peux plus m'en passer")
'ok'
_images/ch6sample23-2-mwm.png

Pour les interactions primitives avec l’usager, les widgets askyesno et askokcancel permettent d’avoir une réponse booléenne à une question: 1 si ‘yes’ et 0 si ‘no’.

>>> root = Tk()
>>> if askyesno('Question', "Python c'est simple"):
...     showinfo('xxx', 'Merci !!!')
... else:
...     showwarning('xxx', 'Dommage...')
...
_images/ch6sample24-mwm.png

Pour des interactions basiques complémentaires avec l’usager, askfloat, askinteger et askstring permettent de demander des réponses typées.

>>> from tkSimpleDialog import *
>>> root = Tk()
>>> askfloat('Float', 'Entrer un nombre decimal')
123
>>> askinteger('Integer', 'Entrer un nombre entier')
123.4
>>> askstring('String', 'Entrer une chaine')
'qwerty'

Le module tkFileDialog fournit des boîtes de dialogue pour les interactions relatives aux fichiers. Les différentes widgets offerts respectent les normes de l’interface graphique (X11, Mac OS X ou MS-Windows):

  • askopenfilename, asksaveasfilename et askdirectory retournent le nom du fichier sélectionné ou une chaîne vide,
  • askopenfile et asksaveasfile retournent l’objet représentant le fichier en mode 'r' (lecture), 'w' (écriture) , ou None,
  • filetypes, initialdir et initialfile sont des variables destinés configurer la recherche de fichiers.
>>> from tkFileDialog import *
>>> askopenfilename(filetypes=[('Text', '.txt')])
'/home/raphael/enseign/python/bar.txt'
>>> asksaveasfilename(initialdir='/tmp')
'/tmp/foo.txt'

Barre de menus

La définition de menus dans une fenêtre se fait à l’aide d’une Frame dédiée. Le widget Menubutton permet de définir un élément de la barre et le widget Menu permet de définir les commandes associées à l’élément. L’exemple suivant crée une frame pour servir de barre de boutons puis un élément de cette barre pour les commandes relatives aux fichiers. Ensuite, une entrée est ajouté dans ce premier menu offrant la fonction «Quit».

>>> root = Tk()
>>> menubar = Frame(root)
>>> menubar.pack(side=TOP, fill=X)
>>> fbutton = Menubutton(menubar, text='File', underline=0)
>>> fbutton.pack(side=LEFT)
>>> filemenu = Menu(fbutton)
>>> filemenu.add_command(label='Quit', command=root.destroy, underline=0)
>>> fbutton.config(menu=file) # lien entre bouton et menu

L’organisation des menus dans la barre se fait à l’aide des variables et fonctions suivantes:

  • bd, relief nombre de points et relief du menu (RAISED, SUNKEN, RIDGE),
  • add_separator() ajoute une séparation entre deux commandes d’un menu,
  • add_cascade() création d’un sous-menu,
  • entryconfig(num, state=...) fixe l’aspect disponible (ENABLED, valeur par défaut) ou non disponible (DISABLED) d’une entrée d’un menu.

L’exemple suivant définit un sous menu «Stuff» à notre menu «File».

>>> submenu = Menu(fbutton, tearoff=0)
>>> submenu.config(bd=2)
>>> submenu.add_command(label='Sub stuff(destroy)',
...                     command=root.destroy,
...                     underline=0)
>>> filemenu.add_cascade(label='Stuff', menu=submenu, underline=0)
_images/ch6sample27-mwm.png

Autres extensions disponibles

  • Tix Tk Interface eXtension cite{tixbook,tixuser} est une extension de Tkinter qui offre des widgets de plus haut niveau (plus de 40) comme ComboBox, NoteBook, DirTree, FileSelectBox, etc. http://tix.sourceforge.net
  • wxPython offre l’accès à la librairie wxWindow http://www.wxpython.org
  • PyQt offre l’accès au toolkit Qt (Opie pour Ipaq).
  • PyKDE offre l’accès aux librairies KDE.
  • PyGTK offre l’accès aux widgets GTK.

Exercices

Module «Vue» du MVC

Développer une interface graphique en utilisant Tkinter, reposant sur les traitements du contrôleur défini dans le chapitre précédent et composées des éléments suivants:

  • une Listbox associée à une Scrollbar donne la liste des numéros de dossier, nom et prénom des étudiants~;
  • un formulaire (ensemble de Label et Entry) donnant les information d’un étudiant sélectionné dans la Listbox (par exemple avec un double clic)~;
  • un formulaire de saisie associé à un Button pour entrer de nouvelles fiches d’étudiants.