Extra: een grafische gebruikersinterface
Deze opdracht is erg veel werk. Indien je geen zin hebt in het programmeren van een grafische gebruikersinterface mag je in plaats van deze opdracht ook werken aan opdrachten die je nog niet af hebt.
In deze opdracht gaan we een simpel grafisch programma in elkaar zetten. Het maken van grafische programma's is erg veel werk. Daarom beperken we ons hier tot een simpele rekenmachine om je een beetje een idee te geven van hoe dit in zijn werk gaat, zodat je eventueel na afloop van deze cursus zelf uitgebreidere grafische programma's kunt maken, mocht je daar interesse in hebben.
Graphical User Interfaces (GUI's) maken zeer intensief gebruik van objecten, klassen en methoden. Voor grotere grafische programma's is het daarom van belang dat je goed weet hoe dit werkt (zie hoofdstuk 14 uit CS Circles). Voor deze opdracht proberen we het simpel te houden. Waarschijnlijk is deze opdracht goed te maken zonder dat je precies hoeft te weten hoe objecten werken.
Om een grafisch programma te maken heb je een GUI-library nodig (die je met import importeert). We gebruiken voor deze opdracht de library QT. Deze library bevat allerlei handige functies om een GUI in elkaar te zetten. QT ondersteunt naast Python ook andere talen/platformen, zoals C++, Android of Java. Alternatieve GUI-libraries zijn GTK, TCL/TK of wxWidgets. Voor een uitgebreide introductie van QT in Python kun je hier kijken.
In het eerste deel van de deze opdracht gaan we een venster maken. Voor
dit venster maken we een klasse IntroGUI
. Dit object stoppen we in de
variabele app en starten we met de code gui = IntroGUI()
. Zodra je
gui-code aanroept, wordt de code in de functie __init__()
aangeroepen. Binnen de init-functie gebruiken we voor 'ingebouwde'
GUI-methodes die over het venster zelf gaan het keyword self.
- We gaan nu een voorbeeld bekijken. Download het bestand
intro.py, plaats het in PyCharm en
voer het uit. Probeer te snappen wat er precies gebeurt. Indien je
niet snapt wat er precies gebeurt, moet je even om hulp vragen omdat
anders de rest van deze opgave erg lastig wordt. Het pictogram
aap.png
moet je zetten in dezelfde map als het
intro.py
bestand.
Een leeg venster is natuurlijk een beetje saai. We gaan daarom nu de
benodigde knoppen voor een rekenmachine toevoegen. Om de knoppen in het
venster netjes te ordenen, hebben we een layout nodig. Er zijn
verschillende layouts mogelijk,
waar wij hier gebruik maken van een
gridlayout
. Met een gridlayout
kunnen we widgets in een 'grid'
neerzetten: we kunnen een coördinatenstelsel gebruiken om de de widgets
(in dit geval dus knoppen/buttons) op de goede plaats neer te zetten.
Buttons zelf worden gemaakt met een QPushButton
. Als argument heeft dit
widget een string nodig, dat de titel van je knopje wordt. Als we een
button gemaakt hebben kunnen we hem aan de layout toevoegen.
Een voorbeeld van een enkele knop in een venster is te vinden in button.py
- Breid de voorbeeldcode uit met knoppen voor de getallen 0 tot en met
9 en de
+
,-
,/
,*
en de=
. Doe dit zo slim mogelijk: maak gebruik van loops en functies om de buttons te maken en aan de gridlayout toe te voegen.
Nu we de buttons allemaal aangemaakt hebben, moeten we ze gaan
'verbinden' met een 'listener'. Een listener is een methode die wordt
uitgevoerd zodra er op een knopje geklikt wordt. Hierdoor moet je in de
IntroGUI klasse een methode aanmaken, en deze methode verbinden met je
knop. In deze methode kun je bijvoorbeeld een print(...)
stoppen, zodat
er wat in de console geprint wordt.
Een voorbeeld van hoe je een knop met een listener verbindt is te vinden in button-listener.py. Als je de voorbeeldcode begrepen hebt kun je nu listeners toevoegen aan al je knoppen. Je kunt voor iedere knop een aparte listener-functie maken, maar je kunt waarschijnlijk af met één listener en een slim if-statement.
- Koppel nu listeners aan de knoppen in de rekenmachine. Zorg ervoor
dat de gebruiker op willekeurige knoppen kan drukken, om zo een som
uit kunnen rekenen. Print het resultaat van je berekening (nu nog in
de console) zodra de gebruiker op de knop
=
drukt. Voordat hij op=
drukt moet je dus bijhouden op welke andere knoppen er gedrukt is. De rekenvolgorde kan nog lastig zijn: $2 + 3*5 = 17$, en niet $25$. Dit is een erg lastig probleem: het is mooi als je het opgelost krijgt, maar je mag het ook laten zitten en verder gaan.
De rekenmachine zou nu moeten werken, maar de uitvoer komt helaas nog steeds op de console te staan en niet in het venster met de rekenknoppen. Daar gaan we nu aan werken. Naast de standaard buttons, zijn er ook nog andere soorten widgets zoals tekstlabels en invoervelden.
Op dit moment maken we gebruik van een gridlayout om de knoppen netjes
te rangschikken. Een gridlayout is hier ook zeer geschikt voor. Het
uitvoerveldje van de rekenmachine past echter niet in de 'grid', maar
hoort erboven te staan. We zullen daarom gebruik gaan maken van een
vboxlayout
, waarmee je widgets (of groepen van widgets - dus
layouts) onder elkaar kunt plaatsen.
Om tekst te tonen gebruiken we de widget QLabel
. De tekst van dit widget
kan met de methode setText()
gewijzigd worden.
Een voorbeeld van een QLabel
in combinatie met een grid en vboxlayout
is
te vinden in qlabel.py.
- Vervang de
print(...)
in je programma door eenQLabel
en voeg een vboxlayout aan je rekenmachine toe. Als het goed is moet je rekenmachine nu volledig werken zonder console.
In plaats van een QLabel
kun je ook een QLineEdit
gebruiken. Hiermee kun
je de tekst ook aanpassen. Zodra de tekst aangepast wordt kun je daar
weer een melding in een listener van krijgen. Een voorbeeld is te vinden
in qlineedit.py.
- Vervang de
QLabel
door eenQLineEdit
. Zorg er ook voor dat de gebruiker in plaats van op de knoppen te drukken een som in hetQlineEdit
tekstvakje in kan voeren en deze uit kan rekenen door op de knop=
te drukken.
Naast knoppen kunnen we ook menubalken of toolbars toevoegen. Hiervoor
moeten we een QMainWindow
in plaats van een QWidget
gebruiken. Dit zorgt
ervoor dat een layout aan je venster toevoegen wat lastiger wordt. Een
voorbeeld van een menubalk is te vinden in menubar.py.
Een voorbeeld van een toolbar is te vinden in toolbar.py.
- Mocht je nog niet genoeg gekregen hebben van GUI's, dan kun je nu een menubalk en/of een toolbar aan je programma toevoegen met behulp van bovengenoemde voorbeeldcode.