Plattformübergreifende Apps in Python programmieren

Aus LinuxUser 11/2023

Plattformübergreifende Apps in Python programmieren

© rawpixel / 123RF.com

Eine für alle!

Mit dem Python-Framework Kivy lassen sich auf einfache Weise plattformübergreifende Apps für PCs und Smartphones programmieren.

Möchten Sie Apps entwickeln, die unter Linux, Windows, MacOS, Android und iOS laufen? Dann können Sie ab sofort darauf verzichten, mehrere Code-Varianten zu entwerfen oder gar verschiedene Programmiersprachen zu bemühen – zumindest, wenn Sie sich mit der populären Programmiersprache Python auskennen: Dan greifen Sie einfach zum pfiffigen Open-Source-Framework Kivy [1], das eine aktive Community unter der MIT-Lizenz entwickelt. Im Zusammenspiel mit der Bibliothek KivyMD [2] und dem Build-Tool 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. Gegebenenfalls müssen Sie dazu zuerst Pip installieren. Unter Ubuntu und dessen Abkömmlingen gelingt das mit dem Kommando aus der ersten Zeile von Listing 1.

Sobald Sie Python und Pip eingerichtet haben, kommt unter Ubuntu und dessen Derivaten anschließend das Modul Venv an die Reihe (Zeile 2). 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, legen eine solche virtuelle Umgebung namens kivy_venv an und aktivieren sie (Zeile 3 und 4).

Dann bringen Sie Pip und die Setup-Tools auf den aktuellen Stand (Zeile 5). Anschließend installieren Sie Kivy auf dem System, einschließlich Audio/Video-Support und diverser Demo-Beispiele (Zeile 6). Im nächsten Schritt sehen Sie sich die mitgelieferten Demos an, darunter eine Vorführung der verschiedenen Kivy-Widgets (Zeile 7) und einen dreidimensional gerenderten Affen (Zeile 8). Mit dem Kommando deactivate verlassen Sie bei Bedarf die virtuelle Umgebung wieder.

Listing 1

Installation

$ sudo apt install python3-pip
$ sudo apt install -y python3-venv
$ 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. Sie speichern das Programm 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).

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.

Die ersten beiden Codezeilen aus Listing 2 importieren die beiden Klassen App und Label aus den Packages kivy.app und kivy.uix.label. In Zeile 3 definieren Sie die Klasse HelloWorldApp, die von der Klasse kivy.app.App die Funktionen erbt.

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

Möchten Sie Kivy-Widgets im Applikationsfenster positionieren, müssen Sie die Methode build() in der Klasse HelloWorldApp überschreiben (Zeile 4 und 5). Sie gibt im Beispiel lediglich ein Objekt der Klasse Label mit dem Text “Hallo Welt” zurück. Der 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 – dazu später mehr.

Zu guter Letzt instanziieren Sie in Zeile 7 ein Objekt app der Klasse HelloWorldApp und führen es mit app.run() aus. Der Parameter title der Klasse bestimmt den Titel des Applikationsfensters.

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 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/, und erstellen eine virtuelle Umgebung namens kivy_venv, die Sie wie bereits beschrieben aktivieren (Listing 1, Zeile 3). Dann installieren Sie Kivy in der virtuellen Umgebung (Listing 1, Zeile 4).

Die Bibliothek Pyjnius [4], ein weiteres Projekt der Kivy-Entwickler, macht Java-Klassen in Python verfügbar. In unserer Beispiel-App importieren Sie auf diesem Weg unter Android den MediaPlayer aus dem Java-Paket android.media. Er kümmert sich um das Abspielen von MP3-Dateien. 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 (Listing 3, erste Zeile). Das JDK brauchen Sie später auch für das Tool Buildozer. Außerdem benötigen Sie für den MP3-Player noch die Python-Library Tinytag [5]. Damit lassen sich die Metadaten von MP3-Dateien auslesen, um sie in der grafischen Oberfläche der Kivy-App anzuzeigen.

Um Pyjnius und die anderen notwendigen Packages einzurichten, führen Sie als letzten Schritt der Vorbereitungen das Kommando aus der zweiten Zeile von Listing 3 aus. Danach erzeugen Sie die Datei main.py mit dem Code für den MP3-Player (Listing 4). In den ersten neun Zeilen der Datei binden Sie die notwendigen Klassen aus den Packages jnius, kivy und kivymd sowie die Python-Bibliothek Tinytag in die Applikation ein.

Listing 3

Vorbereitungen

$ sudo apt install openjdk-17-jdk
$ pip install pyjnius kivymd tinytag androidstorage4kivy

Listing 4

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

Zeile 11 von Listing 4 ermittelt, ob die App unter Android ausgeführt läuft. Trifft das zu, fordern die nächsten beiden Zeilen die benötigten Leserechte an. Zusätzlich importiert das Listing in Zeile 14 aus dem Package androidstorage4kivy [6] die Klassen Chooser und SharedStorage, die dazu dienen, unter Android Dateien auszuwählen. Der FileChooser von Kivy funktioniert nicht mit allen Versionen von Android. Mit pip list überprüfen Sie gegebenenfalls die Version der installierten Packages.

Zeile 16 definiert die von der Basisklasse BoxLayout abgeleitete Klasse Mp3Player. Damit bildet das BoxLayout von Kivy die Basis für unsere PlayerApp in Zeile 108. Das ermöglicht, die Widgets der grafischen Oberfläche im entsprechenden Layout anzuordnen. Dabei erscheinen alle Elemente der Oberfläche entweder nebeneinander oder untereinander angeordnet.

Der Konstruktor der Klasse Mp3Player ab Zeile 18 ü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. Dasselbe gilt für die Variable mp3_file, die später den Pfad zur ausgewählten MP3-Datei speichert.

Unter Android dient die Klasse Chooser aus dem Package androidstorage4kivy zur Auswahl einer MP3-Datei (Zeile 24), ansonsten kommt dazu die Kivy-Klasse FileChooserListView zum Einsatz (Zeile 26). Für sie erzeugt der Konstruktor noch ein Kivy-Popup, das Sie später zur Auswahl einer MP3-Datei brauchen (Zeile 27).

Ladevorgang

Die nächsten beiden Methoden load_mp3_android() (ab Zeile 29) und load_mp3() (ab Zeile 45) laden eine MP3-Datei in den Speicher und geben sie wieder. Sie fallen verschieden aus, weil sich das Laden einer MP3-Datei unter Android mit MediaPlayer anders gestaltet als mit SoundLoader unter Linux, MacOS oder Windows.

Die Methode load_mp3_android() wählt zuerst im Zusammenhang mit der Klasse Chooser aus Zeile 24 eine MP3-Datei aus. Die Klasse nutzt das native Android-Chooser-UI und nimmt als Parameter die Callback-Methode load_mp3_android() auf. Diese Methode erhält dadurch die Liste shared_file_list als Parameter zurück, in der sich der Pfad der ausgewählten MP3-Datei befindet. Um an diese Datei zu kommen, kopiert die Methode copy_from_shared() der Klasse SharedStorage sie in den privaten Cache-Speicher der App (Zeile 31). Durch diese sogenannte List Comprehension verwandelt sich ein Shared File aus dem Shared Storage des Android-Geräts in ein Private File im Cache der App. Das ist notwendig, da ein Shared File nicht als reguläre Python-Referenz auf eine Datei gilt.

Ab Android-Version 10 ist der Shared Storage im Android-Speicher eine Datenbank und kein Dateisystem mehr. Deshalb funktioniert der FileChooser von Kivy nur bis Android 9. Die erzeugte Liste private_files aus Zeile 31 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 prüfen, ob bereits eine MP3-Datei geladen wurde. Falls ja, ermittelt die Methode isPlaying(), ob gerade eine Wiedergabe stattfindet, und stoppt diese gegebenenfalls. In der nächsten Zeile gibt die Anwendung mit release() die Ressourcen wieder frei, falls 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 die Java-Klasse MediaPlayer in die Python-Applikation ein. Dann weisen Sie der Variablen mPlayer ein entsprechendes Objekt zu (Zeile 38). Daraufhin lädt das Kommando setDataSource(self.mp3_file) die entsprechende MP3-Datei (Zeile 39). Der Befehl prepare() bringt den Player in einen Zustand, in dem er Musik abspielen kann (Zeile 40). Außerdem stellen Sie mithilfe von setVolume() die aktuelle Lautstärke ein (Zeile 41).

Als Nächstes liest die Methode show_mp3_info() in Zeile 42 die Metadaten der MP3-Datei aus, damit die grafische Oberfläche den Künstler, den Songtitel und das Album anzeigen kann. Die Definition dieser Methode finden Sie ab Zeile 57. Als Parameter nimmt sie den String mit dem Pfad zur entsprechenden MP3-Datei entgegen. Über die Anweisung TinyTag.get(self.mp3_file) liest sie die MP3-Datei ein (Zeile 59). In den Zeilen 60 bis 62 folgen drei ternäre Operatoren, die die Attribute artist, title und album der Metadaten auf Inhalt prüfen. Liegen in der MP3-Datei keine Metadaten vorliegen, erscheinen die Strings no artist, no title beziehungsweise no album in der grafischen Oberfläche.

Verwenden Sie die App nicht unter Android, kommt statt load_mp3_android() die Methode load_mp3() mit der Kivy-Klasse SoundLoader zum Einsatz (ab Zeile 45). Hier laden Sie lediglich mit SoundLoader.load() die MP3-Datei und justieren über die Methode volume() die Lautstärke. Deren Wertebereich liegt bei SoundLoader wie bei MediaPlayer zwischen 0 für lautlos und 1 für volle Lautstärke. Deswegen müssen Sie in beiden Fällen den Wert des Lautstärkereglers durch 100 dividieren. Die Zeilen 47 bis 50 prüfen wie unter Android, ob schon eine MP3-Datei geladen wurde, unterbrechen bei Bedarf die Wiedergabe und entfernen die MP3-Datei aus dem Speicher.

Die Python-Datei main.py ist mit der grafischen Oberfläche verbunden, die wir später in der Datei player.kv definieren. Mittels der IDs der GUI-Widgets in der Datei player.kv greifen Sie darauf zu. Zeile 60 setzt beispielsweise mit self.ids.lblArtist.text den Text des MDLabel für den Künstler in der GUI. Mehr dazu lesen Sie weiter unten im Abschnitt “KV Language”.

Lautstärke

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

Den erzeugten Schieberegler betten Sie mithilfe des Codes aus den Zeilen 67 und 68 in ein Popup-Fenster von Kivy ein. Die Klasse Popup hat drei Parameter: Der erste gibt den Titel des Popups an, der zweite (content) integriert den Slider in das 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.

Die Callback-Funktion on_volume_change() ab Zeile 70 passt die Lautstärke an. Der dritte Parameter, val, enthält den aktuellen Wert der Lautstärke. Hier gilt es wieder, zu prüfen, unter welchem OS der Player momentan läuft. Unter Android dienen das Objekt der Java-Klasse MediaPlayer und die verbundene Methode setVolume() dazu, die Lautstärke zu ändern. Die Unterscheidung muss stattfinden, da die Methode zum Ändern der Lautstärke im MediaPlayer von Java setVolume() heißt (Zeile 75), in SoundLoader jedoch den Namen volume() trägt (Zeile 77). Das ganze Konstrukt 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.

Dateiauswahl

Die Methode open_file_dialog() ab Zeile 82 ruft einen Dateiauswahldialog auf, um die abzuspielende MP3-Datei auszuwählen. Abbildung 3 zeigt, wie das unter Android aussieht, in Abbildung 4 sehen Sie das Pendant unter Linux. Der Code aus Zeile 84 ruft dazu unter Android die Methode choose_content() der Klasse Chooser aus der Library androidstorage4kivy auf. Mit dem Argument 'audio/mpeg' lassen Sie sich nur MP3-Dateien anzeigen.

Abbildung 3: Screenshot des Dateiauswahldialogs unter Android.

Abbildung 3: Screenshot des Dateiauswahldialogs unter Android.


Abbildung 4: Screenshot des Dateiauswahldialogs unter Linux.

Abbildung 4: Screenshot des Dateiauswahldialogs unter Linux.

Läuft die App nicht unter Android, greift stattdessen die Klasse FileChooserListView. Dafür deklarieren Sie im Konstruktor der Klasse Mp3Player die Variable mp3_popup und weisen ihr ein Fenster zu (Zeile 86).

Mittels der Klasse FileChooserListView lassen sich beliebige Dateien auf dem Gerät auswählen. Im Konstruktor wurde dazu in Zeile 26 ein Objekt der Klasse FileChooserListView instanziiert und in das Popup mp3_popup eingefügt. Da es bei der Beispielanwendung ausschließlich um Musikdateien geht, sorgen Sie mit dem Parameter filters=['*.mp3','*.MP3'] dafür, dass hier nur MP3-Dateien erscheinen.

Der zweite Parameter on_submit bestimmt, welche Methode es auszuführen gilt, 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, bei deren erstem Element es sich um den Pfad zur ausgewählten Datei handelt (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 self.mp3_popup.dismiss() (Zeile 54) den Dateiauswahldialog mit.

Die Methode play_music() ab Zeile 88 dient dazu, die ausgewählte MP3-Datei wiederzugeben, sobald der Anwender auf die Schaltfläche Play klickt oder tippt beziehungsweise eine MP3-Datei mit der Schaltfläche Open auswählt. Auch diese Methode muss prüfen, ob es sich um ein Android-Gerät handelt: Beim MediaPlayer heißt die Methode zum Abspielen der Datei start(), beim von Kivy mitgelieferten SoundLoader jedoch play(). Auch hier fangen Sie per Try/Except einen AttributeError auf, falls noch kein MP3 geladen wurde.

Die Methode stop_music() ab Zeile 97 beendet das Abspielen der Musik. Unter Android müssen Sie den MediaPlayer zudem mit der Funktion prepare() zurücksetzen, damit bei erneuter Wiedergabe der Song von vorn beginnt. Die Methode close_app() (Zeile 105) schließt zu guter Letzt mit app.stop() die App.

Der Code ab Zeile 108 definiert die Klasse PlayerApp, die von der Klasse MDApp aus der Bibliothek KivyMD erbt. PlayerApp hat wieder eine überschriebene Methode build(), in der Sie den Titel des App-Fensters festlegen (Zeile 110) und eine Instanz der Klasse Mp3Player zurückgeben (Zeile 111). Zum Schluss erzeugen Sie ein Objekt app der Klasse PlayerApp (Zeile 114) und führen es mit run() aus (Zeile 115).

Mit knapp 120 Zeilen Python-Code haben Sie damit einen rudimentären MP3-Player mit grafischer Oberfläche programmiert, der sowohl unter Android (Abbildung 5) als auch unter Linux (Abbildung 6) seinen Dienst versieht.

Abbildung 5: Screenshot des MP3-Players unter Android.

Abbildung 5: Screenshot des MP3-Players unter Android.


Abbildung 6: Screenshot des MP3-Players unter Linux.

Abbildung 6: Screenshot des MP3-Players unter Linux.

KV Language

Bei umfangreicheren Kivy-Projekten sollten Sie die Python-Logik von der grafischen Oberfläche trennen: Das macht den Code übersichtlicher. Zwar könnten Sie GUI-Elemente wie Schaltflächen und Labels direkt in der Python-Datei programmieren, doch für fortgeschrittene Apps bringt Kivy die KV Language mit, auch Kvlang oder Kivy Language genannt.

Die KV Language lässt sich als eine Art Auszeichnungssprache bezeichnen und ähnelt den Sprachen YAML und QML. Sie notieren sie in einem File mit der Endung .kv, die Sie im selben Ordner ablegen wie die Python-Datei main.py. In dieser Datei bauen Sie den Widget-Tree des Projekts auf und legen die grafische Oberfläche fest.

Es hat sich als Konvention eingebürgert, die Kivy-Applikation mit einem Wort und dem Suffix App zu benennen. Im Kontext unseres MP3-Players haben wir die Anwendung in der main.py PlayerApp genannt. Kivy extrahiert das Wort vor dem Zusatz App und sucht nach einer Datei mit passendem Namen (in Kleinschreibung) und der Endung .kv. In unserem Fall muss die Datei folglich player.kv heißen. Findet Kivy solch eine KV-Datei, verbindet es sie automatisch mit der Python-Datei.

In der ersten Zeile der Datei player.kv (Listing 5) geben Sie die verwendete Kivy-Version an. In der folgenden Zeile notieren Sie dann die Klasse Mp3Player als Root-Widget der main.py.

Listing 5

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"

Die Beispiel-App nutzt zum Anordnen der GUI-Elemente das BoxLayout und darin verschachtelt das GridLayout. Das BoxLayout haben wir schon in der Datei main.py festgelegt. Dessen Eigenschaft orientation definiert, ob die Anwendung einzelne Elemente horizontal oder – wie in diesem Fall – vertikal anordnen soll. In dieses BoxLayout fügen Sie vertikal zwei GridLayout-Elemente ein. Die Optionen cols und rows legen hier die Anzahl der Spalten und Zeilen fest, die einzelne GUI-Elemente aufnehmen. Daraus ergibt sich gewissermaßen eine Art Gitter.

Das erste GridLayout ab Zeile 4 enthält ein MP3-Logo und die Angaben zu Künstler, Titel und Album-Name der MP3-Datei jeweils als MDLabel darunter. Über die hier festgelegten IDs liest der Python-Code aus main.py die Angaben aus den Metatags und zeigt sie an. Das MP3-Logo liegt als PNG-Datei im Projektverzeichnis vor. Die Ausrichtung der einzelnen MDLabel-Elemente bestimmen Sie mittels halign, die Formatierung nehmen Sie mit text_color und font_style vor. Damit zwischen dem oberen Rand des Displays und dem MP3-Logo etwas Platz frei bleibt, fügen Sie ein MDLabel als Platzhalter ein, genauso zwischen dem MP3-Logo und dem ersten Text.

Das zweite GridLayout nimmt die Navigationsleiste am unteren Display-Rand samt den einzelnen Buttons als MDBottomNavigation-Items auf. Mit dem übergeordneten MDBottomNavigation-Element legen Sie die Farbe (panel_color) und Größe (size) der Navigationsleiste fest. Den untergeordneten MDBottomNavigationItem-Elementen ordnen Sie mit der Option icon passende Icons aus der Bibliothek KivyMD zu [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 Methodennamen ein root voranstellen müssen. Mit der Option text beschriften zu guter Letzt die Schaltflächen.

Jetzt können Sie die Kivy-App mit dem Kommando python3 main.py im Terminal ausführen.

Das Tool Buildozer

Um die Kivy-App auf das Smartphone zu bekommen, installieren Sie im Projektverzeichnis innerhalb der virtuellen Python-Umgebung kivy_venv das vom Kivy-Team entwickelte Buildozer (Listing 6, erste Zeile). Es dient unter anderem dazu, APK-Dateien für Android-Systeme zu erzeugen. Danach aktualisieren Sie die Paketquellen (Zeile 2), installieren für Ubuntu und dessen Derivate noch die notwendigen Abhängigkeiten (Zeile 3) und integrieren Cython in die Umgebung (Zeile 4).

Listing 6

Buildozer einrichten

$ pip install buildozer
$ sudo apt update
$ sudo apt install -y git zip unzip autoconf libtool pkg-config zlib1g-dev libncurses5-dev libncursesw5-dev libtinfo5 cmake libffi-dev libssl-dev
$ pip install --upgrade Cython==0.29.33
$ buildozer init
$ buildozer -v android debug

Anschließend legen Sie eine Konfigurationsdatei für Buildozer im Projektverzeichnis an (Zeile 5). Sie trägt den Namen buildozer.spec und muss sich in demselben Ordner wie die main.py befinden. Um Komplikationen zu vermeiden, sollten Sie in der buildozer.spec nur ASCII-Zeichen verwenden.

In der Sektion title der Konfiguration 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. Die übrigen Konfigurationen dürfen Sie zunächst vernachlässigen. In der offiziellen Dokumentation des Werkzeugs [8] informieren Sie sich bei Interesse über die restlichen Spezifikationen.

Nach Abschluss der Konfiguration können Sie den eigentlichen Build anstoßen (Listing 6, letzte Zeile). Der erste Durchlauf kann etwas Zeit beanspruchen, da das SDK und das NDK für Android heruntergeladen werden müssen. Zwischendurch müssen Sie außerdem zwei Lizenzvereinbarungen zustimmen. Als Resultat erhalten Sie im Ordner bin/ eine APK-Datei, die Sie anschließend auf ein Android-Smartphone transferieren und dort installieren. Näheres zu diesem Dateiformat finden Sie im Kasten “APK-Dateien”.

APK-Dateien

Bei einer APK-Datei (Android Package Kit) handelt es sich um ein Dateiformat für Android-Installationsdateien. Damit diese sich auf einem Android-Gerät einrichten lassen, müssen Sie gegebenenfalls die Sicherheitseinstellungen in Android anpassen, was ein gewisses Sicherheitsrisiko mit sich bringt. Verwenden Sie daher ausschließlich APK-Dateien aus vertrauenswürdigen Quellen.

Fazit

Kivy erweist sich als äußerst praktisches Werkzeug und vereinfacht das Entwickeln von Apps für unterschiedliche Endgeräte enorm. Die Anwendungen sehen ansprechend aus, und mit nur einer Codebasis decken Sie die meisten Betriebssysteme ab.

Im Detail besteht jedoch an der einen oder anderen Stelle noch Verbesserungspotenzial. Könnte beispielsweise Kivys SoundLoader unter Android einwandfrei mit MP3-Dateien umgehen, wäre der in unserem Beispiel verwendete Workaround mit der Java-Klasse MediaPlayer unnötig. Zusätzlich wäre es wünschenswert, dass Kivys 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. (jlu)

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDF
LinuxUser 11/2023 KAUFEN
EINZELNE AUSGABE
ABONNEMENTS
TABLET & SMARTPHONE APPS
E-Mail Benachrichtigung
Benachrichtige mich zu:

Hinweis: Dieser Artikel ist älter als ein Jahr, enthaltene Informationen sind möglicherweise veraltet.

0 Kommentare
Älteste
Neuste Beste Bewertung
Nach oben