UKOnline

Librairie Kivy

Il existe de nombreuses librairies pour réaliser des interfaces graphiques avec Python. Les principales différences qu'il y a entre ces librairies sont le support de plusieurs systèmes d'exploitation (Linux, Windows, MacOS...) ainsi que les composants graphiques proposés (bouton, zone de texte, menu, liste déroulante...). Dans la librairie de base fournie avec Python, on retrouve la librairie graphique Tk, très basique et pas forcément esthétique. Dans les autres choix majeurs, portables sur les principaux systèmes d'exploitation, on retrouve PyQt et WxPython. Dans ce livre, on va utiliser la librairie Kivy (la librairie Kivy peut être téléchargée sur son site officiel : https://kivy.org/), open source, portable, moderne et qui tourne également sur Android et iOS.

Hello World!

Voyons immédiatement comment créer un premier programme avec une interface graphique à l'aide de la librairie Kivy. Pour ne pas changer, on va simplement créer un Hello World. Le programme suivant affiche une fenêtre qui contient la phrase « Hello World! » :

L'exécution de ce programme crée une nouvelle fenêtre qui ne contient qu'une zone de texte non éditable avec la phrase « Hello World! », en taille $100$ pixels, comme on peut le voir sur la figure 1.

Hello World avec Kivy
La librairie Kivy permet de créer des programmes basés sur une interface graphique, comme celle-ci qui montre la phrase « Hello World! » dans une zone de texte non éditable.

Une interface graphique se définit à l'aide d'une nouvelle classe de type App, comme on l'a indiqué entre parenthèses après le nom de la classe. On a déjà vu cette construction précédemment, pour définir ses propres exceptions. Dans cette classe, il faut, au minimum, définir une méthode build qui va construire, et renvoyer, les composants à placer dans la fenêtre. Dans notre cas, on va se limiter à un composant de type Label, c'est-à-dire une zone de texte non éditable. Enfin, pour lancer le programme, il suffit de créer une instance de la classe qui a été définie, et d'appeler run.

Cette méthode run, spécifique aux objets de type App, initialise toute une série de choses permettant d'obtenir une interface graphique. Elle s'occupe notamment de mettre en place l'interaction avec le hardware de l'écran, de discuter avec les dispositifs d'entrée/sortie tels que l'écran multitouch, le clavier, l'accéléromètre, etc. et enfin, elle planifie plusieurs tâches à exécuter de manière régulière.

Fenêtre

Le principal élément d'une application qui possède une interface graphique est la fenêtre. Par défaut, une application Kivy se charge de créer une fenêtre, dans laquelle les composants créés par la méthode build seront placés.

On peut facilement personnaliser quelques caractéristiques de cette fenêtre. Tout d'abord, on peut modifier son titre et l'icône associée à la fenêtre et à l'application à l'aide des variables d'instance title et icon, à modifier dans la méthode build. On peut également modifier la taille, en pixels, de la fenêtre en configurant deux options. L'exemple suivant modifie ces trois éléments :

La modification de la taille de la fenêtre passe donc par une fonction dans la classe Config du module kivy.config et elle doit être faite avant le lancement de l'application.

Composant graphique

Une interface graphique est construite à partir de composants que l'on place sur une fenêtre. On retrouve plusieurs types de composants tels que des zones de texte éditables ou non, des boutons, des listes, des cases à cocher, etc. Les composants sont placés les uns dans les autres, permettant ainsi de les organiser visuellement sur la fenêtre. Par conséquent, chaque composant possède un composant parent. La figure 2 montre une fenêtre remplie de composants, ainsi qu'une vue explosée où on voit comment ils ont été imbriqués.

Fenêtre explosée
Les composants graphiques sont placés les uns dans les autres dans une fenêtre, créant ainsi une hiérarchie de composants.

Certains composants, appelés conteneurs, n'ont aucun rendu visuel. Il permettent simplement d'en accueillir d'autres, afin d'organiser l'aspect visuel global de l'interface graphique. Dans l'exemple de la figure 3, on en a utilisés deux.

Pour comprendre comment sont organisés les composants d'une fenêtre, on peut représenter les liens entre ces derniers sous forme d'un arbre, comme le montre la figure 4. Un flèche arrive sur chaque composant, reliant son composant parent à lui. De plus, une ou plusieurs flèches partent de chaque composant, le reliant à ses composants enfants, c'est-à-dire ceux qui y sont imbriqués.

Hiérarchie de composants
On peut représenter le hiérarchie qu'il y a entre les composants d'une fenêtre, reliant les composants parents à leurs composants enfants.

Widget

Un widget est un composant qui offre une interaction spécifique avec l'utilisateur, en pouvant lui montrer une information et en offrant une ou des interactions possibles avec lui. Un bouton permet, par exemple, de montrer un texte et de recevoir un clic gauche. On ne va pas passer en revue tous les widgets proposés par Kivy mais uniquement les principaux.

Commençons avec un exemple d'une simple interface graphique qui regroupe trois composants de base :

  • Le label est une zone permettant d'accueillir un texte qui ne peut pas être modifié. On l'utilise, par exemple, pour renseigner le nom d'un champ à remplir dans un formulaire.
  • La zone de texte est une zone dans laquelle l'utilisateur peut entrer un texte. On l'utilise, par exemple, comme champ d'un formulaire.
  • Le bouton est une zone sur laquelle l'utilisateur peut cliquer, pour déclencher une action. On l'utilise, par exemple, pour permettre à l'utilisateur de valider un formulaire.

La figure 5 montre le résultat que l'on souhaite obtenir, à savoir aligner horizontalement un label, une zone de texte et un bouton. Pour ce faire, on va utiliser un conteneur de type BoxLayout qui aligne les composants qui lui sont ajoutés, de gauche à droite et horizontalement.

Fenêtre de connexion
Pour aligner trois widgets horizontalement, on utilise un conteneur de type BoxLayout qui permet de gérer la mise en page.

Voici le programme qui permet de construire l'interface graphique que l'on souhaite pour la fenêtre de connexion :

Le premier composant que l'on crée, et qui sera renvoyé par la méthode build, est le BoxLayout. Ce dernier va contenir tous les autres composants qui lui seront ajoutés à l'aide de sa méthode add_widget. De plus, ils seront alignés horizontalement comme on l'a précisé dans le constructeur lors de la création du conteneur. Les composants sont placés dans le BoxLayout en suivant l'ordre des appels à add_widget.

Malgré que la fenêtre que l'on vient de construire est très simple, le nombre de lignes de code, et d'imports, a déjà bien augmenté. On va voir, plus loin dans ce chapitre, comment facilement décrire les composants à mettre dans une fenêtre et leur agencement, à l'aide d'un fichier KV.

On peut proposer un choix à l'utilisateur de différentes manières, selon le type de choix que l'on souhaite lui proposer :

  • La case à cocher propose un choix booléen à l'utilisateur, qui peut la cocher ou non. On l'utilise, par exemple, pour proposer à l'utilisateur d'activer ou non une option.
  • Le bouton radio permet de sélectionner un choix parmi un groupe d'options. On l'utilise, par exemple, pour proposer plusieurs choix à l'utilisateur qui doit en choisir un seul.
  • La liste déroulante permet de proposer toute une liste de choix à l'utilisateur qui pourra en choisir un seul. On l'utilise lorsqu'on a plus de choix qu'avec le bouton radio, pour permettre à l'utilisateur de sélectionner son pays, par exemple.

L'exemple suivant crée une interface graphique pour gérer des feux de signalisation et reprend ces trois composants :

La figure 6 montre le résultat obtenu en exécutant ce programme. La liste déroulante s'obtient avec un objet Spinner à qui on spécifie la liste des choix à proposer sous forme d'un tuple, avec le paramètre nommé values du constructeur. On voit d'ailleurs qu'elle est dépliée en l'attente d'un choix de l'utilisateur. Les cases à cocher et les boutons radios sont tous les deux représentés par un objet de type CheckBox. Pour obtenir des boutons radios, il faut les placer dans un même groupe que l'on définit à l'aide du paramètre nommé group du constructeur.

Contrôle de signalisation
Plusieurs widgets peuvent être utilisés pour proposer un choix à l'utilisateur, parmi lesquels les cases à cocher et boutons radios (CheckBox) et les listes déroulantes (Spinner).

La construction de cette interface graphique est encore plus complexe et plus lourde que la construction de la précédente. On a, en effet, dû utiliser plusieurs conteneurs imbriqués les uns dans les autres pour arriver à la mise en page désirée. La figure 7 montre la hiérarchie des composants utilisés dans cette fenêtre.

Hiérarchie de composants
L'interface graphique du programme de gestion de feux de signalisation possède trois niveaux d'imbrication de composants, permettant un arrangement vertical de lignes de composants arrangés horizontalement.

Gestionnaire de mise en page

Si on revient un moment sur l'exemple précédent, on se rend compte que les composants de l'interface graphique sont organisés en trois lignes et une colonne. Pour la construire, on a utilisé un conteneur de type BoxLayout, qui n'est sans doute pas le plus adapté pour cela. Un tel conteneur n'a aucun aspect visuel particulier, son seul but étant d'accueillir d'autres composants en les organisant dans l'espace d'une façon particulière. Ce type de composant est un conteneur gestionnaire de mise en page. Une fois le composant créé, on lui en ajoute d'autres grâce à la méthode add_widget.

Un conteneur de type BoxLayout positionne ses composants les uns à côté des autres, soit horizontalement, soit verticalement. On précise le sens que l'on désire à l'aide du paramètre nommé orientation du constructeur, qui peut valoir horizontal ou vertical.

Un autre conteneur très utilisé est le GridLayout, qui permet d'organiser les composants qu'il héberge sous forme d'une grille. Le nombre de lignes et de colonnes que l'on souhaite est spécifié par les paramètres nommés rows et cols du constructeur. Les éléments que l'on ajoute à ce conteneur sont placés dans la grille de gauche à droite et de haut en bas. L'exemple suivant crée une interface graphique avec $12$ boutons placés dans une grille de trois lignes et quatre colonnes :

Le résultat produit par l'exécution de ce programme est montré à la figure 8. Comme on a utilisé les nombres de $1$ à $12$ comme textes sur les boutons, on voit explicitement l'ordre dans lequel ils ont été ajoutés.

Grille de composants
Un conteneur de type GridLayout organise les composants qu'il contient sous la forme d'une grille dont on spécifie le nombre de lignes et de colonnes.

Enfin, on peut positionner les éléments exactement là où on le souhaite, avec la taille que l'on souhaite en utilisant un conteneur de type FloatLayout. Pour cela, il faut tout d'abord spécifier la taille du conteneur avec le paramètre nommé size_hint de son constructeur. Ensuite, lorsqu'on lui ajoute des composants, ceux-ci seront placés à la position qui a été spécifiée par le paramètre nommé pos. L'exemple suivant, dont le résultat se trouve à la figure 9, crée une fenêtre avec un conteneur de type FloatLayout qui contient deux boutons qui ont été placés à des endroits bien précis. Ces boutons ont par ailleurs une taille fixée à (0.3, 0.2), c'est-à-dire $30$% de la fenêtre en largeur et $20$% en hauteur :

Positionnement libre de composant
Un conteneur de type FloatLayout permet de placer les composants qu'il contient à n'importe quelle position que l'on précise lors de leur création.

Fichier KV

Simplifier la construction de l'interface graphique, c'est-à-dire établir la liste des composants avec leurs propriétés et leur placement, peut se faire plus facilement à l'aide du langage KV. La description se fait dans un fichier séparé, qui porte le même nom que celui de l'application (à savoir celui de la classe sans App), et avec l'extension .kv. Reprenons l'exemple précédent de la fenêtre de login ; le programme principal devient :

À côté du fichier contenant ce programme, on crée donc un autre fichier, nommé login.kv, avec le contenu suivant :

Le langage KV permet en fait de décrire un genre de dictionnaire de manière textuelle. Le premier composant déclaré dans ce fichier est celui qui sera inséré dans la fenêtre, c'est-à-dire celui qui est renvoyé par la méthode build, c'est-à-dire un conteneur de type BoxLayout dans notre cas. Vient ensuite un bloc indenté qui reprend les propriétés du composant et les autres composants qu'on veut lui imbriquer. Dans notre cas, on imbrique, dans l'ordre, un label, une zone de texte et un bouton, dans le conteneur.