Met en zonder X
GUI-ontwikkeling met Kivy voor Python
Met de Python-bibliotheek Kivy kun je grafische toepassingen makkelijk programmeren, of je dat nu in een venster of helemaal zonder X-server wilt doen. Daarnaast is de bibliotheek vrijwel cross-platform en dus geschikt voor bijvoorbeeld de desktop of voor embedded-systemen als de Raspberry Pi.
De Kivy-bibliotheek voor Python zet grafische toepassingen op het scherm, of er nu een X-server met desktop draait of niet. Daarom is Kivy niet alleen interessant voor Python-programmeurs die een aantrekkelijke en makkelijk te bedienen interface voor hun applicaties willen. Ook ontwikkelaars die bijvoorbeeld met een Raspberry Pi of een ander embedded systeem een apparaat (appliance) willen maken, kunnen de bibliotheek goed gebruiken. Bovendien ondersteunt Kivy touchscreens en is het met gestures te bedienen.
Als er geen X-server draait, kan Kivy de grafische hardware direct via OpenGL aansturen. Kivy-programma's draaien daarmee ook op een desktop-pc onder X én op embedded boards, zoals de Raspberry Pi zonder X-server. Dat bespaart niet alleen resources. Het maakt daarnaast bediening via touchdisplays makkelijker omdat je gestures kunt gebruiken. We kozen om die reden ook voor Kivy toen we voor de vorige c't [1] op zoek waren naar een bibliotheek die onze internetradio moest aansturen.
Een ander pluspunt is dat de vormgeving van de grafische interface volstrekt losstaat van de uitvoerlaag. Daarom kun je toepassingen voor embedded systemen gewoon op de pc onder X ontwerpen en programmeren. Je hoeft ze dus niet steeds op een Raspberry Pi te testen. Hardwareen systeemspecifieke commando's moet je tijdens die programmeerfase dan wel uitcommentariëren.
Versiejungle
Kivy maakt geen onderdeel uit van de standaardinstallatie van de verschillende Linux-distributies, Meestal moet je het apart installeren via het pakket pythonkivy. Om de voorbeelden in dit artikel te kunnen uitproberen, heb je minstens Kivy 1.9 nodig. Bij OpenSuse en Arch Linux hoef je daar met versie 1.9.1 in de repository niets extra's voor te doen. Ubuntu vormt met versie 1.9.0 ook geen probleem. Debian Jessie, Raspbian en andere van Debian
stable afgeleide distributies bieden alleen de verouderde versie 1.8.0. Daarin ontbreken een heleboel bedieningselementen, waaronder de in dit artikel gebruikte keuzelijsten.
Bij Debian en Raspbian testing zit daarentegen versie 1.9.1 al in de repository. Je kunt de pakketten uit de testingrepository installeren of de kivy-pakketten zelf compileren. Hoe je dat doet, staat in het kader aan het eind van dit artikel, met een Raspberry Pi als voorbeeld. Is er nog geen kant-en-klaar pakket (zoals bij Fedora), dan moet je Kivy vanuit de broncode lokaal compileren en installeren. De makkelijkste manier is om de Python-installer PIP te gebruiken. Vaak volstaat het om pip install kivy aan te roepen als Python en PIP al aanwezig zijn – zelfs bij macOS.
Het sterke punt van Kivy is de scheiding van functie en weergave. Daarom is er naast het Python-script van een applicatie met de extensie .py nog een apart bestand met de extensie .kv met daarin de vormgeving van de userinterface. Dat wordt in de documentatie vaak het kv-bestand genoemd. Beide bestanden zijn via de Kivy-bibliotheek met elkaar verbonden. De in het kv-bestand genoemde elementen worden dan ook automatisch als globale objecten in de Python-code gedefinieerd.
Verdeel en programmeer
Een eerste voorbeeld maakt die scheiding tussen weergave en functie duidelijk. Het kv-bestand met als inhoud
genereert het root-object Screen met een tekstveld (label) dat de tekst Voorbeeld1 bevat. Sla die regels op in het bestand voorbeeld1.kv. Zoals gebruikelijk in Python, is het belangrijk om in het kv-bestand ook met vier spaties per laag in te springen. Om bij de hier afgedrukte voorbeelden het afbreken van regels tot een minimum te beperken, hebben we met slechts twee spaties per laag ingesprongen. De bijbehorende Python-code van voorbeeld 1 heeft bijna evenveel regels:
Het maakt voor Kivy niet uit onder welke naam je de Python-code opslaat. We gebruiken voorbeeld1.py. De naam van het kv-bestand doet er wel toe. Die wordt door de naam van de app-class in de Pythoncode bepaald. Bij de door ons gebruikte aanduiding Voorbeeld1App moet het kvbestand met de naam voorbeeld1 opgeslagen zijn. Anders kan Kivy hem niet vinden. Kivy bepaalt de bestandsnaam van het kv-bestand steeds op grond van de naam van de klasse, haalt daar vervolgens de extensie App vanaf en zet het geheel om in kleine letters.
Bij het starten van het voorbeeldprogramma met python voorbeeld1.py opent Kivy onder X een zwart venster en laat de tekst in witte letters zien. Zonder X zijn er geen vensters en gebruikt Kivy het hele scherm voor de weergave. Natuurlijk kun je het beeldscherm ook inkleuren, bijvoorbeeld donkerrood. In voorbeeld 2 is het kvbestand uitgebreid met de definitie van een achtergrondkleur:
Denk eraan dat je de naam van de appclass in de Python-code verandert in Voorbeeld2App. Verder moet je bij de initialisatie Voorbeeld2App().run() aanroepen als je voor dit nieuwe voorbeeld een nieuw kv-bestand maakt met de naam voorbeeld2.kv.
Het canvas-element krijgt kleur (Color) en grootte (Rectangle) toegewezen. De kleurwaarden voor RGB en het alfakanaal kies je van 0 tot 1 – in het voorbeeld is het rood met 30 procent verzadiging, wat op de zwarte achtergrond donkerrood oplevert. Die methode werkt niet alleen bij het rootelement, maar ook bij veel andere elementen – zoals de BoxLayout, die je je kunt voorstellen als frames in html. Hieronder voorbeeld 3 met twee naast elkaar staande delen, waarvan de eerste rood is.
Het is belangrijk dat de twee boxen in een gemeenschappelijke bovenliggende BoxLayout zijn opgenomen. Die is verantwoordelijk voor het plaatsen van de daarin opgenomen elementen. Om de twee boxen niet horizontaal naast elkaar te zetten, maar verticaal onder elkaar, geef je dat gewoon aan in de bovenliggende BoxLayout:
Nieuw in voorbeeld 3 is ook dat de Labeltekst nu niet meer door constanten gedefinieerd is, maar vanuit de – lege – variabele opschrift toegevoegd wordt. Die wordt bij het root-object Screen gedefinieerd. De onderstaande Python-code voor voorbeeld3.py laat zien hoe je de variabele in het kv-bestand aan de Python-code koppelt en pas bij het starten van het programma vult:
Op die manier kun je vanuit de Pythoncode teksten in de grafische interface aanpassen.
Als er in het kv-bestand niets anders staat, reserveert Kivy voor objecten in dezelfde laag ook dezelfde grootte. Daarom zijn de twee boxen uit voorbeeld 3 ook even groot. Je kunt Kivy een instructie voor de gewenste grootte meegeven via size_hint. Voor de breedte en hoogte doe je dat met size_hint_x en size_hint_y. De waarde ligt tussen 0 en 1 en geeft de relatieve grootte aan met betrekking tot het bovenliggende element.
Net als html-frames kun je boxen meervoudig nesten en het venster daarmee in verschillende gebieden verdelen om hier bijvoorbeeld verschillende bedieningselementen in onder te brengen. Dat hebben we bij de grafische interface van de internetradio in de vorige c't ook gedaan.
Teksten kun je het beste met de al bekende Labels verwezenlijken. Buttons werken net zo simpel:
De afbeelding rechtsboven laat het resultaat zien. Een grijze button rechts naast het label met donkerrode achtergrond, waarbij beide elementen zonder verdere instructies voor de verhouding van de groottes (size_hint) even groot zijn. Wat de button triggert als je erop klikt, staat achter on_press: hij draait de volgorde van de tekens in root.opschrift om. Aangezien root.opschrift op alle plaatsen aan
tekstuitvoer gekoppeld is, verandert het opschrift dus ook overal.
Bij toepassingen voor touchscreens kun je overigens beter on_release in plaats van on_press gebruiken, want als de button bij het indrukken verdwijnt en er verschijnt op dezelfde plaats een ander element, dan wordt ook daarvoor een on_press-event getriggerd. Doordat je op on_release reageert, wordt de button alleen bij aantikken actief, dus wanneer de vinger meteen weer weggehaald wordt – niet bij 'ingedrukt' houden.
Kivy heeft nog een heleboel andere bedienings- en weergave-elementen, bijvoorbeeld ListView, dat bij de internetradio voor het kiezen van een zender gebruikt wordt, DropDownList en Image om een afbeelding toe te voegen. Maar er zijn ook complexe elementen zoals de videoplayer Video en FileChooserIconView, een bestandsselectievenster met grafische pictogrammen.
Virtuele schermen
Een beperking van Kivy is dat het geen nieuwe vensters kan openen – alleen al vanwege het feit dat er zonder X-server en zonder een window-manager helemaal geen vensters zijn. Daarom gebruik je meerdere virtuele schermen waartussen je met de ScreenManager kunt wisselen. Bij de radiointerface gebruiken we die functie om van de radiobediening over te schakelen naar de systeeminstellingen.
Een enigszins vereenvoudigd voorbeeld zie je in de twee listings op pagina 135. Links staat de inhoud van het kv-bestand, rechts de inhoud van het Pythonbestand. Je kunt beide bestanden downloaden via de link onderaan het artikel, net als de andere voorbeelden.
Het nieuwe rootelement van het kvbestand is ScreenManagement. Het bevat de klassen Main en Setup. De definitie van die klassen staat er direct onder, gekenmerkt door de klassennamen tussen vishaakjes. De klasse Main komt grotendeels overeen met het kv-bestand van het laatste voorbeeld. Alleen de definitie van name aan het begin van de klasse is erbij gekomen. Bovendien triggert Button een nieuwe actie: hij verandert de waarde van root.manager. current. Achter root.manager gaat ScreenManager schuil en current bevat de naam van de huidige weergegeven klasse. Onmiddellijk na het starten is dat de eerste klasse die in ScreenManagement gedefinieerd is, dus main. Wanneer met een druk op de knop daar setup ingevoerd wordt, wisselt ScreenManager naar de Setup-klasse – en weer terug naar Main, als je in Setup op de button klikt.
Bij het wisselen tussen vensters blijft de scherminhoud van de op dat moment niet weergegeven klasse echter wel behouden. Je hoeft bij een schermwisseling dus niet de weergave te actualiseren. Dat is geen vervanging voor een window-manager, maar maakt het programmeren wel een stuk makkelijker. Je kunt nu namelijk meerdere onafhankelijke delen gebruiken.
(jmu)
www.ct.nl/softlink/1706134