Zum Inhalt

Einstieg in Kivy (GUI)

Es gibt mehrere Möglichkeiten, für dein Python-Programm eine graphische Oberfläche (GUI - Graphical User Interface) zu programmieren. Im Folgenden beschreibe ich Kivy, wessen Apps auf allen gängigen Betriebssystemen und Handys laufen können. Auf Android gibt's den Kivy-Launcher, mit dem man die Dateien direkt auf dem Handy ausführen kann. Und in den Kivy-Downloads findet man eine VirtualBox-Maschine, auf der Buildozer vorinstalliert ist, um damit apk-Dateien zu erstellen.

Hallo-Welt

Das Hallo-Welt-Programm auf der Kivy-Homepage sieht so aus:

1
2
3
4
5
6
7
8
9
#!/usr/bin/env python3
from kivy.app import App
from kivy.uix.button import Button

class TestApp(App):
    def build(self):
        return Button(text='Hallo Welt')

TestApp().run()

Wie du von der Programmierung von Internetseiten bereits weißt, hat es Vorteile, den Inhalt von der Form zu trennen. Dem HTML-Code entspricht hier der Python-Code in einer Datei der Form *.py, und dem CSS-Code der Kivy-Code in einer Datei der Form *.kv.

Das Hallo-Welt-Programm sieht dann so aus: In der py-Datei steht:

1
2
3
4
5
6
7
8
9
#!/usr/bin/env python3
import kivy
from kivy.app import App

class HalloApp(App):
    pass

if __name__ == '__main__':
    HalloApp().run()
Und in der Datei hallo.kv steht:

1
2
Button:
    text: 'Hallo Welt'
Damit das funktioniert, muss der Dateiname der kv-Datei so heißen wie das Wort vor "App" in der py-Datei.

Widgets und ihr Layout

Will man in einem Fenster etwas erscheinen lassen, spricht man davon, ein Widget hinzuzufügen. Jedes Programm hat ein root widget, dem du Kinder-Widgets hinzufügen kannst. Die Zugehörigkeiten der Widgets zueinander kann man in einem hierarchischen Baumdiagramm aufzeichnen. Abhängig davon wie die Kinder-Widgets angeordnet sein sollen, entscheidest du dich für ein Layout (1, 2). Im Folgenden wird das BoxLayout verwendet, damit alle Widgets schön untereinander sind. Im KivyCatalog (1 , 2) kann man die verschiedenen Layouts ausprobieren.

Das Hallo-Welt-Programm kann dann so aussehen:

kivy_hallo_welt_3.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#!/usr/bin/env python3
import kivy
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout

class WurzelWidget(BoxLayout):
    pass

class HalloWidgetApp(App):
    def build(self):
        return WurzelWidget()

if __name__ == '__main__':
    HalloWidgetApp().run()
hallowidget.kv
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<WurzelWidget>:
    # Mit canvas kann man einen Hintergrund zeichnen
    canvas.before:
        Color:
            rgba: 1, 1, 0.5,0.5
        Rectangle:
            pos: self.pos
            size: self.size
    BoxLayout:
        Label:
            text: 'Hallo Welt!'
            font_size: '24sp'
            color: 1,1,0,0.5

Übergabe von Objekten zwischen Python- und Kivy-Code

Um eine Aktion des Anwenders deines Programms in deinem Python-Code verarbeiten zu können, muss dein Python-Code auf die Widgets in deinem Kivy-Code zugreifen können. Wenn der Anwender z.B. auf einen Button klickt, soll der Python-Code etwas tun.

Zugriff auf Text kann man auf mehreren Wegen kriegen. Um einer Funktion funktion(self, etwas) etwas aus einem Widget zu übergeben, verwendet man bei einem Event root.funktion(etwas). Will man wiederum einen damit kreierten Output in einem Widget anzeigen, muss man entweder eine StringProperty() definieren oder den Output dem Widget über seine id zuweisen. Aus dem folgenden Beispiel kannst du beide Methoden ablesen. (Du solltest allerdings nicht beide Methoden vermischen, sonst funktioniert das was du willst vielleicht nur bei der ersten Ausführung, da die Variable mit einem konkreten Wert überschrieben wird.)

kivy_widget-access.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/env python3
import kivy
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty

class Login(BoxLayout):
    outpt = StringProperty('Fülle bitte beide Felder aus.') # 1. Weg: Um durch access_widget_1 den text des Labels zu ändern, wird hier eine Variable definiert, auf die der Python- und Kivy-Code dieser Klasse ("Plugin") Zugriff haben.

    def access_widget_1(self,inpt_name,inpt_pwd):
        print('Dein Name ist: {x}'.format(x = inpt_name)) # Erste Möglichkeit etwas Übergebenes im Terminal auszugeben.
        print('Dein Passwort ist: ' + inpt_pwd) # Zweite Möglichkeit etwas Übergebenes im Terminal auszugeben.
        self.outpt = 'Dein Name ist: ' + inpt_name

    def access_widget_2(self,inpt_pwd):
        self.ids["ausgabelabel"].text = 'Dein Passwort ist: ' + inpt_pwd # 2. Weg: Wenn man im Kivy-Code dem Label eine id gegeben hat, kann man auf sie zugreifen. Dieser Weg ist praktisch, aber als "best practice" wird der erste Weg angesehen. Der Vorteil ist hier, dass man im Kivy-Code sehen kann, was im Label-Text ausgegeben wird.

class WidgetAccessApp(App):
    def build(self):
        return Login()

if __name__ == '__main__':
    WidgetAccessApp().run()

widgetaccess.kv:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<Login>:
    BoxLayout:
        orientation: 'vertical'
        padding: 100
        spacing: 20

        BoxLayout:
            orientation: 'horizontal'
            Label:
                text: 'Name:'
            TextInput:
                id: inpt_name
                text: ''
                focus: True
                multiline: False

        BoxLayout:
            orientation: 'horizontal'
            Label:
                text: 'Passwort:'
            TextInput:
                id: inpt_pwd
                text: ''
                password: True
                multiline: False

        Button:
            text: "Los!"
            size_hint: 1, 3
            background_color:  .5, .6, .8, 1
            on_press: root.access_widget_1(inpt_name.text,inpt_pwd.text) # So werden drei Dinge übergeben: Die eigene Instanz und zwei Texte.
            on_release: root.access_widget_2(inpt_pwd.text)

        Label:
            id: ausgabelabel # Um durch access_widget_2 den text dieses Labels ändern zu können.
            text: root.outpt # Wenn im Python-Code festgelegt wurde, dass diese Variable im Besitz ("Property") der Widget ("Login") ist, kann ihr Wert hier angezeigt werden.
            size_hint: 1, 3
            bold: True
            color: .8, .8, 0, 1

Bemerkungen zu self, root und app: Verwendet man eine Variable, muss man sagen, wo sie verwaltet wird. Das outpt und die Widget-IDs sind im Mutter-Widget Login definiert. Will man in der Funktion access_widget_1 darauf zugreifen, muss man das mit self tun, weil das aktuelle Widget beim Ausführen der Funktion das Widget Login ist. Im Kivy-Code muss man hingegen mit root darauf zugreifen, weil self das Label-Widget wäre und man auf die erste Instanz in der Hierarchie schauen muss. Definiert man in der App-Klasse (hier WidgetAccessApp(App)) etwas, auf das man im Kivy-Code zugreifen will (z.B. def funktion(self)), dann macht man das mit app.funktion(). Mehr zu Werten von Properties.

Um auf andere Dinge als Text zuzugreifen, z.B. auf den Zustand eines Buttons, ob er gerade gedrückt ist oder nicht, verwendet man andere Properties wie ObjectProperty, ListProperty oder RecycleView. Mehr zu Properties

Aufgaben

EA: BMI-Rechner

Erweitere deinen BMI-Rechner zu einer App mit graphischer Oberfläche.

EA: Verschlüsselung

Informiere dich auf cryptool-online über Techniken, einen Text zu verschlüsseln und programmiere dein eigenes Verschlüsselungsprogramm. Deine Grundstruktur könnte wie folgt aussehen.

verschluesselung.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/env python3
import kivy
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
# from kivy.core.window import Window

class Verschluesselung(BoxLayout):
    ausgabe = StringProperty('Es ist noch kein Text zum Übersitzen eingegeben.')

    def verschluessle(self,eingabe):
        self.ausgabe = 'Dein Text ist:\n' + eingabe + '\nRichtig?!'

class VerschluesselungsappApp(App):
    def build(self):
        kivy.Config.set('graphics', 'width',  900)
        kivy.Config.set('graphics', 'height', 600)
        # Window.fullscreen = True
        return Verschluesselung()

if __name__ == '__main__':
    VerschluesselungsappApp().run()

verschluesselungsapp.kv:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<Verschluesselung>:
    BoxLayout:
        orientation: 'vertical'
        padding: 100
        spacing: 20

        Label:
            text: 'Hier kann ein Klartext eingegeben werden'
            font_size: '20sp'

        TextInput:
            id: eingabe
            text: ''
            focus: True

        Button:
            text: "Jetzt verschlüsseln"
            # size_hint: 1, 3
            background_color:  .5, .6, .8, 1
            on_press: root.verschluessle(eingabe.text)

        Label:
            text: root.ausgabe
            bold: True
            color: .8, .8, 0, 1

EA: Vertiefung

Um eine App mit mehreren Fenstern zu programmieren, bietet sich der Screen Manager an.

Weitere Möglichkeiten von Python und Kivy erfährst du, wenn du die Examples studierst.

Hilfe kannst du dir bei stackoverflow.com holen.

Willst du Module/Packages verwenden, die im Kivy Launcher nicht vorhanden sind, verwendest du am besten QPython und installierst damit über pip dein Module.