Le Raspberry Pi en console avec urwid
Introduction
urwid est une bibliothèque assez récente dans le paysage UNIX. Elle repose essentiellement sur une autre bibliothèque qui est relativement ancienne, la bibliothèque curses. À l’origine de curses, on retrouve les premiers développeurs informatiques. À l’époque, la console était le seul moyen d’interagir avec la machine. Ainsi, les premiers développeurs ont très rapidement dû faire face à la nécessité d’assembler un jeu de fonctions pour dessiner des courbes et écrire du texte afin de créer des interfaces utilisateurs. La bibliothèque curses était née et les premières interfaces graphiques avec. Cependant, l’apprentissage de cette bibliothèque est déroutant et relativement complexe. De ce fait, ce chapitre se concentrera sur l’étude de urwid, une bibliothèque beaucoup plus simple à prendre en main et avec laquelle le développeur peut rapidement créer des interfaces console riches. Dernier point important : contrairement à la bibliothèque curses, qui est livrée avec la bibliothèque standard de Python, la bibliothèque urwid doit être installée via le gestionnaire de paquets pip.
urwid, les fondamentaux
La bibliothèque urwid s’articule principalement autour de l’utilisation de widgets. Un widget est simplement une classe Python avec des attributs pré-conçus : un champ texte, une liste déroulante, un espace vide, etc. Dans le jargon urwid cependant, un widget est en charge d’afficher du texte, de représenter un bouton ou de servir de conteneur pour encapsuler d’autres widgets. Comme avec d’autres frameworks graphiques, la construction d’une application avec urwid s’effectue en imbriquant successivement des widgets les uns dans les autres.
La bibliothèque urwid n’est malheureusement pas parmi les bibliothèques standards Python. Vous devez l’installer via l’outil pip3 :
pi@raspberrypi:~ $ sudo pip3 install urwid
Voyons les éléments nécessaires à l’écriture d’une application avec urwid. Ouvrez une console Python et tapez :
>>> import urwid
>>> texte = urwid.Text("Bonjour")
>>> filler = urwid.Filler(texte)
>>> main = urwid.MainLoop(filler)
>>> main.run()
Le message "Bonjour" apparaît alors au milieu de l’écran. Le positionnement par défaut des composants d’une application s’effectue toujours de la gauche vers la droite. Un autre aspect à...
Projet #1 : une horloge en console
Voyons comment construire une application légèrement plus complexe, à savoir une horloge géante affichée dans la console (Chapitre_4/urwid_2.py) :
1 #!/usr/bin/env python3
2 import urwid
3 import time
La logique du programme est encapsulée au sein de la classe Horloge afin d’en réduire la complexité. L’initialisation de la classe configure l’horloge, définit la palette de couleurs, instancie la boucle événementielle et attache une alarme de 1 seconde à une fonction :
6 class Horloge:
7 def __init__(self):
8 self.configurer_horloge()
9 self.palette = [("horloge", "dark blue", "")]
10 self.boucle = urwid.MainLoop(
11 self.horloge,
12 palette=self.palette,
13 unhandled_input=self.quitter)
14 self.boucle.set_alarm_in(1, self.actualiser)
Nous étudierons plus tard l’utilité d’une alarme. Mais en premier lieu, examinons la configuration de l’horloge :
21 def configurer_horloge(self):
22 text = time.strftime("%H:%M:%S") ...
Projet #2 : un navigateur de fichiers en console
Pour continuer l’étude de la bibliothèque, voici un programme légèrement plus complexe, à savoir un navigateur de fichiers (Chapitre_4/urwid_3.py) :
1 #!/usr/bin/env python3
2 import urwid
3 import os
Comme dans l’exemple précédent, toute la logique de création du navigateur de fichiers est encapsulée dans une classe appelée NavigateurFichiers. À sa création, cette classe initialise un certain nombre de variables et procède à un listing des fichiers du répertoire courant :
6 class NavigateurFichiers(urwid.WidgetPlaceholder):
7 def __init__(self, chemin):
8 self.chemin_courant = chemin
9 self.chemin_prec = None
10 self.liste_fichiers = sorted(os.listdir(self.chemin_courant))
11 if self.chemin_courant != "/":
12 self.liste_fichiers.insert(0, "..")
13 super(NavigateurFichiers, self).__init__(self.creer_contenu())
La variable liste_fichiers est stockée comme membre de l’instance afin d’être réutilisée plus tard. Le code prend aussi soin d’ajouter le chemin .. en début de liste pour que l’utilisateur puisse remonter d’un répertoire lors de la navigation. Voyons en détail la fonction creer_contenu :
30 def creer_contenu(self):
31 contenu = []
32 ...
Projet #3 : un hôte virtuel
Ce dernier projet met en avant une fonction essentielle d’urwid : les signaux. Pour capturer le changement d’état d’un widget, urwid utilise un signal. Celui-ci est rattaché à une fonction qui est ensuite appelée lorsque le signal est capté par l’application. Les signaux ont déjà été utilisés dans les applications précédentes mais ce projet s’attarde vraiment dessus. Voici le code de notre hôte virtuel (Chapitre_4/urwid_4.py) :
1 #!/usr/bin/env python3
2 import urwid
3
4
5 def main():
6 question = urwid.Text("Quel est votre nom ? ")
7 reponse = urwid.Edit(edit_text="")
8 bonjour = urwid.Text("")
9 sortir = urwid.Button("Sortir")
10
11 div = urwid.Divider()
12
13 pile = urwid.Pile([question, div, reponse, div, bonjour, div, sortir])
14
15 hote = urwid.Filler(pile, valign="top")
16
17 def dis_bonjour(bouton, txt):
18 bonjour.set_text(f"Bonjour {txt} ! Comment allez-vous ?")
19
20 def quitter(bouton):
21 raise urwid.ExitMainLoop()
22
23 ...
Conclusion
Ce chapitre couvre en détail la bibliothèque urwid et fournit des exemples pour tirer pleinement profit des possibilités offertes par celle-ci.