Python gyerekeknek - grafikus felület

Kategória: Python gyerekeknek.

Áttekintés

Idáig csak parancssori utasításokat tanultunk. Ahhoz, hogy a Scratch-hez hasonló játékokat tudjunk készíteni, meg kell ismerkednünk a grafikus felhasználói felület programozásával. Ez több szempontból sem annyira nyilvánvaló, mint a Scratch-ben:

  • A Pythonban (és ez igaz majdnem minden programozási nyelvre) a grafikus felhasználói felület használatának a mikéntje nem egyértelmű. A legtöbb esetben egy olyan könyvtárat kell használnunk, ami alapból nem is része a nyelvnek. Ezekből ráadásul többféle lehet.
  • Itt nincs vászon, nincsenek szereplők, nincsenek jelmezek, nincs rajzasztal sem, és olyan műveletek sem léteznek, mint pl. az, hogy egy szereplő érint-e egy másik szereplőt. Ezt mind nekünk kell megvalósítanunk.

Ugyanakkor a legtöbb grafikus felület tudása messze több, mint amit a Scratch-ben megismerünk, csak nem áll kézre. Mi most a TK grafikus felülettel ismerkedünk meg, ami a Pythonban a legelterjedtebb, és része az alap telepítésnek. Teljes egészében nem fogjuk megismerni, épp hogy csak a felszínét karcoljuk majd, amely elegendő lesz ahhoz, hogy egy játékot elkészítsünk.

Egy egyszerű ablak

Lássunk egy egyszerű példát!

from tkinter import *
root = Tk()
root.mainloop()

Ennek az eredménye egy üres ablak:

ablak.png

A kód magyarázata:

  • from tkinter import *: a könyvtár, amit használni fogunk, az a tkinter. Innen mindent betöltünk.
  • root = Tk(): ezzel létrehozunk egy fő ablakot. A Tk egy osztály. A root ennek az ablaknak a neve. Ez bármi lehet. Megszokott a root elnevezés, ami azt jelenti angolul, hogy gyökér. De adhatnánk neki más nevet is, pl. window (ablak).
  • root.mainloop(): enélkül parancssorból indítva nem működik; az IDLE szerkesztőből egyébként enélkül is működik.

Összetettebb ablak

Példa

Lássunk most egy összetettebb példát!

from tkinter import *
 
def button_click():
    name = str(name_entry.get())
    if name != '':
        greeting_label['text'] = f'Szia, {name}!'
 
root = Tk()
name_frame = Frame(master=root)
name_label = Label(master=name_frame, text='Név:')
name_label.grid(column=0, row=0)
name_entry = Entry(master=name_frame)
name_entry.grid(column=1, row=0)
name_frame.pack()
button = Button(master=root, text='Kattints rám!', command=button_click)
button.pack()
greeting_label = Label(master=root, text='Hello!')
greeting_label.pack()
root.mainloop()

Ennek az eredménye:

ablak_szia.png

Ilyen grafikus elemek nincsenek a Scratch-ben. De nem egyszerű! Nézzük sorról sorra!

  • from tkinter import *: ezt már láttuk.
  • root = Tk(): ezt is.
  • name_frame = Frame(master=root): egy ún. keretet hozunk létre. Angolul a frame szó keretet jelent. Ez is egy osztály. Azt láthatjuk a fenti példába is, hogy az elemeket egymás alá helyezi; ezzel azt fogjuk megmondani a programnak, hogy a név feliratot, és a bevitel mezőt (ahova beírjuk a nevünket) ne egymás alá, hanem egymás mellé tegye.
  • name_label = Label(master=name_frame, text='Név:'): egy feliratot hozunk létre ezzel. A label szó szerint címkét jelent. Ennek két paramétert adunk át név szerint: az egyik a master, melynek megmondjuk, hogy mihez tartozik. A name_frame az a keret, amit az imént létrehozunk. A text maga a felirat.
  • name_label.grid(column=0, row=0): ezzel azt mondjuk meg, hogy a név címke pontosan hol szerepeljen a kereten belül. A grid szó szerint rácsot jelent: azt adjuk meg, hogy a rács hányadik oszlopában (column) és hányadik sorában (row) kerüljön. Az oszlopokat és a sorokat is 0-tól sorszámozzuk. Ez a keret egy soros lesz, aminek tehát 0 a sorszáma. Az oszlop is 0, tehát ez lesz bal oldalon.
  • name_entry = Entry(master=name_frame): az entry beviteli mezőt jelent, ide a felhasználó be tud írni valamit. Megadjuk, hogy ez is a korábban létrehozott keretre kerüljön.
  • name_entry.grid(column=1, row=0): a felirathoz hasonlóan ennek is megadjuk a koordinátáját a rácson. Ez eggyel jobbra kerül, mint a név felirat.
  • name_frame.pack(): ezzel tesszük láthatóvá a keretet, ill. ezáltal mindazt, ami rajta van. A pack azt jelenti, hogy becsomagol, ami arra utal, hogy itt határozza meg a pontos méretét.
  • button = Button(master=root, text='Kattints rám!', command=button_click): a button nyomógombot jelent. Ez közvetlenül rákerül a fő ablakra.
  • button.pack(): láthatóvá tesszük a nyomógombot is.
  • greeting_label = Label(master=root, text='Hello!'): az a felirat, ahol üdvözli a program a felhasználót. Ez is közvetlenül a fő ablakra kerül.
  • greeting_label.pack(): ezt is láthatóvá tesszük.
  • root.mainloop(): ezt már láttuk; néha kell.

Most láttuk a button_click részt:

  • def button_click():: függvényt hozunk létre a szokásos módon.
  • name = str(name_entry.get()): a name_entry a beviteli mező neve. Ezzel a sorral kiolvassuk az oda beírt értéket, és értékül adjuk a name változónak.
  • if name != '':: csak akkor változtatjuk meg a felirat értékét, ha a beírt név nem üres; itt ezt ellenőrizzük.
  • greeting_label['text'] = f'Szia, {name}!': így tudjuk egy címkének átírni a szövegét.

Még számos grafikus elem létezik: rádiógombok, kiválasztó elemek, többsoros beviteli mezők stb.; ezekre hely- és időszűke miatt nem térünk ki. Egyet viszont részletesebben meg kell néznünk: a vásznat, angolul canvas. Ez az, ami leginkább hasonlít a Scratch felületre.

from tkinter import *
 
root = Tk()
canvas = Canvas(master=root, width=480, height=360, bg='white')
canvas.pack()
root.mainloop()

Lássuk az ismeretlen sorokat:

  • canvas = Canvas(master=root, width=480, height=360, bg='white'): itt hozzuk létre a vásznat, és állítjuk be az alapértékeit. Ennek nagyon sok adatmezője és függvénye van. A paramétereket nézzük meg részletesebben:
    • master=root: közvetlenül a fő ablakra kerül.
    • width=480: szélesség 480 képpont, mint a Scratch-ben. Itt nem -240-től megy 24 -ig, hanem 0-tól 480-ig.
    • height=360: magasság 360 képpont, mint a Scratch-ben. Itt szintén 0-tól megy 360-ig, ráadásul fentről lefelé haladva, ellentétben a Scratch-csel, ahol lentről felfelé haladunk, -180-tól 180-ig.
    • bg='white': a háttérszínt beállítjuk fehérre. (A white angolul fehéret jelent.)
  • canvas.pack(): megjelenítjük a vásznat

Feladat

Készítsük el az egy kérdéses kvíz játékot grafikus felületen!

from tkinter import *
 
def button_click():
    answer = str(answer_entry.get())
    if answer == 'Budapest':
        check_label['text'] = 'A válasz helyes!'
    else:
        check_label['text'] = 'A válasz helytelen; a helyes válasz Budapest lett volna.'
 
root = Tk()
question_frame = Frame(master=root)
question_label = Label(master=question_frame, text='Mi Magyarország fővárosa? ')
question_label.grid(column=0, row=0)
answer_entry = Entry(master=question_frame)
answer_entry.grid(column=1, row=0)
question_frame.pack()
button = Button(master=question_frame, text='Válasz', command=button_click)
button.grid(column=2, row=0)
check_label = Label(master=root, text='')
check_label.pack()
root.mainloop()

Önálló feladat

Bővítsük ki a kvízt tetszőlegesen több kérdésesre, többféle válaszlehetőségre (pl. számot kelljen beírni, több válaszlehetőségből lehessen kiválasztani egyet vagy akár többet stb.).

Vászon

Példa

A következő már egy egészen összetett példa lesz: kirajzolunk egy kört, és mozgatjuk is:

from tkinter import *
 
def key_pressed(event):
    circle_coord = canvas.coords(circle)
    if event.keysym == 'Left' and circle_coord[0] > 5:
        canvas.move(circle, -5, 0)
    if event.keysym == 'Right' and circle_coord[0] <= 455:
        canvas.move(circle, 5, 0)
    if event.keysym == 'Up' and circle_coord[1] > 5:
        canvas.move(circle, 0, -5)
    if event.keysym == 'Down' and circle_coord[1] <= 335:
        canvas.move(circle, 0, 5)
 
root = Tk()
root.title('Mozgatás')
canvas = Canvas(master=root, width=480, height=360, bg='white')
canvas.pack()
circle = canvas.create_oval(230, 170, 250, 190, width=3, outline='black', fill='yellow')
root.bind('<KeyPress>', key_pressed)
root.mainloop()

Eredmény:

vaszon_mozgat.png

Először lássuk a főprogram ismeretlen sorait!

  • circle = canvas.create_oval(230, 170, 250, 190, width=3, outline='black', fill='yellow'): ezzel hozunk létre a vásznon egy kört. Valójában olyan oválist rajzolunk, amelynek a szélessége és a magassága is megegyezik. Az első 4 szám annak a képzeletbeli téglalapnak a bal felső ill. jobb alsó x és y koordinátája, melynek képzeletbeli éleit az ovális érinti. A width a körív vastagsága képpontban, az outline a körív színe (black = fekete), a fill pedig annak kitöltése (yellow = sárga).
  • root.bind('<KeyPress>', key_pressed): ezzel mondjuk meg azt, hogy mi fusson le akkor, ha lenyomunk egy billentyűt. Ez a key_pressed() nevű függvény, amit lent megvizsgálunk részletesen.

A key_pressed() függvény sorai:

  • def key_pressed(event):: paraméterként magát a kiváltó esemény részleteit kapja meg (event = esemény).
  • circle_coord = canvas.coords(circle): a canvas a vászon változó neve, ld. a canvas = Canvas(…) sort lejjebb. A canvas.coords(circle) a vászonra rajzolt kör aktuális koordinátáit adja vissza, mégpedig a képzeletbeli körbe rajzolt négyzet bal felső képpontjáét. Ezt elmentjük a circle_coord változóba.
  • if event.keysym == 'Left' and circle_coord[0] > 5:: itt azt ellenőrizzük, hogy egyrészt a lenyomott billentyű a bal nyíl-e (event.keysym == 'Left'), másrészt az x koordináta (ezt jelenti a [0] index) nagyobb-e, mint 5. Itt azt vizsgáljuk, hogy ha még balra lépne, akkor kiférne-e.
  • canvas.move(circle, -5, 0): a canvas.move() utasítással tudjuk mozgatni a megadott objektumot (circle). A másik két paraméter azt mutatja, hogy mennyivel változzon az x és az y koordináta. Ebben az esetben az x koordináta -5-tel változik, azaz 5-tel balra lép, míg az y koordináta 0-val változik, azaz nem változik.
  • if event.keysym == 'Right' and circle_coord[0] <= 455:: itt azt vizsgáljuk, hogy a lenyomott billentyű a jobbra nyíl-e, ill. az x koordináta eléri-e a 455-öt. Ez a 455 úgy jött ki, hogy a vászon 480 pont széles, ebből lejön az átmérő (20 képpont), valamint az, hogy ha még jobbra lépnénk ötöt, akkor kiférne-e.
  • canvas.move(circle, 5, 0): az x koordináta 5 képpontnyit változik, azaz jobbra lépünk.
  • if event.keysym == 'Up' and circle_coord[1] > 5:: a felfelé nyíl kezelése, felfelé lépéssel. Figyeljük meg az [1] indexet: ez az y koordinátára vonatkozik.
  • canvas.move(circle, 0, -5): léptetés felfelé.
  • if event.keysym == 'Down' and circle_coord[1] <= 335:: a lefele nyíl vizsgálata.
  • canvas.move(circle, 0, 5): léptetés lefelé.

Scratch-ben ez sokkal egyszerűbb:

scratch_mozgat.png

Most lássuk hogyan tudunk képet megjeleníteni! Töltsünk le egy png formátumú képet a netről, pl. ezt: https://freepngimg.com/png/13523-smiling-face-with-sunglasses-cool-emoji-png. Átméreteztem, hogy elférjen, és át is neveztem; ezt is lehet használni:

emoji_sunglasses.png
from tkinter import *
 
root = Tk()
canvas = Canvas(root, width=480, height=360, bg='white')
canvas.pack()
emoji_sunglasses_image = PhotoImage(file='emoji_sunglasses.png')
emoji = canvas.create_image(240, 180, image=emoji_sunglasses_image)
root.mainloop()

Eredmény:

canvas_photo.png

Ez Scratch-ben jóval egyszerűbb, mivel ott a beépített szereplők között is többnyire van megfelelő.

Feladat

Mozgassuk a képet!

from tkinter import *
 
def key_pressed(event):
    emoji_coord = canvas.coords(emoji)
    if event.keysym == 'Left' and emoji_coord[0] > 50:
        canvas.move(emoji, -5, 0)
    if event.keysym == 'Right' and emoji_coord[0] <= 430:
        canvas.move(emoji, 5, 0)
    if event.keysym == 'Up' and emoji_coord[1] > 50:
        canvas.move(emoji, 0, -5)
    if event.keysym == 'Down' and emoji_coord[1] <= 310:
        canvas.move(emoji, 0, 5)
 
root = Tk()
root.title('Mozgatás')
canvas = Canvas(master=root, width=480, height=360, bg='white')
canvas.pack()
emoji_sunglasses_image = PhotoImage(file='emoji_sunglasses.png')
emoji = canvas.create_image(240, 180, image=emoji_sunglasses_image)
root.bind('<KeyPress>', key_pressed)
root.mainloop()

Önálló feladat

Mozgassunk egyszerre két képet: az egyiket a nyilakkal, a másikat pedig az A, S, D és W billentyűkkel.

Teszt

TODO

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License