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:
#!/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:
#!/usr/bin/env python3
import kivy
from kivy.app import App
class HalloApp(App):
pass
if __name__ == '__main__':
HalloApp().run()
hallo.kv
steht:
Button:
text: 'Hallo Welt'
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
#!/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()
<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:
#!/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:
<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:
#!/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:
<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.