Aus Linux-Magazin 10/2023

Apps in Python programmieren

© Andrew Poplavsky / 123RF.com

Mit dem Python-Framework Kivy lassen sich auf einfache Weise plattformübergreifende Apps programmieren. Wie das geht, erklärt dieser Artikel.

Möchten Sie Apps entwickeln, die unter Linux, Windows, MacOS, Android und iOS laufen sollen, können Sie ab sofort darauf verzichten, mehrere Code-Varianten zu entwerfen oder gar verschiedene Programmiersprachen zu bemühen. Wer sich mit der populären Programmiersprache Python auskennt, nutzt dazu schlicht das Framework Kivy [1]. Eine aktive Communtiy entwickelt das Open-Source-Framework der MIT-Lizenz weiter. Im Zusammenspiel mit der Bibliothek KivyMD [2] und dem Buildtool Buildozer [3] bauen Sie damit plattformunabhängig schnell ansprechende grafische Apps.

Vorbereitungen

In der aktuellen Version 2.2.1 unterstützt Kivy offiziell die Python-Versionen 3.7 bis 3.11. Das Framework lässt sich unter Linux im Terminal bequem mithilfe des Paketmanagers Pip aufspielen. Falls noch nicht geschehen, müssen Sie dazu allerdings zuerst Pip installieren. Unter Ubuntu und dessen Abkömmlingen gelingt das mit dem Kommando »sudo apt install python3-pip«. Sobald Sie Python und Pip eingerichtet haben, kommt für Ubuntu und dessen Derivate anschließend das Modul Venv an die Reihe: »sudo apt install -y python3-venv«. Es dient dazu, eine virtuelle Python-Umgebung einzurichten, die zunächst Kivy und später auch Buildozer nutzen.

Nachdem sie Venv installiert haben, wechseln Sie ins gewünschte Projektverzeichnis und legen mit dem Befehl aus den ersten beiden Zeilen von Listing 1 eine solche virtuelle Umgebung namens »kivy_venv« an und aktivieren sie. Dann bringen Sie Pip und die Setup-Tools auf den aktuellen Stand (Zeile 3). Anschließend installieren Sie Kivy auf dem System, einschließlich Audio/Video-Support und diverser Demo-Beispiele (Zeile 4).

Im nächsten Schritt sehen Sie sich die mitgelieferten Demos an, darunter eine Vorführung der verschiedenen Kivy-Widgets (Zeile 5) und einen dreidimensional gerenderten Affen (Zeile 6). Mit dem Kommando »deactivate« verlassen Sie bei Bedarf die virtuelle Umgebung wieder.

Listing 1

Installation

$ python3 -m venv kivy_venv
$ source kivy_venv/bin/activate
$ python3 -m pip install --upgrade pip setuptools
$ python3 -m pip install "kivy[base,media]" kivy_examples
$ python kivy_venv/share/kivy-examples /demo/showcase/main.py
$ python kivy_venv/share/kivy-examples /3Drendering/main.py

Hallo-Welt-Beispiel

Um ein einfaches Fenster mit einem Text abzubilden, genügen wenige Codezeilen, wie Listing 2 veranschaulicht.

Listing 2

kivy-hello-world.py

from kivy.app import App
from kivy.uix.label import Label
class HelloWorldApp(App):
  def build(self):
    return Label(text="Hallo Welt", font_size=30)
app = HelloWorldApp(title="Hallo Welt")
app.run()

Der Code landet in einer Datei, die zum Beispiel »kivy-hello-world.py« heißen könnte. Im Terminal führen Sie diese Datei mit »python3 kivy-hello-world.py« in der zuvor erstellten virtuellen Python-Umgebung »kivy_venv« aus. Abbildung 1 zeigt das Ergebnis. Die ersten beiden Codezeilen importieren die beiden Klassen »App« und »Label« aus den Packages »kivy.app« und »kivy.uix.label«. In Zeile 4 definieren Sie die Klasse »HelloWorldApp«, die von der Klasse »kivy.app.App« die Funktionen erbt.

Möchten Sie Kivy Widgets im Applikationsfenster positionieren, müssen Sie die Methode »build()« in der Klasse »HelloWorldApp« überschreiben (Zeilen 3 und 4). Diese gibt in diesem Fall lediglich ein Objekt der Klasse »Label« mit dem Text “Hallo Welt” zurück. Dessen zweiter Parameter »font_size« legt die Schriftgröße fest. Kivy liefert mit der KV-Sprache ein praktisches Datenformat aus, mit dem Sie Widgets einfach im Fenster positionieren und die Logik von der GUI-Darstellung trennen können. Später dazu mehr. Zuletzt wird in Zeile 8 von Listing 2 ein Objekt »app« von der Klasse »HelloWorldApp« instanziiert und mit »app.run()« ausgeführt. Der Parameter »title« der Klasse bestimmt den Titel des Applikationsfensters.

Abbildung 1: So sieht das Fenster des Hallo-Welt-Beispiels am Ende aus.

Abbildung 1: So sieht das Fenster des Hallo-Welt-Beispiels am Ende aus.

Ein MP3-Player

Um die Konzepte von Kivy zu verdeutlichen, soll ein einfacher MP3-Player als Beispiel dienen. Er lässt sich mit Kivy und der eng verbundenen Bibliothek KivyMD umsetzen. Letztere stellt dazu verschiedene Widgets im Material Design von Google zur Verfügung. Zunächst legen Sie erneut ein Projektverzeichnis an, etwa »Mp3Player«, erstellen eine virtuelle Umgebung namens »kivy_venv« und aktivieren sie, wie bereits beschrieben. Danach erzeugen Sie eine Datei »main.py« (Listing 3).

In den ersten neun Zeilen der Datei »main.py« binden Sie verschiedene Klassen aus den Packages »jnius, kivy, kivymd« und die Python-Bibliothek TinyTag in die Applikation ein. Die Bibliothek Pyjnius [4] ist ebenfalls ein Projekt der Kivy-Entwickler und notwendig, um Java-Klassen in Python verfügbar zu machen (Reflection). In dieser Beispiel-App nutzen Sie das dazu, um unter Android den Media Player aus dem Java-Package »android.media« zu importieren, der sich um das Abspielen von MP3-Dateien unter diesem Betriebssystem kümmert. Um diesen Schritt kommen Sie kaum herum, da der in Kivy integrierte SoundLoader unter Android noch etwas hakt.

Da Pyjnius das Java Development Kit (JDK) verlangt, müssen Sie es gegebenenfalls zuerst installieren. Für Ubuntu und Derivate funktioniert das mithilfe des Befehls »sudo apt install openjdk-17-jdk«. Das JDK brauchen Sie außerdem später für das Tool Buildozer. Mittels der Python-Library TinyTag [5] lassen sich die Metadaten von MP3-Dateien auslesen, um sie in der grafischen Oberfläche der Kivy-App anzeigen zu lassen.

Installieren Sie Kivy in der virtuellen Umgebung (Listing 1, Zeile 4). Um die anderen Packages einzurichten, führen Sie das folgende Kommando aus:

$ pip install pyjnius kivymd tinytag androidstorage4kivy

Zeile 11 von Listing 3 ermittelt, ob die App unter Android ausgeführt wird. Trifft das zu, werden benötigte Leserechte angefordert. Zusätzlich importiert das Listing aus dem Package »androidstorage4kivy« [6] die Klassen »Chooser« und »SharedStorage«, die notwendig sind, um Dateien unter Android auswählen zu können. Der FileChooser von Kivy funktioniert nicht mit allen Versionen von Android. Mit »pip list« überprüfen Sie die Version der installierten Packages.

Die Klasse »Mp3Player« definieren Sie in Zeile 17, die von der Basisklasse »BoxLayout« abgeleitet ist. Damit bildet das BoxLayout von Kivy die Basis für unsere »PlayerApp« in Zeile 109. Das ist erforderlich, um die Widgets der grafischen Oberfläche in diesem Layout anordnen zu können. Beim BoxLayout erscheinen alle Elemente der Oberfläche entweder nebeneinander oder untereinander angeordnet.

Der Konstruktor der Klasse »Mp3Player« in Zeile 18 von Listing 3 übernimmt die Initialisierung verschiedener Variablen. Die Variable »volume_val« regelt die Lautstärke des MP3-Players, im Beispiel liegt der Ausgangswert bei 25. Auch wird hier die später notwendige Variable »mPlayer« mit »None« initialisiert. Die Initialisierung der Variable »mp3_file«, die später den Pfad zur ausgewählten MP3-Datei speichert, geschieht ebenfalls in diesem Konstruktor.

Wie erwähnt; dient die Klasse »Chooser« aus dem Package »androidstorage4kivy« in Zeile 24 von Listing 3 zur Auswahl einer MP3-Datei unter Android. Wird die App nicht unter Android ausgeführt, kommt die Klasse »FileChooserListView« von Kivy zum Einsatz. Darüber hinaus wird in diesem Fall noch im Konstruktor ein Kivy-Popup erzeugt, das Sie später zur Auswahl einer MP3-Datei brauchen.

Die nächsten beiden Methoden »load_mp3_android« und »load_mp3« sorgen dafür, dass eine MP3-Datei in den Speicher geladen und wiedergegeben wird. Sie fallen verschieden aus, weil sich das Laden einer MP3-Datei unter Android mit dem erwähnten MediaPlayer im Vergleich mit dem SoundLoader von Kivy unter Linux respektive MacOS und Windows unterscheidet.

In der Methode »load_mp3_android« wird zuerst im Zusammenhang mit der Klasse »Chooser« aus Zeile 24 von Listing 3 eine MP3-Datei ausgewählt. Die Klasse nutzt das native Android Chooser UI und nimmt an dieser Stelle als Parameter die Callback-Methode »load_mp3_android« auf. Dieser Methode wird dadurch die Liste »shared_file_list« als Parameter zurückgegeben, in der sich der Pfad der ausgewählten MP3-Datei befindet. Um an die gewählte MP3-Datei zu kommen, kopiert die Methode »copy_from_shared« der Klasse »SharedStorage« die MP3-Datei in den privaten Cache-Speicher der App. Dies geschieht in Zeile 31 von Listing 3 mittels einer List Comprehension. Damit verwandelt sich ein Shared File aus dem Shared Storage des Android-Geräts in ein Private File im Cache der App. Das braucht es, da ein Shared File nicht als reguläre Python-Referenz auf eine Datei gilt.

Ab Android-Version 10 ist der Shared Storage des Speichers von Android eine Datenbank und kein Dateisystem mehr. Deshalb funktioniert der FileChooser von Kivy nur bis zur Version 9 des Betriebssystems. Die erzeugte Liste »private_files« aus Zeile 31 von Listing 3 enthält als erstes Element den Pfad zur gewählten MP3-Datei, die in der nächsten Zeile der Variablen »mp3_file« zugewiesen wird.

Die Zeilen 33 bis 36 von Listing 3 prüfen, ob eine MP3-Datei bereits geladen wurde. Falls ja, ermittelt die Methode »isPlaying()«, ob gerade eine Wiedergabe stattfindet, und gegebenenfalls wird diese gestoppt. In der nächsten Zeile gibt schließlich die Anwendung mit »release()« die Ressourcen wieder frei, wenn bereits eine MP3-Datei in den Speicher geladen wurde.

Zur Wiedergabe der MP3-Datei unter Android binden Sie mit der Funktion »autoclass()« aus dem Package jnius in Zeile 37 von Listing 2 die Java-Klasse MediaPlayer in die Python-Applikation ein und weisen ein Objekt davon der Variablen »mPlayer« zu. Daraufhin lädt das Kommando »setDataSource(self.mp3_file)« die entsprechende MP3-Datei. Der Befehl »prepare()« bringt den Player in einen Zustand, in dem er Musik abspielen kann, was bei der Java-Klasse »MediaPlayer« notwendig ist. Letztlich stellen Sie mithilfe von »setVolume()« die aktuelle Lautstärke ein.

Listing 3

main.py

from jnius import autoclass
from kivy.core.audio import SoundLoader
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.filechooser import FileChooserListView
from kivy.uix.popup import Popup
from kivy.uix.slider import Slider
from kivy.utils import platform
from kivymd.app import MDApp
from tinytag import TinyTag
if platform == 'android':
  from android.permissions import request_permissions, Permission
  request_permissions([Permission.READ_EXTERNAL_STORAGE, Permission.READ_MEDIA_AUDIO])
  from androidstorage4kivy import Chooser, SharedStorage
class Mp3Player(BoxLayout):
  def __init__(self, **kwargs):
    super().__init__(**kwargs)
    self.volume_val = 25
    self.mPlayer = None
    self.mp3_file = None
    if platform == 'android':
      self.chooser = Chooser(self.load_mp3_android)
    else:
      self.file_chooser = FileChooserListView(filters=['*.mp3', '*.MP3'], on_submit=self.load_mp3)
      self.mp3_popup = Popup(title="MP3 wählen", content=self.file_chooser, size_hint=(0.9, 0.9))
  def load_mp3_android(self, shared_file_list):
    shared_storage = SharedStorage()
    self.private_files = [shared_storage.copy_from_shared(shared_file) for shared_file in shared_file_list]
    self.mp3_file = self.private_files[0]
    if self.mPlayer is not None:
      if self.mPlayer.isPlaying() is True:
        self.mPlayer.stop()
      self.mPlayer.release()
    MediaPlayer = autoclass('android.media.MediaPlayer')
    self.mPlayer = MediaPlayer()
    self.mPlayer.setDataSource(self.mp3_file)
    self.mPlayer.prepare()
    self.mPlayer.setVolume(self.volume_val / 100, self.volume_val / 100)
    self.show_mp3_info(self.mp3_file)
    self.play_music()
  def load_mp3(self, chooser, selection, *args):
    self.mp3_file = selection[0]
    if self.mPlayer is not None:
      if self.mPlayer.state == 'play':
        self.mPlayer.stop()
      self.mPlayer.unload()
    self.mPlayer = SoundLoader.load(self.mp3_file)
    self.mPlayer.volume = self.volume_val / 100
    self.show_mp3_info(self.mp3_file)
    self.mp3_popup.dismiss()
    self.play_music()
  def show_mp3_info(self, mp3_file):
    self.mp3_file = mp3_file
    meta_tags = TinyTag.get(self.mp3_file)
    self.ids.lblArtist.text = meta_tags.artist if meta_tags.artist else 'no artist'
    self.ids.lblSong.text = meta_tags.title if meta_tags.title else 'no title'
    self.ids.lblAlbum.text = meta_tags.album if meta_tags.album else 'no album'
  def open_volume_dialog(self):
    volume_slider = Slider(min=0, max=100, value=self.volume_val)
    volume_slider.bind(value=self.on_volume_change)
    volume_popup = Popup(title="Lautstärke", content=volume_slider, size_hint=(0.9, 0.9))
    volume_popup.open()
  def on_volume_change(self, instance, val):
    try:
      if platform == 'android':
        left = val / 100
        right = val / 100
        self.mPlayer.setVolume(float(left), float(right))
      else:
        self.mPlayer.volume = val / 100
      self.volume_val = val
    except AttributeError:
        self.volume_val = val
  def open_file_dialog(self):
    if platform == 'android':
      self.chooser.choose_content('audio/mpeg')
    else:
      self.mp3_popup.open()
  def play_music(self):
    try:
      if platform == 'android':
        self.mPlayer.start()
      else:
        self.mPlayer.play()
    except AttributeError:
        print('No mp3 file selected!')
  def stop_music(self):
    try:
      self.mPlayer.stop()
      if platform == 'android':
        self.mPlayer.prepare()
    except AttributeError:
      print('No mp3 file selected!')
  def close_app(self):
    app.stop()
class PlayerApp(MDApp):
  def build(self):
    self.title = 'mp3-Player'
    return Mp3Player()
if __name__ == '__main__':
  app = PlayerApp()
  app.run()

Metadaten visualisieren

Nachfolgend liest die Methode »show_mp3_info« die Metadaten der MP3-Datei aus, damit die grafische Oberfläche den Künstler, den Song-Titel und das Album anzeigt. Diese Methode finden Sie in Zeile 57 von Listing 3. Als Parameter nimmt sie den String mit dem Pfad zur entsprechenden MP3-Datei entgegen. Gemäß der Anweisung »TinyTag.get(self.mp3_file)« liest das Programm diese MP3-Datei ein. In den Zeilen 60 bis 62 folgen drei ternäre Operatoren, in denen jeweils die Attribute Artist, Title und Album der Metadaten auf Inhalt geprüft werden.

Sollten in der MP3-Datei keine Metadaten vorliegen, erscheinen die Strings »no artist«, »no title« oder »no album« in der GUI. Die Python-Datei »main.py« ist mit der grafischen Oberfläche verbunden, die später in der Datei »player.kv« definiert wird. Mittels der IDs der GUI-Widgets in der Datei »player.kv« greifen Sie auf diese zu. An dieser Stelle wird zum Beispiel mit »self.ids.lblArtist.text« (Zeile 60) der Text des MDLabels für den Künstler in der GUI gesetzt. Mehr dazu lesen Sie im Abschnitt KV Language. Zuletzt sorgt die Methode »play_music« (Zeile 88) für die Wiedergabe der MP3-Datei.

Möchten Sie die App nicht unter Android verwenden, kommt die Klasse »SoundLoader« von Kivy innerhalb der Methode »load_mp3« zum Einsatz. In dieser Konstellation haben Sie es etwas leichter: Sie laden lediglich mit »SoundLoader.load()« die MP3-Datei und justieren über die Methode »volume()« die Lautstärke. Der Wertebereich der Lautstärke liegt bei SoundLoader sowie bei MediaPlayer zwischen null für lautlos bis eins für volle Lautstärke. Deswegen müssen Sie den Wert des Lautstärkereglers durch 100 dividieren. In Zeile 47 bis 50 von Listing 3 prüft die Anwendung ebenfalls, ob schon eine MP3-Datei geladen wurde, unterbricht bei Bedarf die Wiedergabe und entfernt die MP3-Datei aus dem Speicher.

Innerhalb der Methode »open_volume_dialog« (Zeile 64) erzeugt die Klasse »Slider« aus der Kivy-Bibliothek einen Schieberegler zum Einstellen der Lautstärke. Die Parameter hier lauten »min« und »max«, »value« setzt den Ausgangswert. Die nächste Zeile koppelt mithilfe der Funktion »bind« den Slider mit der Methode »on_volume_change«. Immer wenn sich der Wert des Sliders ändert, wird diese Methode aufgerufen.

Die verbundene Callback-Funktion »on_volume_change« (Zeile 70) passt die Lautstärke an. Der dritte Parameter »val« enthält hier den aktuellen Wert der Lautstärke. Dabei wird wieder geprüft, ob der Player unter Android momentan läuft. Im positiven Fall werden das Objekt der schon erwähnten Java-Klasse »MediaPlayer« und die verbundene Methode »setVolume()« aufgerufen, um die Lautstärke zu ändern. Die Unterscheidung muss stattfinden, da die Methode des SoundLoaders zum Ändern der Lautstärke mit »volume()« benannt wurde, anstelle von »setVolume()« des MediaPlayer von Java. Das Ganze ist in einem »try/except«-Block gekapselt, um einen »AttributeError« aufzufangen, falls noch keine MP3-Datei geladen wurde. In diesem Fall ändert sich nur der Wert für die aktuelle Lautstärke.

Den erzeugten Schieberegler betten Sie mithilfe des Codes aus Zeile 67 von Listing 3 in ein Popup-Fenster von Kivy ein. Die Klasse »Popup« hat drei Parameter: Der erste Parameter gibt den Titel des Popup-Fensters an, der zweite »content« integriert den Slider ins Popup-Fenster, und »size_hint« legt die Größe des Popup-Fensters fest. Am Ende ruft die Methode »volume_popup.open()« das Popup auf (Abbildung 2).

Abbildung 2: Screenshot des Lautstärkereglers unter Linux.

Abbildung 2: Screenshot des Lautstärkereglers unter Linux.

In Zeile 82 von Listing 3 definieren Sie die Methode »open_file_dialog«, die einen FileChooser aufruft, um damit eine MP3-Datei auszuwählen (Abbildung 3, Abbildung 4). Der Code aus Zeile 84 ruft dafür die Methode »choose_content(‘audio/mpeg’)« der Klasse »Chooser« aus der Library »androidstorage4kivy« auf. Mit dem Argument »’audio/mpeg’« lassen Sie nur MP3-Dateien anzeigen.

Nutzen Sie die App nicht unter Android, verwenden Sie dazu die Klasse »FileChooserListView«. Dafür deklarieren Sie die Variable »mp3_popup« im Konstruktor der Klasse »Mp3Player« und weisen ihr ein Popup-Fenster zu. Mittels der Klasse »FileChooserListView« lassen sich beliebige Dateien auf dem Gerät auswählen, das die App ausgeführt. In Zeile 26 von Listing 2 wird ein Objekt der Klasse »FileChooserListView« instanziiert und in das Popup »mp3_popup« eingefügt. Da es bei der Beispielanwendung ausschließlich um Musikdateien geht, zeigen Sie mit dem Parameter »filters=[‘*.mp3’, ‘*.MP3’]« hier nur MP3-Dateien an.

Der zweite Parameter »on_submit« bestimmt, welche Methode auszuführen ist, wenn ein Nutzer doppelt auf eine Datei geklickt oder getippt hat. Hier kommt die Methode »load_mp3« aus Zeile 45 ins Spiel. Sie erhält eine Liste, die als erstes Element den Pfad zur ausgewählten Datei enthält (Parameter »selection«). Der Code aus Zeile 46 liest dieses Element aus und weist es der Variablen »mp3_file« zu. Wurde eine Datei ausgewählt, schließt sich der Dateiauswahldialog mit »self.mp3_popup.dismiss()« (Zeile 54), und die Methode »play_music« spielt die MP3-Datei ab.

Abbildung 3: Screenshot des Dateiauswahldialogs unter Linux.

Abbildung 3: Screenshot des Dateiauswahldialogs unter Linux.

Abbildung 4: Screenshot des Dateiauswahldialogs unter Android.

Abbildung 4: Screenshot des Dateiauswahldialogs unter Android.

Die Methode »play_music« in Zeile 88 von Listing 3 dient dazu, die ausgewählte MP3-Datei wiederzugeben, wenn der Anwender auf die Schaltfläche Play klickt oder tippt oder eine MP3-Datei mit der Schaltfläche Open auswählt. Die Methode prüft erneut, ob es sich um ein Android-Gerät handelt, da bei dem erwähnten MediaPlayer die Methode zum Abspielen der Datei »start()« heißt, aber beim von Kivy mitgelieferten SoundLoader mit »play« benannt ist. Auch hier fangen Sie mit »try/except« einen »AttributeError« auf, falls noch keine MP3 geladen wurde und es dementsprechend keine Variable »mPlayer« gibt. Zeile 97 zeigt die Methode »stop_music«, die das Abspielen der Musik stoppt. Verwenden Sie einen MediaPlayer, müssen Sie ihn zudem mit der Funktion »prepare« zurücksetzen, damit bei erneuter Wiedergabe der Song von vorne beginnt. Die Methode »close_app« (Zeile 105) schließt mit »app.stop()« die App.

In Zeile 109 definieren Sie eine Klasse »PlayerApp«, die von der Klasse »MDApp« aus der Bibliothek KivyMD erbt. Diese besitzt wieder eine überschriebene Methode »build«, in der Sie den Titel des App-Fensters festlegen und eine Instanz der Klasse »Mp3Player« zurückgeben. Zum Schluss erzeugen Sie ein Objekt »app« der Klasse »PlayerApp« und führen es mit »run()« aus. Mit circa 120 Zeilen Python-Code haben Sie damit einen rudimentären MP3-Player programmiert (Abbildung 5). Nun zu dessen grafischer Oberfläche (Abbildung 6).

Abbildung 5: Screenshot des MP3-Players unter Linux.

Abbildung 5: Screenshot des MP3-Players unter Linux.

Abbildung 6: Screenshot des MP3-Players unter Android.

Abbildung 6: Screenshot des MP3-Players unter Android.

KV Language

Bei umfangreicheren Projekten, die Sie mit Kivy erstellen, ergibt es Sinn, die Python-Logik von der grafischen Oberfläche zu trennen. Das macht den Code übersichtlicher. Zwar ließen sich GUI-Elemente wie Schaltflächen und Labels direkt in der Python-Datei programmieren, doch für fortgeschrittene Kivy-Apps bringt Kivy die KV Language mit (auch Kvlang oder Kivy Language genannt). Sie notieren sie in einer Datei mit der Endung ».kv« im selben Ordner wie die Python-Datei »main.py«.

Im Kontext unseres MP3-Players haben wir die App in der »main.py« in Zeile 109 PlayerApp genannt. Kivy sucht in diesem Fall bei der Ausführung nach einer Datei mit der Endung ».kv« und dem Namen »player«, denn das Tool extrahiert das Wort vor dem Zusatz App und sucht nach einer kv-Datei mit diesem Wort in Kleinschreibung und der Endung kv. In unserem Fall heißt die Datei folglich »player.kv«. Findet Kivy solch eine kv-Datei, verbindet es sie automatisch mit der Python-Datei. Es hat sich als Konvention ergeben, die Kivy-App mit einem Wort und dem Suffix App zu benennen. In der kv-Datei wird der Widget-Tree des Projekts aufgebaut und die grafische Oberfläche festgelegt.

Die KV Language lässt sich als eine Art Auszeichnungssprache bezeichnen und ähnelt den Sprachen YAML und QML. Zuerst erstellen Sie eine Datei »player.kv« (Listing 4). Die erste Zeile gibt gewöhnlich die verwendete Kivy-Version an. In der folgenden Zeile notieren Sie dann die Klasse »Mp3Player« als Root-Widget der »main.py«. Die Beispiel-App nutzt zum Anordnen der GUI-Elemente das BoxLayout und darin verschachtelt das GridLayout.

Das BoxLayout wurde schon in der Datei »main.py« festgelegt. Die Eigenschaft »orientation« des BoxLayouts in Zeile 3 definiert, ob die Anwendung einzelne Elemente horizontal oder wie in diesem Fall vertikal anordnen soll. In dieses BoxLayout fügen Sie zwei GridLayouts vertikal ein. Die Optionen »cols« und »rows« des GridLayouts legen die Anzahl an Spalten und Zeilen fest, die einzelne GUI-Elemente aufnehmen. Daraus ergibt sich gewissermaßen eine Art Gitter. Das erste GridLayout in Zeile 4 von Listing 4 enthält das obere MP3-Logo als Image und die Angaben zu Künstler, Titel und Album-Name der MP3-Datei jeweils als MDLabel darunter. Mit den hier festgelegten IDs werden vom Python-Code in der Datei »main.py« aus die Angaben aus den Metatags gelesen und hier angezeigt. Das MP3-Logo liegt als PNG-Datei im Projektverzeichnis vor. Die MDLabels richten Sie mit »halign« aus und formatieren sie mit »text_color« und »font_style«. Damit zwischen dem oberen Displayrand und dem MP3-Logo etwas Platz frei bleibt, fügen Sie ein MDLabel als Platzhalter ein – genauso wie zwischen dem MP3-Logo und dem ersten Text.

Das zweite GridLayout beschreibt die Navigationsleiste am unteren Displayrand mit den einzelnen Buttons als MD Bottom Navigation Items. Mit dem übergeordneten MD Bottom Navigation Item legen Sie die Farbe mit »panel_color« und die Größe der Navigationsleiste mit »size« fest. Den untergeordneten MD Bottom Navigation Items ordnen Sie mit der Option »icon« die entsprechenden Icons zu, die KivyMD mitbringt [7]. Mithilfe des Befehls »on_tab_release« geben Sie an, welche Python-Methode der Klasse »Mp3Player« in der »main.py« aufgerufen werden soll, wenn der Nutzer auf die entsprechende Schaltfläche tippt oder klickt. Beachten Sie bitte, dass Sie dem Methoden-Namen ein »root« voranstellen müssen. Mit der Option »text« beschriften Sie schließlich die Schaltflächen. Jetzt können Sie die Kivy-App mit dem Kommando »python3 main.py« im Terminal ausführen.

Listing 4

player.kv

#:kivy 2.2.1
<Mp3Player>:
  orientation: "vertical"
  GridLayout:
    cols: 1
    rows: 6
    MDLabel:
      id: lblPlaceholder1
    Image:
      id: imageView
      source: "mp3_logo.png"
      fit_mode: "scale-down"
    MDLabel:
      id: lblPlaceholder2
    MDLabel:
      id: lblArtist
      halign: "center"
      text_color: 1, 1, 1, 1
      font_style: "H4"
    MDLabel:
      id: lblSong
      halign: "center"
      text_color: 1, 1, 1, 1
      font_style: "H5"
    MDLabel:
      id: lblAlbum
      halign: "center"
      text_color: 1, 1, 1, 1
      font_style: "H5"
  GridLayout:
    cols: 5
    rows: 1
    MDBottomNavigation:
      panel_color: .2,.2,.2,.2
      size: root.width, 25
      MDBottomNavigationItem:
        id: btnVolume
        icon: "volume-high"
        on_tab_release: root.open_volume_dialog()
        text: "Volume"
      MDBottomNavigationItem:
        id: btnFile
        icon: "folder-music-outline"
        on_tab_release: root.open_file_dialog()
        text: "Open"
      MDBottomNavigationItem:
        id: btnPlay
        icon: "play-outline"
        on_tab_release: root.play_music()
        text: "Play"
      MDBottomNavigationItem:
        id: btnStop
        icon: "stop-circle-outline"
        on_tab_release: root.stop_music()
        text: "Stop"
      MDBottomNavigationItem:
        id: btnClose
        icon: "close-outline"
        on_tab_release: root.close_app()
        text: "Close"

Das Tool Buildozer

Um die Kivy-App auf das Smartphone zu bekommen, nutzen Sie das vom Kivy-Team entwickelte Buildozer. Es dient unter anderem dazu, APK-Dateien für Android-Systeme zu erzeugen. Für die Installation genügt ein »pip install buildozer« im Projekt-Verzeichnis innerhalb der virtuellen Python-Umgebung »kivy_venv«. Daraufhin aktualisieren Sie mittels »sudo apt update« die Paketquellen und installieren für Ubuntu und dessen Derivate noch die notwendigen Abhängigkeiten (Listing 5).

Listing 5

Abhängigkeiten für Buildozer installieren

$ sudo apt install -y git zip unzip autoconf libtool pkg-config zlib1g-dev libncurses5-dev libncursesw5-dev libtinfo5 cmake libffi-dev libssl-dev

Integrieren Sie Cython mithilfe des Kommandos »pip install –upgrade Cython==0.29.33«. Nun legen Sie zuerst mit dem Befehl »buildozer init« eine Datei »buildozer.spec« im Projektverzeichnis an. Sie muss sich in demselben Ordner wie die »main.py« befinden. Dabei handelt es sich um eine Konfigurationsdatei für Buildozer. Unter der Sektion »title« in dieser Datei halten Sie den Titel der App fest. Mit »package.name« definieren Sie den Namen der erzeugten APK-Datei.

Unter »requirements« listen Sie die notwendigen Packages python3, kivy, kivymd, pyjnius, android, tinytag und androidstorage4kivy. Im Abschnitt »android.permissions« muss »READ_EXTERNAL_STORAGE,READ_MEDIA_AUDIO« stehen. Um Komplikationen zu vermeiden, sollten Sie in der »buildozer.spec« nur ASCII-Zeichen verwenden. Die übrigen Konfigurationen dürfen Sie zunächst vernachlässigen. In der offiziellen Dokumentation des Werkzeugs [8] informieren Sie sich über die restlichen Spezifikationen.

Den eigentlichen Build stößt das Kommando »buildozer -v android debug« an. Der erste Build kann etwas Zeit beanspruchen, da das Android SDK und NDK heruntergeladen werden müssen. Zwischendurch müssen Sie außerdem zwei Lizenzvereinbarungen zustimmen. Als Resultat erhalten Sie eine APK-Datei im Ordner »bin«, die Sie anschließend auf ein Android-Smartphone transferieren und installieren. Näheres zu diesem Dateiformat finden Sie im Kasten “APK-Datei”.

APK-Datei

Bei einer APK-Datei (Android Package Kit) handelt es sich um ein Dateiformat von Installationsdateien für Android. Damit diese sich auf einem Android-Gerät installieren lassen, müssen Sie gegebenenfalls die Sicherheitseinstellungen in Android anpassen, was ein gewisses Sicherheitsrisiko birgt. Achten Sie daher stets auf die Quelle von APK-Dateien.

Fazit

Kivy erweist sich als äußerst praktisches Werkzeug und vereinfacht das Entwickeln von Apps für unterschiedliche Endgeräte. Die Anwendungen sehen ansprechend aus, und mit nur einer Codebasis decken sie die meisten Betriebssysteme ab. Im Detail besteht an der einen oder anderen Stelle jedoch noch Verbesserungspotenzial. Wenn beispielsweise der SoundLoader einwandfrei mit MP3-Dateien unter Android umgehen könnte, wäre der Workaround mit der Java-Klasse MediaPlayer in der behandelten Beispiel-App unnötig. Zusätzlich wäre es wünschenswert, dass der FileChooser unter allen Versionen von Android funktioniert.

Wer sich noch tiefergehend in die Materie einlesen möchte, kann auf die gute Dokumentation von Kivy zurückgreifen [9]. Da hinter der Entwicklung des Frameworks eine große Community steht, dürfte die Weiterentwicklung auf lange Sicht gewährleistet sein. jcb/csi

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 8 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Nach oben