Tutorial 2 - Es wird interessant#

In Tutorial 1 haben wir ein lauffähiges Rumpfprojekt erstellt, aber eigenen Code haben wir nicht geschrieben. Schauen wir uns an, was für uns generiert wurde.

Was generiert wurde#

In dem Verzeichnis src/helloworld sollten 3 Dateien zu sehen sein: __init__.py, __main__.py und app.py.

__init__.py markiert das helloworld-Verzeichnis als importierbares Python-Modul. Es ist eine leere Datei; allein die Tatsache, dass sie existiert, sagt dem Python-Interpreter, dass das helloworld-Verzeichnis ein Modul definiert.

__main__.py markiert das helloworld Modul als eine besondere Art von Modul - ein ausführbares Modul. Wenn Sie versuchen, das Modul helloworld mit python -m helloworld zu starten, ist die Datei __main__.py der Ort, an dem Python mit der Ausführung beginnt. Der Inhalt von __main__.py ist relativ einfach:

from helloworld.app import main

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

Das heißt, es importiert die Methode main aus der Anwendung helloworld; und wenn es als Einstiegspunkt ausgeführt wird, ruft es die Methode main() auf und startet die Hauptschleife der Anwendung. Die Hauptschleife ist die Art und Weise, wie eine GUI-Anwendung auf Benutzereingaben (wie Mausklicks und Tastatureingaben) wartet.

Die interessantere Datei ist app.py - diese enthält die Logik, die unser Anwendungsfenster erstellt:

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()

Gehen wir diese Zeile für Zeile durch:

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

Als erstes importieren wir das toga-Widget-Toolkit, sowie einige stilbezogene Utility-Klassen und Konstanten. Unser Code verwendet diese noch nicht - aber wir werden sie in Kürze nutzen.

Definieren wir eine Klasse:

class HelloWorld(toga.App):

Jede Toga-Anwendung hat eine einzige toga.App-Instanz, die laufende Entität, die Anwendung, darstellt. Die Anwendung kann mehrere Fenster verwalten, aber für einfache Anwendungen gibt es ein einziges Hauptfenster.

Als nächstes definieren wir eine Startup() Methode:

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

Das erste, was die Startup-Methode macht, ist die Definition eines Hauptfeldes. Das Layout-Schema von Toga verhält sich ähnlich wie HTML. Sie bauen eine Anwendung auf, indem Sie eine Sammlung von Boxen konstruieren, von denen jede andere Boxen oder Widgets enthält. Sie wenden dann Stile auf diese Boxen an, um zu definieren, wie sie den verfügbaren Platz im Fenster nutzen werden.

In dieser Anwendung definieren wir ein einzelnes Feld, in das wir jedoch nichts einfügen.

Als Nächstes definieren wir ein Fenster, in das wir dieses leere Feld einfügen können:

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

Dies erzeugt eine Instanz eines toga.MainWindow, das einen Titel hat, der dem Namen der Anwendung entspricht. Ein Hauptfenster ist eine besondere Art von Fenster in Toga - es ist ein Fenster, das eng an den Lebenszyklus der Anwendung gebunden ist. Wenn das Hauptfenster geschlossen wird, wird die Anwendung beendet. Das Hauptfenster ist auch das Fenster, in dem sich das Menü der Anwendung befindet (wenn Sie auf einer Plattform wie Windows arbeiten, wo die Menüleisten Teil des Fensters sind)

Dann fügen wir unsere leere Box als Inhalt des Hauptfensters hinzu und weisen die Anwendung an, unser Fenster anzuzeigen:

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

Als letztes definieren wir eine main() Methode. Diese erzeugt die Instanz unserer Anwendung:

def main():
    return HelloWorld()

Diese main() Methode ist diejenige, die von __main__.py importiert und aufgerufen wird. Sie erzeugt eine Instanz unserer HelloWorld-Anwendung und gibt diese zurück.

Das ist die einfachste mögliche Toga-Anwendung. Lassen Sie uns einige unserer eigenen Inhalte in die Anwendung einfügen und die Anwendung etwas Interessantes tun lassen.

Hinzufügen von eigenen Inhalten#

Ändern Sie Ihre HelloWorld Klasse in src/helloworld/app.py so, dass sie wie folgt aussieht:

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}")

Bemerkung

Entfernen Sie nicht die Importe am Anfang der Datei oder main() am Ende. Sie müssen nur die Klasse HelloWorld aktualisieren.

Schauen wir uns im Detail an, was sich geändert hat.

Wir erstellen immer noch ein Hauptfeld, aber wir wenden jetzt einen Stil an:

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

Das in Toga eingebaute Layoutsystem heißt „Pack“. Es verhält sich sehr ähnlich wie CSS. Sie definieren Objekte in einer Hierarchie - in HTML sind die Objekte <div>, <span> und andere DOM-Elemente; in Toga sind es Widgets und Boxen. Sie können dann den einzelnen Elementen Stile zuweisen. In diesem Fall geben wir an, dass es sich um einen COLUMN-Kasten handelt - das heißt, es handelt sich um einen Kasten, der die gesamte verfügbare Breite beansprucht und sich in der Höhe vergrößert, wenn Inhalt hinzugefügt wird, aber er versucht, so kurz wie möglich zu sein.

Als nächstes definieren wir ein paar Widgets:

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

Hier definieren wir ein Label und einen TextInput. Beiden Widgets sind Stile zugeordnet; das Label hat links und rechts ein Padding von 5px und oben und unten kein Padding. Der TextInput ist als flexibel gekennzeichnet, d. h. er nimmt den gesamten verfügbaren Platz in seiner Layoutachse ein.

Der TextInput wird als Instanzvariable der Klasse zugewiesen. Dies ermöglicht uns einen einfachen Zugriff auf die Instanz des Widgets - etwas, das wir gleich verwenden werden.

Als nächstes definieren wir eine Box, die diese beiden Widgets enthält:

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

Die name_box ist eine Box genau wie die Hauptbox, aber dieses Mal ist es eine ROW-Box. Das bedeutet, daß der Inhalt horizontal eingefügt wird, und daß versucht wird, die Breite so schmal wie möglich zu machen. Die Box hat auch etwas Padding - 5px auf allen Seiten.

Jetzt definieren wir eine Schaltfläche:

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

Die Schaltfläche hat auch 5px Polsterung auf allen Seiten. Wir definieren auch einen Handler - eine Methode, die aufgerufen wird, wenn die Schaltfläche gedrückt wird.

Dann fügen wir das Namensfeld und die Schaltfläche zum Hauptfeld hinzu:

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

Damit ist unser Layout fertiggestellt; der Rest der Startup-Methode ist wie zuvor - Definition eines MainWindow und Zuweisung des Hauptfeldes als Inhalt des Fensters:

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

Als Letztes müssen wir den Handler für die Schaltfläche definieren. Ein Handler kann eine beliebige Methode, ein Generator oder eine asynchrone Co-Routine sein; er akzeptiert das Widget, das Ereignis erzeugt hat, als Argument und wird immer dann aufgerufen, wenn die Schaltfläche gedrückt wird:

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

Der Hauptteil der Methode ist eine einfache Druckanweisung - sie fragt jedoch den aktuellen Wert der Namenseingabe ab und verwendet diesen Inhalt als den Text, der gedruckt wird.

Nachdem wir nun diese Änderungen vorgenommen haben, können wir sehen, wie sie aussehen, indem wir die Anwendung erneut starten. Wie zuvor werden wir den Entwicklermodus verwenden:

(beeware-venv) $ briefcase dev

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

Sie werden feststellen, dass dieses Mal keine Abhängigkeiten installiert werden. Briefcase kann erkennen, dass die Anwendung schon einmal ausgeführt wurde, und um Zeit zu sparen, wird nur die Anwendung ausgeführt. Wenn Sie neue Abhängigkeiten zu Ihrer Anwendung hinzufügen, können Sie sicherstellen, dass diese installiert werden, indem Sie die Option -r beim Aufruf von briefcase dev mitgeben.

Dies sollte ein GUI-Fenster öffnen:

Hello World Tutorial 2 Fenster, auf macOS

Wenn Sie einen Namen in das Textfeld eingeben und die Schaltfläche GUI drücken, sollte die Ausgabe in der Konsole erscheinen, in der Sie die Anwendung gestartet haben.

Nächste Schritte#

Wir haben jetzt eine Anwendung, die etwas Interessanteres macht. Aber sie läuft nur auf unserem eigenen Computer. Lassen Sie uns diese Anwendung für die Verteilung verpacken. In Tutorial 3 werden wir unsere Anwendung als eigenständiges Installationsprogramm verpacken, das wir an einen Freund oder Kunden schicken oder in einen App Store hochladen können.