Tutoriel 2 - Rendre les choses intéressantes

Dans le Tutoriel 1, nous avons généré une ébauche de projet capable de s’exécuter, mais nous n’avons pas écrit de code nous-mêmes. Jetons un coup d’œil à ce qui a été généré pour nous.

Ce qui a été généré

Dans le répertoire src/helloworld, vous devriez voir 3 fichiers : __init__.py, __main__.py et app.py.

__init__.py marque le répertoire helloworld comme un module Python importable. C’est un fichier vide ; le simple fait qu’il existe indique à l’interpréteur Python que le répertoire helloworld définit un module.

Le fichier __main__.py marque le module helloworld comme un type spécial de module - un module exécutable. Si vous essayez d’exécuter le module helloworld'' en utilisant ``python -m helloworld'', le fichier ``__main__.py est l’endroit où Python commencera à s’exécuter. Le contenu de __main__.py` est relativement simple :

from helloworld.app import main

if __name__ == "__main__":
    main().main_loop()

Il importe la méthode main de l’application helloworld, et s’il est exécuté en tant que point d’entrée, il appelle la méthode main(), et démarre la boucle principale de l’application. La boucle principale est la façon dont une application GUI écoute les entrées de l’utilisateur (comme les clics de souris et les pressions sur le clavier).

Le fichier le plus intéressant est app.py - il contient la logique responsable de la création de notre fenêtre d’application :

import toga
from toga.style import Pack
from toga.style.pack import COLUMN, ROW

class HelloWorld(toga.App):
    def startup(self):
        main_box = toga.Box()

        self.main_window = toga.MainWindow(title=self.formal_name)
        self.main_window.content = main_box
        self.main_window.show()

def main():
    return HelloWorld()

Examinons-le ligne par ligne :

import toga
from toga.style import Pack
from toga.style.pack import COLUMN, ROW

Tout d’abord, nous importons la boîte à outils toga, ainsi que quelques classes et constantes utilitaires liées au style. Notre code ne les utilise pas encore, mais nous les utiliserons bientôt.

Ensuite, nous définissons une classe :

class HelloWorld(toga.App):

Chaque application Toga possède une seule instance toga.App, représentant l’entité en cours d’exécution, c’est-à-dire notre application. L’application peut gérer plusieurs fenêtres, mais pour les applications simples, il n’y aura qu’une seule fenêtre principale.

Ensuite, nous définissons une méthode de démarrage startup() :

def startup(self):
    main_box = toga.Box()

La première chose que fait la méthode de démarrage est de définir une boîte principale. Le schéma de présentation de Toga est similaire à celui du HTML. Vous construisez une application en construisant une collection de boîtes, chacune d’entre elles contenant d’autres boîtes, ou de véritables widgets. Vous appliquez ensuite des styles à ces boîtes pour définir la manière dont elles utiliseront l’espace disponible dans la fenêtre.

Dans cette application, nous définissons une seule boîte, mais nous n’y mettons rien.

Ensuite, nous définissons une fenêtre dans laquelle nous pouvons placer cette boîte vide :

self.main_window = toga.MainWindow(title=self.formal_name)

Cela crée une instance de toga.MainWindow, qui aura un titre correspondant au nom de l’application. Une fenêtre principale est un type spécial de fenêtre dans Toga - c’est une fenêtre qui est étroitement liée au cycle de vie de l’application. Lorsque la fenêtre principale est fermée, l’application se termine. La fenêtre principale est également la fenêtre qui contient le menu de l’application (si vous êtes sur une plateforme comme Windows où les barres de menu font partie des fenêtres).

Nous ajoutons ensuite notre boîte vide au contenu de la fenêtre principale et demandons à l’application d’afficher notre fenêtre :

self.main_window.content = main_box
self.main_window.show()

Enfin, nous définissons une méthode main(). C’est elle qui crée l’instance de notre application :

def main():
    return HelloWorld()

Cette méthode main() est celle qui est importée et invoquée par main__.py. Elle crée et retourne une instance de notre application HelloWorld.

C’est l’application Toga la plus simple possible. Bous allons ajouter notre propre contenu à l’application, et faire en sorte qu’elle fasse quelque chose de plus intéressant.

Ajouter notre propre contenu

Modifiez votre classe HelloWorld dans src/helloworld/app.py pour qu’elle ressemble à ceci :

class HelloWorld(toga.App):
    def startup(self):
        main_box = toga.Box(style=Pack(direction=COLUMN))

        name_label = toga.Label(
            "Your name: ",
            style=Pack(padding=(0, 5)),
        )
        self.name_input = toga.TextInput(style=Pack(flex=1))

        name_box = toga.Box(style=Pack(direction=ROW, padding=5))
        name_box.add(name_label)
        name_box.add(self.name_input)

        button = toga.Button(
            "Say Hello!",
            on_press=self.say_hello,
            style=Pack(padding=5),
        )

        main_box.add(name_box)
        main_box.add(button)

        self.main_window = toga.MainWindow(title=self.formal_name)
        self.main_window.content = main_box
        self.main_window.show()

    def say_hello(self, widget):
        print(f"Hello, {self.name_input.value}")

Note

Ne supprimez pas les imports en haut du fichier, ni le main() en bas. Vous n’avez besoin de mettre à jour que la classe HelloWorld.

Examinons en détail ce qui a changé.

Nous sommes toujours en train de créer une boîte principale, mais nous appliquons maintenant un style :

main_box = toga.Box(style=Pack(direction=COLUMN))

Le système de mise en page intégré à Toga s’appelle « Pack ». Il se comporte en grande partie comme CSS. Vous définissez des objets dans une hiérarchie - en HTML, les objets sont <div>, <span>, et d’autres éléments DOM ; dans Toga, ce sont des widgets et des boîtes. Vous pouvez ensuite attribuer des styles aux différents éléments. Dans ce cas, nous indiquons qu’il s’agit d’une boîte COLUMN - c’est-à-dire qu’il s’agit d’une boîte qui utilisera toute la largeur disponible, et qui augmentera sa hauteur au fur et à mesure que du contenu sera ajouté, mais qui essaiera d’être aussi courte que possible.

Ensuite, nous définissons quelques widgets :

name_label = toga.Label(
    "Your name: ",
    style=Pack(padding=(0, 5)),
)
self.name_input = toga.TextInput(style=Pack(flex=1))

Ici, nous définissons un Label (widget de texte) et un TextInput (widget de saisie de texte). Les deux widgets sont associés à des styles ; le Label aura un espace de 5 px à gauche et à droite, et aucun espace en haut et en bas. Le TextInput est marqué comme étant flexible, c’est-à-dire qu’il absorbera tout l’espace disponible dans son axe de mise en page.

Le TextInput est assigné en tant que variable d’instance d classe. Cela nous permet d’accéder facilement à l’instance du widget, ce que nous utiliserons dans un instant.

Ensuite, nous définissons une boîte pour contenir ces deux widgets :

name_box = toga.Box(style=Pack(direction=ROW, padding=5))
name_box.add(name_label)
name_box.add(self.name_input)

La name_box est une boîte comme la boîte principale, mais cette fois, c’est une boîte ROW (rangée). Cela signifie que le contenu sera ajouté horizontalement, et qu’il essaiera d’être le moins large possible. La boîte a également un peu de padding (espace) - 5px sur tous les côtés.

Nous définissons maintenant un bouton :

button = toga.Button(
    "Say Hello!",
    on_press=self.say_hello,
    style=Pack(padding=5),
)

Le bouton est également doté d’un espace de 5 px sur tous les côtés. Nous définissons également un handler - une méthode à invoquer lorsque le bouton est pressé.

Ensuite, nous ajoutons la boîte de nom et le bouton à la boîte principale :

main_box.add(name_box)
main_box.add(button)

Le reste de la méthode de démarrage est identique à la précédente : définition d’une fenêtre principale et attribution de la boîte principale en tant que contenu de la fenêtre :

self.main_window = toga.MainWindow(title=self.formal_name)
self.main_window.content = main_box
self.main_window.show()

Notre dernière action est de définir le gestionnaire du bouton. Un gestionnaire peut être n’importe quelle méthode, générateur ou co-routine asynchrone ; il accepte le widget qui a généré l’événement comme argument, et sera invoqué chaque fois que le bouton est pressé :

def say_hello(self, widget):
    print(f"Hello, {self.name_input.value}")

Le corps de la méthode est une simple instruction d’affichage (print). Cependant, elle interroge la valeur actuelle de l’entrée name et utilise ce contenu comme texte à imprimer.

Maintenant que nous avons effectué ces changements, nous pouvons voir à quoi ils ressemblent en relançant l’application. Comme précédemment, nous utiliserons le mode développeur :

(beeware-venv) $ briefcase dev

[helloworld] Starting in dev mode...
===========================================================================

Vous remarquerez que cette fois-ci, briefcase n’installe pas de dépendances. Briefcase peut détecter que l’application a déjà été exécutée auparavant, et pour gagner du temps, il n’exécutera que l’application. Si vous ajoutez de nouvelles dépendances à votre application, vous pouvez vous assurer qu’elles soient installées en passant une option -r lorsque vous lancez briefcase dev.

Cela devrait ouvrir une fenêtre GUI :

Fenêtre Hello World Tutorial 2, sur macOS

Si vous saisissez un nom dans la zone de texte et que vous appuyez sur le bouton GUI, vous devriez voir apparaître une sortie dans la console où vous avez démarré l’application.

Étapes suivantes

Nous avons maintenant une application qui fait quelque chose d’un peu plus intéressant. Mais elle ne fonctionne que sur notre propre ordinateur. Allons empaqueter cette application pour la distribuer. Dans Tutoriel 3, nous allons empaqueter notre application sous la forme d’un programme d’installation autonome que nous pourrons envoyer à un ami, un client, ou télécharger sur un App Store.